You are currently not logged in and your progress will not be saved. Register or Log in

Logger module

The logger module is the recommended method for sending messages to a console, unlike the printk() function which will not return until all bytes of the message are sent. The logger module supports both in-place and deferred logging among many other advanced features such as:

  • Multiple backends
  • Compile time filtering on module level
  • Run time filtering independent for each backend
  • Timestamping with user provided function
  • Dedicated API for dumping data
  • Coloring of logs
  • printk() support – printk message can be redirected to the logger

You can read the full list of features in the Logging documentation.

As you will see, the logger module is highly configurable at compile time and at run time. By using proper configuration options, logs can be gradually removed from compilation to reduce image size and execution time when logs are not needed. During compilation, logs can be filtered out based on module and severity level.

More in this (Lesson 7)

The logging module, when used, will by default create a low priority thread (log_process_thread_func). The task of this thread is to take the deferred “queued” logs and push them out to a console.

Severity levels

Logs can also be compiled in but filtered at run time using a dedicated API. The run time filtering is independent for each backend and each module. There are four severity levels available in the system, see the table below.

1 (most severe)ErrorSevere error conditionsLOG_LEVEL_ERR
2WarningConditions that should be taken care ofLOG_LEVEL_WRN
3InfoInformational messages that require no actionLOG_LEVEL_INF
4 (least severe)DebugDebugging messagesLOG_LEVEL_DBG
Logger module: Severity Levels

For each level, the following set of macros are available:

LOG_X for standard printf-like messages where X can be DBG, INF, WRN, or ERR.

For example, the following line:

LOG_INF("nRF Connect SDK Fundamentals");

would give the output

[00:00:00.382,965] <inf> Less4_Exer2: nRF Connect SDK Fundamentals

[00:00:00.382,965] is a timestamp (formatted in hh:mm:ss.ms,us) associated with the generation of the message. The logger gets the timestamp by calling the kernel function k_cycle_get_32() internally. This routine returns the current time since boot up (uptime), as measured by the system’s hardware clock. You could change this to return an actual time and date if an external Real-time clock is present on the system.

<inf> indicates the severity level, i.e information.

Less4_Exer2 is the name of the module generating the log message.

nRF Connect SDK Fundamentals is the actual message.

As another example, the following lines:

    LOG_INF("Exercise %d",2);   
    LOG_DBG("A log message in debug level");
    LOG_WRN("A log message in warning level!");
    LOG_ERR("A log message in Error level!");

would print the following on the console:

[00:00:00.257,354] <inf> Less4_Exer2: Exercise 2
[00:00:00.257,385] <dgb> Less4_Exer2.main: A log message in debug level
[00:00:00.257,385] <wrn> Less4_Exer2: A log message in warning level!
[00:00:00.257,385] <err> Less4_Exer2: A log message in Error level!

The first logging command LOG_INF("Exercise %d",2) is used to generate a formatted string with an integer place holder %d, similar to what have we seen with printk(). Instead of the constant 2, you could place any integer variable to print its value.

The remaining three logging lines will print messages in three different severity levels. Note that messages with Warning severity level will be displayed in yellow and messages with Error severity level will be displayed in red. This is because the coloring of error and warning logs feature (LOG_BACKEND_SHOW_COLOR) is enabled by default as we will see in Exercise 2&3.  

Dumping data

We also have the LOG_HEXDUMP_X  macros for dumping data where X can be DBG, INF, WRN, or ERR.

The LOG_HEXDUMP_X  macro takes three parameters: a pointer to the data to be printed, the size in bytes of the data to be printed, and a string to describe the data.

For example, the following lines:

    uint8_t data[] = {0x00, 0x01, 0x02, 0x03,
                      0x04, 0x05, 0x06, 0x07,
                      'H', 'e', 'l', 'l','o'};
    LOG_HEXDUMP_INF(data, sizeof(data),"Sample Data!"); 

will print the following on the console:

[00:00:00.257,385] <inf> Less4_Exer2: Sample Data!
                                      00 01 02 03 04 05 06 07  48 65 6c 6c 6f          |........ Hello

Configuration categories

There are two configuration categories for the logger module: configurations per module and global configuration. When logging is enabled globally, it works for all modules. However, modules can disable logging locally. Every module can specify its own logging level (LOG_LEVEL_[level]) or use LOG_LEVEL_NONE which will disable the logging for that module.

The module logging level will be honored unless a global override is set. A global override can only increase the logging level. It cannot be used to lower module logging levels that were previously set higher. It is also possible to globally limit logs by providing a maximum severity level present in the system. For instance, if the maximum level in the system is set to INFO, messages less severe than the info level (i.e DEBUG) will be excluded.

Each module that is using the logger must specify a unique name and register itself to the logger. We will cover how to do this in Exercise 2. If the module consists of more than one file, registration is performed in one file but each file must declare the module name.

Summary

As we have seen, the logger API has two types of messages: standard and hexdump. When the logger API is called, a message is created and added to a dedicated, configurable buffer containing all log messages. Each message contains a source ID, timestamp, and severity level. A standard message contains a pointer to the string and any arguments. Hexdump message contains copied data. We will cover how to use the logger module in Exercise 2 and Exercise 3.

The logger module is designed to be thread-safe and minimizes the time needed to log the message. Time-consuming operations like string formatting or obtaining access to the transport (i.e UART, RTT or whatever backend you are using) are not performed by default when the logger API is called.