Device info Ledger

This project stores device information like debugging logs and other device information in Ledger.

Overview

One of the commonly used troubleshooting tools is the USB serial debug log. One issue is that the logs tend to be large and verbose, so it's not practical to send entire logs in real time to the cloud. Plus, if the issue is with connectivity, the cloud won't be available at the time.

To help solve two common issues, this example can store a small portion of the log in two common scenarios:

  • For the network and cloud connection process, for debugging intermittent connectivity issues.
  • Right before the most recent reboot, for help in isolating a SOS panic reset.

The configuration, including what to include and how much log information to save, can be configured three ways:

  • Locally, on device, in C++ code.
  • Cloud configuration of product default settings via Ledger.
  • Cloud configuration overrides for a specific device via Ledger.

For example, you could include minimal log information by default, but increase the information when troubleshooting issues on a specific device.

Finally, the device information is saved in Ledger. A sample log is shown here, and there is a tool below for viewing the information for devices in your product.

Sample log

Creating a product

This example requires device-side Ledger, which requires Device OS 6.1.0 or later. Additionally, it can only be used with devices in a product, and only developer sandbox products (not organization products).

If running the example, we recommend that you create a new product for testing, adding a device or two to the test product, then removing the devices and product when done testing. This will reduce the likelihood of breaking a working product.

Device type  


Create Ledgers

This example uses up to three Ledgers. The default names are included here, but they can be changed, though you must also modify the firmware to use the new names.

Device information Ledger

This Ledger stores the data generated by the device so you can view it from the cloud. There is an interactive tool to view this data below, you can view it in JSON format in the console, or you can access the data using the Particle cloud API.

Name Description
Ledger Name device-info
Direction Device to cloud

This Ledger is required. Without it, there would be no purpose to this feature as the data would not be stored anywhere!

Each instance of this Ledger is device that has stored its device information.

Default configuration Ledger

This Ledger stores the default configuration for devices in your product. You can either store the default configuration in the cloud, or you can store it in code in firmware.

Name Description
Ledger Name device-info-defaults
Direction Cloud to device
Scope Product

This Ledger is optional, and if you do not create it, you will need to embed the default configuration in the firmware code.

Each instance of this Ledger corresponds to a product default configuration. You must specify all configuration fields in the JSON; if you specify a cloud configuration it will not merge with a local configuration.

If you are using cloud default configuration, you should create an instance for your test product and set the JSON to this using the Advanced tab:

{
    "lastRunLog": 1024,
    "connectionLog": 2048,
    "includeGeneral": true,
    "includeDiag": true,
    "includeTower": true,
    "logLevel": "LOG_LEVEL_INFO",
    "logFilters": [],
}

Device configuration Ledger

This Ledger allows you to override configuration for a specific device in your product. You might use this to adjust the log sizes and levels to better troubleshoot a problem with a specific device, for example.

Name Description
Ledger Name device-info-config
Direction Cloud to device
Scope Device

This Ledger is optional, and if you do not create it, you will not be able to override the configuration on a per-device basis.

Each instance of this Ledger corresponds to a single device override. The value is JSON and only needs to specify the fields you want to override.

Project source

   

     

Viewing data

The tool below can be used to view the logs once they've been uploaded to the cloud.

Configuration

Local configuration

Local configuration stores the default configuration in the source code as a string containing JSON data.

It's very tedious typing JSON strings into a C++ string because of the need to escape double quotes, but the Convert JSON to code tool makes it painless. Just paste the JSON into the box, hit the button, and copy and paste the code into your source.

For example, this JSON:

{
    "lastRunLog": 1024,
    "connectionLog": 2048,
    "includeGeneral": true,
    "includeDiag": true,
    "includeTower": true,
    "logLevel": "LOG_LEVEL_INFO",
    "logFilters": [],
}

Converted to code looks like:

const char localConfig[] = 
"{"
    "\"lastRunLog\": 1024,"
    "\"connectionLog\": 2048,"
    "\"includeGeneral\": true,"
    "\"includeDiag\": false,"
    "\"includeTower\": false,"
    "\"logLevel\": \"LOG_LEVEL_INFO\","
    "\"logFilters\": []"
"}";

You set a local config using withLocalConfig from setup() like this:

DeviceInfoLedger::instance()
    .withLocalConfig(localConfig)
    .withRetainedBuffer(retainedLogs, sizeof(retainedLogs))
    .setup(); 

Default configuration in Ledger

You can also configure the product defaults in the cloud using Ledger using setup() code like this:

DeviceInfoLedger::instance()
    .withConfigDefaultLedgerEnabled(true)
    .withRetainedBuffer(retainedLogs, sizeof(retainedLogs))
    .setup(); 

It is also possible to change the Ledger name. It currently must be a cloud to device Ledger with a product scope; you cannot use an owner or organization scope at this time.

Device overrides in Ledger

To enable the device-specific override feature, use withConfigDeviceLedgerEnabled() in your setup code.

DeviceInfoLedger::instance()
    .withConfigDefaultLedgerEnabled(true)
    .withConfigDeviceLedgerEnabled(true)
    .withRetainedBuffer(retainedLogs, sizeof(retainedLogs))
    .setup(); 

You can create an instance in the device-info-config Ledger for a specific Device ID. This only needs to set the overrides to the default configuration (either cloud or local).

For example, this JSON will increase the size of the connection log and set it to trace mode.

{
    "connectionLog": 4096,
    "logLevel": "LOG_LEVEL_TRACE",
    "logFilters": [],
}

This is an example using log filters to set the level for specific categories:

{
    "logLevel": "LOG_LEVEL_WARN",
    "logFilters": [
        {
            "category": "LOG_LEVEL_INFO",
            "level": "app"
        },
        {
            "category": "LOG_LEVEL_TRACE",
            "level": "app.network"
        }
    ]
}

This is similar to the JSON configuration equivalent to this setup in C++ code:

SerialLogHandler logHandler(LOG_LEVEL_WARN, { // Logging level for non-application messages
    { "app", LOG_LEVEL_INFO }, // Default logging level for all application messages
    { "app.network", LOG_LEVEL_TRACE } // Logging level for networking messages
});

Log settings in the cloud only affect the cloud logs, however, not the local USB serial logs. You can specify different settings for SerialLogHamdler.