McuLog: Logging Framework for small Embedded Microcontroller Systems

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.

Log Output

Log Output

Outline

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

Source Files

The McuLog consists of three files (see links to GitHub at the end of this article) only:

  1. McuLogconfig.h: configuration header file
  2. McuLog.h: interface file
  3. 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.

Usage

To use the McuLog module, include its interface:

 
#include "McuLog.h"

Before using it, call its initialization routine:

 
McuLog_Init();

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:

McuLog Command Line and Status

McuLog Command Line and Status

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:

ANSI Colors in SEGGER RTT

ANSI Colors in SEGGER RTT

color coding

color coding

Logging to a File on the Host

Logging to a file on the host is implemented using RTT.

Start Data Logging on the host:

Start Data Logging

Start Data Logging

Then select the file where to log the data. Data is logged in a text file:

RTT Log File

RTT Log 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:

McuLog_open_logfile("0:\myLogFile.txt");

The file is ‘synced’ for each log entry. To stop logging call

McuLog_close_logfile();

The messages will be present in the file on the embedded file system:

Logging to Embedded File System

Logging to Embedded File System

Summary

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 🙂

Links

 

32 thoughts on “McuLog: Logging Framework for small Embedded Microcontroller Systems

  1. What would be the requirements to run McuLog? For example, the cpu must be capable of running FreeRTOS and have 6KB of program space.. what about RAM? SEGGER RTT Logger?

    Like

  2. I like this as a small simple logging module- thanks Erich.
    I’ve extended the ‘trace’ logic to be enableable, via a bitmask- mainly because I have only a single UART to get any information over. This way I can group messages by logical function:
    Trace_feature(TraceMaskBitUART, “A UART trace\n”);
    This is only displayed when the TraceMaskBitUART bit is set, which I’d do with
    traceon 400
    for instance (also, ‘traceoff %x’)
    I’ve found this really useful to be able to debug modules by logical function without excessive ‘noise’. In fact, it has been my primary debugging method for several years. Hopefully it’ll help someone out.
    Cheers

    Like

    • Hi Rhys,
      I really like that idea of having such a bit mask, and that you can turn on/off logging for a group of things! I think I have to add something like this too :-).

      Like

      • Glad if you can make some use of it. Happy to send you my source as a reference if you like, I’d just rather not post it verbatim publicly. Can you contact me offline? (and not post this comment 😛 )

        Like

  3. Hi Erich,
    I am looking for a simple embedded log which should:
    1. Store the log in SRAM or flash memory, and the log has only 100 entries of 32 or 64-byte each, and organized as circular buffer manner.
    2. No any specific SW needed but a terminal SW such as TeraTerm to access the log via a serial port, and it will display the log content only when a command is received.

    Not sure if your log or the logs you mentioned can be the modified and configured with the requirements above.
    Thanks

    Like

  4. Pingback: How to get Data off an Embedded System: FatFS with USB MSD Host and FreeRTOS Direct Task Notification | MCU on Eclipse

  5. Pingback: How to Use Eclipse CDT Environment Variables in C/C++ Code | MCU on Eclipse

  6. Hello @Erich, seems a very interesting library indeed! Allow me for a probably noob question, but this library is only implemented in MCULib (so I’d guess it only works for NXP Processor Expert projects), so if I would repurpose it for a STM32Cube project, I’d need to reimplement it?

    Thank you for your attention

    Like

    • No, it can work with any microcontroller, as the library and McuLib is implemented in C. I’m mainly using it with NXP parts, but as well with microcontrollers from other vendors (see the list of processors in McuLibConfig.h).

      Like

  7. Pingback: assert(), __FILE__, Path and other cool GNU gcc Tricks to be aware of | MCU on Eclipse

  8. Hi Erich,
    I’ve some questions about your logging framework. In this post is written, that it consist only of three files, McuLog.c/.h and McuLogconfig.h, but when i put those three files into my project i see there is missing McuShell_ConstStdIOType which is defined in McuShell.h. Are there other dependencies? And can i port McuLog to CC26x2 (from ti) chip in easy way?

    Thanks, tm

    Like

    • From the time of the article, the logging framework has been enhanced a bit. While technically the logging is done with these 3 files, there are others needed for example to get a time stamp (McuTimeDate), using RTOS (mutex) (McuRTOS), writing to a file (FatFS) string utilities (McuUtility) or the McuShell for writing to an UART or similar.
      As for porting to the CC26x2, you simply could remove the not needed parts and replace the McuShell with you own UART (or whatever) writing routines.

      Like

  9. Hi Erich,

    I am trying to get McuLog to run on STM32CubeIDE with STM32G4 MCU.
    So far I imported your library and added McuLib/config and McuLib/src to “Paths and Symbols” > “Includes” as well as “Source Location” in CubeIDE.

    I changed the following two things in McuLibconfig.h:
    1. Enabled STM32:
    #define McuLib_CONFIG_CPU_IS_STM32 (1 && McuLib_CONFIG_CPU_IS_ARM_CORTEX_M)
    2. Disabled FreeRTOS:
    #define McuLib_CONFIG_SDK_USE_FREERTOS (0)
    (BTW: You have this FreeRTOS #define twice in your config file for some reason.)

    So now I get to compilation but get
    #error “Unsupported SDK!” in C11.c, C21.c, Clock1.c, DQ1.c.
    I don’t expect those modules to be required or am I wrong? So what is the best way to remove them from compilation?

    Also, will logging to console just work with STM32Cube? I see you are sending a pointer to an UART in your Kinetis example, how would that translate for the SMT32?
    Your config file states the following:
    #define McuLib_CONFIG_SDK_VERSION_USED McuLib_CONFIG_SDK_GENERIC
    /*!< identify the version of SDK/API used. For STM32 we are using a generic SDK (actually the CubeMX one) */
    So does this already work with CubeMX?

    Thanks in advance for your help!

    Anguel

    Liked by 1 person

    • Hi Anguel,
      >>(BTW: You have this FreeRTOS #define twice in your config file for some reason.)
      thanks for reporting. That’s a copy-paste error, just fixed now.

      >>I don’t expect those modules to be required or am I wrong? So what is the best way to remove them from compilation?
      Yes, they use the legacy Processor Expert API and SDK. Simply removed these .c files from your project and you should be fine.

      >>Also, will logging to console just work with STM32Cube? I see you are sending a pointer to an UART in your Kinetis example, how would that translate for the SMT32?
      The logger supports different channels including UART. For the UART you would have to call a stdio for your UART driver. I hope you are using a SEGGER J-Link, in that case you simply can use the Segger RTT and you do not have a need to setup the UART.
      Then assign your UART as channel for the logger with
      McuLog_set_console(yourUARTstdio, 0); for channel 0.

      And yes, it works for any environment, I do have it for STM, ESP32 and NXP parts running.

      I hope this helps,
      Erich

      Like

      • Erich, thank you for the fast reply.
        It turned out that there were many more problematic files, e.g. containing GPIO definitions that did not compile with STM32CubeIDE. So I decided to move out all src files and add them one by one as required to make McuLog compile.

        Regarding console logging I really hoped to make simple UART output to Putty work. Until now I was using a simple redirected printf() for logging to the UART as described here:
        http://www.emcu.eu/how-to-implement-printf-for-send-message-via-usb-on-stm32-nucleo-boards-using-atollic/
        Do you think this can be somehow made to work with McuLog? Not sure if this can be somehow redirected to the McuShell_StdIOType you use 😦
        Thanks,
        Anguel

        Like

        • Thanks Erich, this sounds very interesting! I think UART is always helpful as it does not require any proprietary solutions. I see your project is based on VSCode but I hope that it will also be usable with ST’s CubeMX as many people (like me) are using it now for configuring the ST hardware 🙂
          For now I am experimenting with the logging lib from Robert Poor as it is very straight forward by just configuring my own printf(), based on the UART redirection link from my last post. Of course it is not as nice as your solution, so please keep us updated. Thanks in advance!

          Liked by 1 person

        • Yes, I started using VSCode as it could become the next standard (maybe) ;-). So your request was a good quick exercise for me to use it for an STM part which worked very well. Using Robert Poor’s library and implementation is always an excellent choice for sure.

          Like

    • Hi Thomas,
      yes, I played a little bit with it. What I need in my most loggings are the target time stamps. I understand that this can be added to the custom message in trice, so having this somehow by default would be a plus for me.
      I miss as well the source file and line information (or did I miss it). Overall I like the concept, but the need for the instrumentation makes things complex, and in my logging world it is more about getting the information easily out of the target, and there are not frequent log messages, so performance is less of a concern: ease-of-use is more important in my cases.

      Erich

      Liked by 1 person

      • Adding appropriate functionality for target timestamps and source location should be no big deal. Thanks for the hint, Erich. I will try to find a solution. Right now you can enable displaying the ID’s for quick finding the source location if you don’t like to add __LINE__ as parameter. Any idea to make the instrumentation easier? I personally feel it is straight forward but I cannot look at it as a newcomer anymore. My main aim was small general footprint and speed and the ability to log inside interrupts and also to have colors.

        Liked by 1 person

  10. Pingback: Visual Studio Code for C/C++ with ARM Cortex-M: Part 9 – RTT | MCU on Eclipse

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.