Configurable Logging

Introduction
Quick Overview
Detailed Specification

Introduction

Logging program execution state is essential during development. Some minimal logging is often desirable also in production code. There is a need (especially in embedded applications with limited HW resources) to have a simple and reliable way of configuring which logging is present in which SW build edition. Additional motivation for having some logging exist in only certain SW builds is intellectual property protection where the inner program workings need to be hidden and, therefore, any detail-revealing logging must not be present.

Traditional way of achieving the above goals (particularly in simpler applications written in C) is the use of a printf-like macro. The macro is defined to have implementation with logging functionality in development builds and to have void implementation, thus to be removed from the source code by a pre-processor, in production builds. Complete removal of some logging code by the pre-processor guarantees its absence and zero run-time overhead in production builds.

PETR provides Scribo – simple and flexible logging system suitable for embedded C and C++ applications.

Quick Overview

Scribo is simple and flexible logging system suitable for embedded C and C++ applications.

Each Scribo log message is characterized by its (optional) category and (optional) verbosity. To generate message text itself, Scribo uses the same style as printf function i.e. format, ... with two additions. The first addition is that the format may be omitted. In this case, only log message header (optionally followed by default message text) is output. The second addition is that newline ('\n') is automatically appended at the end of each log message. Category is user-defined per-source-file (i.e. per translation a.k.a. compilation unit) string (see Detailed Specification below for precise definition). It is optional and, when not defined, the default category GENERIC is used. There are eight levels of verbosity (from the least to the most verbose): FATAL, ERROR, WARNING, LOG, INFO, DEBUG, METHOD, and TRACE. Verbosity is also optional and, when not specified, the default TRACE verbosity is used.

Below is a simple example of basic Scribo usage.

First add logging to your source code:

// foo.c

#define PETR_SCRIBO_CATEGORY FOO // Category (FOO) of all logging messages generated in this translation unit.
#include "petr/Scribo.h"

void doFoo()
{
    SCRIBE(LOG, "Executing doFoo()"); // Enabled in both production and development builds (see Scribo.cfg below).
    for (int i = 0; i < 4; i++)
    {
        SCRIBE(DEBUG, "Iteration %d", i); // Disabled in production (verbosity >= DEBUG) (see Scribo.cfg below).
    }
}
// bar.c

#include "petr/Scribo.h"

void doBar()
{
    SCRIBE(LOG, "Executing doBar()"); // Disabled in production (category == GENERIC) (see Scribo.cfg below).
}

Then specify target configuration:

// Scribo.cfg

#if defined(PRODUCTION) || ! defined(DEVELOPMENT)
    // Production (disable excessive logging).
#   define PETR_SCRIBO_DISABLE_CATEGORY_GENERIC 1    // No logging for unspecified category.
#   define PETR_SCRIBO_DISABLE_VERBOSITY_DEBUG_ETC 1 // No logging for debugging or more verbose.
#else
    // Development (enable all logging).
#endif

Then build targets:

gcc -o app_dev  -D DEVELOPMENT *.c
gcc -o app_prod -D PRODUCTION  *.c

Finally execute the application:

$app_dev
2019-09-14 10:55:03 #0000000000 FOO     LOG     : Executing doFoo()
2019-09-14 10:55:03 #0000000001 FOO     DEBUG   : Iteration 0
2019-09-14 10:55:03 #0000000002 FOO     DEBUG   : Iteration 1
2019-09-14 10:55:03 #0000000003 FOO     DEBUG   : Iteration 2
2019-09-14 10:55:03 #0000000004 FOO     DEBUG   : Iteration 3
2019-09-14 10:55:03 #0000000005 GENERIC LOG     : Executing doBar()
$
$app_prod
2019-09-14 10:55:04 #0000000003 FOO     LOG     : Executing doFoo()
$

Detailed Specification

Leave a Reply

Your email address will not be published. Required fields are marked *

Copyright © 2013-2023 BeneQuidem Generated 2024-06-13T20:47:28Z