Changing logging level of a log handler logging category

Hey, I’m just starting to play around with the new Logging interface that Particle Firmware provides since 0.6.0 and I am wondering if it is possible to change the Logging Level of a Logging Category within a Log Handler after it has been defined.

The use case I am thinking of is issuing a command via the serial monitor to change the Logging Level of a Logging Category on the fly.

Here is what I mean:

Lets say I declare a Log Handler containing a couple of Logging Categories as well as an instance of the Logger Class:

SerialLogHandler logHandler(LOG_LEVEL_WARN,  //Default logging level for non-application messages
   {
      { "app", LOG_LEVEL_INFO },       //Logging level for logs from "app" log category
      { "app.main", LOG_LEVEL_INFO},  //Logging level for logs from "app.main" log category
      { "app.adc", LOG_LEVEL_ALL}      //Logging level for logs from "app.adc" log category
   });

Logger myLogger("app.main");

Then somewhere in my main program I have code like this:

myLogger.trace("Trace level message");
myLogger.info("Info level message");

So, as it is, only the INFO message would be output to the serial monitor.

Now, let’s say I have a function that checks for serial input from the serial monitor. This function would check for a string like “app.main0” and parse the “0” from the string, which corresponds to the log level enum of LOG_LEVEL_ALL.

How do I go about changing the Logging Level for the “app.main” category within the logHandler object?

My understanding is that the Logging Levels and Logging Categories are hard coded when logHandler gets defined… but is there a way to change them after that?

Hi @jaza_tom. 0.6.0 supports only statically allocated log handlers, so you need at least 0.6.1 to configure and register log handlers dynamically. It’s not possible to change filtering for already existing handler, but you always can unregister current handler (or several handlers) and register another one with different settings:

#include "application.h"

SYSTEM_MODE(MANUAL)

void setup() {
    // Configure serial interface
    Serial1.begin(115200);

    // Get log manager's instance
    auto logManager = LogManager::instance();

    // Configure and register log handler dynamically
    LogCategoryFilters filters;
    filters.append({ "app", LOG_LEVEL_INFO });
    auto handler1 = new StreamLogHandler(Serial1, LOG_LEVEL_WARN /* Default level */, filters);
    logManager->addHandler(handler1);
    Log.trace("Trace message 1"); // Filtered out

    // Unregister and destroy log handler
    logManager->removeHandler(handler1);
    delete handler1;

    // Register another log handler with different settings
    filters.clear();
    filters.append({ "app", LOG_LEVEL_TRACE });
    auto handler2 = new StreamLogHandler(Serial1, LOG_LEVEL_WARN, filters);
    logManager->addHandler(handler2);
    Log.trace("Trace message 2"); // Gets logged

    // Unregister and destroy log handler
    logManager->removeHandler(handler2);
    delete handler2;
}

void loop() {
}

Logging can also be configured via USB requests, but this functionality is not supported by our developer tools yet, and using USB requests directly can be a convoluted task. This test script can be a good starting point if you want to explore internals a little: https://github.com/spark/firmware/blob/develop/user/tests/accept/features/logging/log_config.feature

4 Likes

Okay, thanks for the tips! I’ll look into trying this at some point soon. :ok_hand: