An essential tool especially developing larger applications or distributed firmware is to use logging. This article presents an open source logging framework I’m using. It is small and easy to use and can log to a console, to a file on the host or even to a file on an embedded file system as FatFS.
While it is possible to monitor with the debugger multiple targets, it gets very difficult if the system is distributed or runs for days and weeks continuously as in this system. If something goes wrong, logs will be invaluable. While logging framework for host development are common, I have not found much suitable for embedded system. What I have found close to what I wanted was https://github.com/rxi/log.c by rxi or uLog (https://github.com/rdpoor/ulog) by Robert Poor. But I needed something more integrated and ready to use for my applications:
- Different log levels (trace, warning, information, error, fatal)
- Ability to set a log level (only log above a level)
- Color coding based on log level
- Open argument list (printf() style)
- Different ways to log: UART/Terminal, file on the host, embedded file system
- Command line/Shell interface
- Small footprint, depending on the features used
- Configurable, up to the point to completely disable it
- Reentrant and integrated with FreeRTOS
- Automatic and configurable date/timestamping
- Easy to integrate, written in C
The McuLog consists of three files (see links to GitHub at the end of this article) only:
- McuLogconfig.h: configuration header file
- McuLog.h: interface file
- McuLog.c: imlementation file
The configuration header file is used to configure the log format (with or without date, colors, if file system or RTT file logging shall be supported, etc). At the time of this article the following configuration items are available:
/* * Copyright (c) 2020, Erich Styger * * SPDX-License-Identifier: BSD-3-Clause */ #ifndef MCULOGCONFIG_H_ #define MCULOGCONFIG_H_ #include "McuLib.h" #ifndef MCULOG_CONFIG_IS_ENABLED #define MCULOG_CONFIG_IS_ENABLED (1) /* 1: Logging is enabled; 0: Logging is disabled, not adding anything to the application code */ #endif #ifndef MCULOG_CONFIG_USE_MUTEX #define MCULOG_CONFIG_USE_MUTEX (1 && McuLib_CONFIG_SDK_USE_FREERTOS) /* 1: use a RTOS mutex for the logging module; 0: do not use a mutex */ #endif #ifndef MCULOG_CONFIG_USE_COLOR #define MCULOG_CONFIG_USE_COLOR (1) /* 1: use use ANSI color for terminal, 0: do not use color */ #endif #ifndef MCULOG_CONFIG_USE_FILE #define MCULOG_CONFIG_USE_FILE (0) /* 1: use use file for logging, 0: do not use file */ #endif #ifndef MCULOG_CONFIG_LOG_TIMESTAMP_DATE #define MCULOG_CONFIG_LOG_TIMESTAMP_DATE (1) /* 1: add date to timestamp, 0: do not date for timestamp */ #endif #ifndef MCULOG_CONFIG_USE_RTT_LOGGER #define MCULOG_CONFIG_USE_RTT_LOGGER (1) /* use use SEGGER RTT Logger (Channel 1), 0: do not use SEGGER RTT Logger */ #endif #ifndef MCULOG_CONFIG_PARSE_COMMAND_ENABLED #define MCULOG_CONFIG_PARSE_COMMAND_ENABLED (1 && MCULOG_CONFIG_IS_ENABLED) /* 1: shell command line parser enabled; 0: not enabled */ #endif #endif /* MCULOGCONFIG_H_ */
Settings can be turned on/off at runtime too, e.g.
void McuLog_set_color(bool enable);
can be used to turn on/off color coding.
To use the McuLog module, include its interface:
Before using it, call its initialization routine:
Next, register one or more logger for the output, for example:
McuLog_open_logfile("0:\log.txt"); /* logging to a file */ McuLog_set_console(&UART_stdio); /* logging to a console */ McuLog_set_rtt_logger(true); /* log to a file on the host using RTT Data Logging */
After that, you can log messages with one of the available logging functions, e.g.
McuLog_trace("This is a trace message"); McuLog_debug("Function called returned error code %d", returnValue); McuLog_info("Application started"); McuLog_warn("Log on memory, available bytes %d", HeapNofBytes); McuLog_error("Failed creating task '%s', error code %d", taskName, res); McuLog_fatal("Watchdog timed out, waiting for reset...");
Command Line Shell
McuLog includes an optional command line shell which is used to check the status and to configure it:
Logging to a Console
If logging to a console (or UART), it will print the messages on that connection. If the console is able to display colors (e.g. PuTTY or Segger RTT Console/Viewer), the messages are shown with different colors:
Logging to a File on the Host
Logging to a file on the host is implemented using RTT.
Start Data Logging on the host:
Then select the file where to log the data. Data is logged in a text file:
Logging to a file can be stopped any time.
Logging to a file on the target
The logger includes logging to an embedded file system using FatFS. It is possible to open and manage the file on the application side.
Or simply create or open the file with the McuLog:
The file is ‘synced’ for each log entry. To stop logging call
The messages will be present in the file on the embedded file system:
With the McuLog I have a small and versatile logging module which can log to console, host file system or to an embedded file system with small overhead. The module requires about 6 KByte flash (mostly because of printf() style), this without any code optimization. It uses simple callbacks and hooks, so if you need logging over I2C, USB, SPI, … or any other communication channel, this can be easily added or extended.
I hope you find this McuLog module as useful as I do.
Happy Logging 🙂
- McuLog Source Files on GitHub:
- Implementation in McuOnEclipse McuLib: https://github.com/ErichStyger/McuOnEclipseLibrary
- Example project on GitHub using McuLog and logging toFatFS (SD Card): https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/MCUXpresso/FRDM-K22F/FRDM-K22F_FatFS_SD_Card, see “FatFS, MinIni, Shell and FreeRTOS for the NXP K22FN512“
- uLog by Robert Poor: https://github.com/rdpoor/ulog
- log.c by rxi: https://github.com/rxi/log.c
- Segger RTT: Using Segger Real Time Terminal (RTT) with Eclipse