3.7. Logging

In order to efficiently debug and diagnose the Plug & Trust Middleware and its supported use-cases and examples, it supports logging. The logging Framework is written in such a way that embedded platforms are kept in mind and at priority. The choice of logging is done at compile time and not at run time like other high-level languages. Logging library mwlog is compiled and linked to all projects.

3.7.1. Logging level

The logging is divided into following levels:

Error

Some kind of unrecoverable or unexpected error has happened and the behaviour from this point on is most likely to be out of specifications/expectations. The suffix for this in Logging APIs is E.

Warn

Whatever happened is unexpected but not fatal, the severity of the warning requires the judgement of the user. The suffix for this in Logging APIs is W.

Info

This is information for the user. The suffix for this in Logging APIs is I.

Debug

These are verbose low level diagnostic debug messages and not to be used/enabled in normal circumstances. The suffix for this in Logging APIs is D.

3.7.2. Adding log messages into the source code

  1. Add one of Logging Header Files to the C source code.

    Warning

    Only for C source code. Never add the logging files to header files, only add them to C source code.

  2. Call applicable Logging APIs at respective Logging level

  3. Re-compile and re-run the software.

3.7.3. Logging APIs

The following are the loggings APIs to be called from the source code.

LOG_I(…)

Log any value. C language format specifiers and values can be used if needed.

LOG_X8_I(VALUE)

Log a HEX number 8 bits wide

LOG_U8_I(VALUE)

Log an unsigned decimal number 8 bits wide

LOG_X16_I(VALUE)

Log a HEX number 16 bits wide

LOG_U16_I(VALUE)

Log a unsigned decimal number 16 bits wide

LOG_X32_I(VALUE)

Log a HEX number 32 bits wide

LOG_U32_I(VALUE)

Log an unsigned number 32 bits wide

LOG_AU8_I(ARRAY, LEN)

Log an array of 8bits of length LEN

LOG_MAU8_I(MESSAGE, ARRAY, LEN)

Same as LOG_AU8_I, but use MESSAGE.

  • The logging APIs effectively use preprocessor directives like # and ## and therefore, if the variable names are verbose enough, API calls like LOG_X16_I(statusOfDeleteKey) are enough to log response with relevent information, and efficient for the developer to add a logging instruction.

  • The suffix _I can be replaced with _E, _W or _D based on Logging level. The convention of logging remains the same.

  • Use HEX Values to log enums and other hexadecimal numbers. For items that are not treated as HEX (e.g. length), use the decimal logging APIs.

For example, the APIs can be called as below.

retStatus = DoAPDUTxRx_Case3( /* ..., */
    rspbuf,
    &rspbufLen);
LOG_X16_D(retStatus);
LOG_AU8_D(rspbuf, rspbufLen);

If needed, the same can be made more verbose as below.

LOG_D("Sending FOO Command");
retStatus = DoAPDUTxRx_Case3( /* ..., */
    rspbuf,
    &rspbufLen);

LOG_D("FOO retStatus=0x04X", rtStatus);
LOG_MAU8_D("FOO Command", rspbuf, rspbufLen);

3.7.3.1. Logging - Information

Code:

uint32_t xu32val=0x12341234u;
uint8_t xu8val=0x44;

LOG_I("Values are xu32val=0x%08X xu8val=0x%02X", xu32val, xu8val);

Output:

       App:INFO :Values are xu32val=0x12341234 xu8val=0x44

3.7.3.2. Logging - Variable Names

Code:

    uint32_t xu32val=0x12341234u;
    uint8_t xu8val=0x44;
    unsigned int some_int_value = 783;
    unsigned char some_byte_value = 96;

    LOG_I("Values are:");
    LOG_X8_I(xu8val);
    LOG_U8_I(some_byte_value);
    LOG_X16_I(xu8val);
    LOG_U16_I(some_byte_value);
    LOG_X32_I(xu32val);
    LOG_U32_I(some_int_value);

    /* Logging that will be mis-intepreted */
    LOG_X16_I(some_byte_value);

Output:

       App:INFO :Values are:
       App:INFO :xu8val=0x44
       App:INFO :some_byte_value=96
       App:INFO :xu8val=0x0044
       App:INFO :some_byte_value=96
       App:INFO :xu32val=0x12341234
       App:INFO :some_int_value=783
       App:INFO :some_byte_value=0x0060

3.7.3.3. Logging - Arrays

Code:

    const uint8_t some_array[] = {
        0x5A,  0x5B,  0x5C,  0x5D,
        0x5E,  0x5F,  0x60,  0x61,
        0x62,  0x63,  0x64,  0x65,
        0x66,  0x67,  0x68,  0x69,
        0x6A,  0x6B,  0x6C,  0x6D};
    const uint8_t buffer[] = {
        0x2A,  0x2B,  0x2C,  0x2D,
        0x2E,  0x2F,  0x30,  0x31,
        0x32,  0x33,  0x34,  0x35,
        0x36,  0x37,  0x38,  0x39,
        0x3A,  0x3B,  0x3C,  0x3D,
        0x3E,  0x3F,  0x40,  0x41,
        0x42,  0x43,  0x44,  0x45};
    LOG_AU8_I(some_array, ARRAY_SIZE(some_array));
    LOG_MAU8_I("meaningful name", buffer, ARRAY_SIZE(buffer));

