System Log

The system log provides real-time information about the events inside the kernel at different severity levels. Additionally, the user can use the same log interface as well. Log messages give developers insight into application faults and can trace their origin. For example, if the user forgets to register an interrupt handler, the kernel will still log an interrupt event.


The log API is split into front-end and back-ends. The front-end macros format messages, and the back-ends transport the messages to a host system. The system log was designed with the following requirements in mind:

  1. Use a global logger with static function calls (the log back-end does not have to be known in a module).
  2. Tag message severity (trace, debug, info, warning and error).
  3. Filter messages by severity and module at compile time.
  4. Abstract message transport so that the log is technology independent.

The kernel supports message formatting on the target using the standard library. As the log front-end the log facade [45] is used. The library provides a lightweight logging facade commonly used in Rust. The user can the implement or use an existing log back-end. The log facade supports message filtering at compile and run-time.

If the impact on performance from string formatting on the target is too excessive, defmt [35] offers an alternative. The library defers formatting to the host and uses efficient binary transport. However, defmt requires the symbol file of the firmware binary and an application running to host to interpret the messages.


Activating system logs consists of three parts: enabling logs in the kernel, selecting a front-end and back-end (transport).


# log facade
bern-kernel = { version = "0.3", features = ["log-global"1] }
log = "0.4"
rtt-target = { version = "0.3", features = ["cortex-m"] }
# or defmt
bern-kernel = { version = "0.3", features = ["log-defmt"2] }
defmt = "0.3"
defmt-rtt = "0.3" # transport using RTT

Listing: Cargo log dependency.

In this example, the log facade is enabled at 1 in the listing. The log facade is added as dependency and as a method transport real-time transfer (RTT) is used. Using defmt 2 is very similar to the log facade.

Depending on the log front-end in use, some implementation is needed by the user. For the log facade, a trait must be implemented as documented in [45]. When using defmt an additional linker script is introduced to the build process [36].


use bern_kernel::log::{warn, info, error};1

fn main() -> ! {
    warn!("Process {} is out of memory.", &proc_name);2

Listing: Log usage on application level.

In the example in the listing the re-exported log macros from the front-end are included at 1. Log messages can then be created using the macros 2.

Keep in mind that formatting messages on the target requires stack space. Look out for stack overflow warning from the kernel when first activating logs in the application.

the listing contains an example log output showing messages from the kernel and the user application. The log was printed in the host command line using probe-run.

'**\color{green**{INFO}}'  Starting Bern RTOS interrupt example. (main)
'**\color{bfhgrey**{TRACE}}' Try to allocate 2B at 0x20002000 (bern_kernel::alloc::bump)
'**\color{bfhgrey**{TRACE}}' Try to allocate 84B at 0x200012a0 (bern_kernel::alloc::bump)
'**\color{blue**{DEBUG}}' Staring kernel. (bern_kernel::kernel)
'**\color{bfhgrey**{TRACE}}' IRQ 40 called. (bern_kernel::sched)
'**\color{green**{INFO}}'  Button pressed. (main)

Listing: Example log output.