Output:

       App:INFO :some_array (Len=20)
        5A 5B 5C 5D     5E 5F 60 61     62 63 64 65     66 67 68 69
        6A 6B 6C 6D
       App:INFO :meaningful name (Len=28)
        2A 2B 2C 2D     2E 2F 30 31     32 33 34 35     36 37 38 39
        3A 3B 3C 3D     3E 3F 40 41     42 43 44 45

3.7.3.4. Logging - Levels

Code:

    uint32_t xu32val=0x12341234u;
    LOG_X32_D(xu32val);
    LOG_X32_I(xu32val);
    LOG_X32_W(xu32val);
    LOG_X32_E(xu32val);

Output:

       App:INFO :xu32val=0x12341234
       App:WARN :xu32val=0x12341234
       App:ERROR:xu32val=0x12341234

3.7.4. Logging Header Files

Some of the header files for logging are as under.

nxLog_UseCases.h

High level use cases.

nxLog_App.h

Applications and tools.

nxLog_VCOM.h

Logging specifically for VCOM Layer.

nxLog_sss.h

Logging specifically for SSS Layer.

nxLog_hostLib.h

Logging specifically for Host Library Layer.

nxLog_smCom.h

Communication and common layer.

They can be found at hostlib/hostLib/libCommon/log. These files are machine generated and hence is is not recommended to hand edit them.

3.7.5. Changing logging level

To change the level of logging, the following approaches are valid and based on the need of the problem, they can and should be used.

3.7.5.1. Full source-code

hostlib/hostLib/libCommon/log/nxLog_DefaultConfig.h can be modified to change logging level. nxLog_DefaultConfig.h is self documented.

/* See Plug & Trust Middleware Docuemntation --> stack --> Logging
   for more information */

/*
 * - 1 => Enable Debug level logging - for all.
 * - 0 => Disable Debug level logging.  This has to be
 *        enabled individually by other logging
 *        header/source files */
#define NX_LOG_ENABLE_DEFAULT_DEBUG 0

/* Same as NX_LOG_ENABLE_DEFAULT_DEBUG but for Info Level */
#define NX_LOG_ENABLE_DEFAULT_INFO 1

/* Same as NX_LOG_ENABLE_DEFAULT_DEBUG but for Warn Level */
#define NX_LOG_ENABLE_DEFAULT_WARN 1

/* Same as NX_LOG_ENABLE_DEFAULT_DEBUG but for Error Level.
 * Ideally, this shoudl alwasy be kept enabled */
#define NX_LOG_ENABLE_DEFAULT_ERROR 1


/* Release - retail build */
#ifdef FLOW_SILENT
#undef NX_LOG_ENABLE_DEFAULT_DEBUG
#undef NX_LOG_ENABLE_DEFAULT_INFO
#undef NX_LOG_ENABLE_DEFAULT_WARN
#undef NX_LOG_ENABLE_DEFAULT_ERROR

#define NX_LOG_ENABLE_DEFAULT_DEBUG 0
#define NX_LOG_ENABLE_DEFAULT_INFO 0
#define NX_LOG_ENABLE_DEFAULT_WARN 0
#define NX_LOG_ENABLE_DEFAULT_ERROR 0
#endif

3.7.5.2. Logging at component level

For example, changing logging level of App, hostlib/hostLib/libCommon/log/nxLog_App.h can be updated. As shown below, in nxLog_App.h, the values of NX_LOG_ENABLE_APP_INFO, etc. can be updated.

/* If source file, or nxLog_Config.h has not set it, set these defines
 *
 * Do not #undef these values, rather set to 0/1. This way we can
 * jump to definition and avoid plain-old-text-search to jump to
 * undef. */

#ifndef NX_LOG_ENABLE_APP_DEBUG
#   define NX_LOG_ENABLE_APP_DEBUG (NX_LOG_ENABLE_DEFAULT_DEBUG)
#endif
#ifndef NX_LOG_ENABLE_APP_INFO
#   define NX_LOG_ENABLE_APP_INFO (NX_LOG_ENABLE_APP_DEBUG + NX_LOG_ENABLE_DEFAULT_INFO)
#endif
#ifndef NX_LOG_ENABLE_APP_WARN
#   define NX_LOG_ENABLE_APP_WARN (NX_LOG_ENABLE_APP_INFO + NX_LOG_ENABLE_DEFAULT_WARN)
#endif
#ifndef NX_LOG_ENABLE_APP_ERROR
#   define NX_LOG_ENABLE_APP_ERROR (NX_LOG_ENABLE_APP_WARN + NX_LOG_ENABLE_DEFAULT_ERROR)
#endif

3.7.5.3. Individual files

Rather than applying logging levels to full stack, if the need is to set the logging level in individual files, the individual source file can set required defined before including the respective log file.

e.g. The below lines will set log level to maximum:

#define NX_LOG_ENABLE_APP_DEBUG 1
#include <nxLog_App.h>

e.g. The below lines will set log level to just error:

#define NX_LOG_ENABLE_APP_DEBUG 0
#define NX_LOG_ENABLE_APP_INFO 0
#define NX_LOG_ENABLE_APP_WARN 0
#define NX_LOG_ENABLE_APP_ERROR 1
#include <nxLog_App.h>

nxLog_App.h and _APP_ needs to be replaced with respective names as per the list in Logging Header Files.