All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Logging API

Click here for the API Reference documentation.


Logging Basics
Log Controls
Log Formats

Logging is a critical part of any embedded development framework. Most devices can't display error or warning messages and don't have a human user monitoring them. Even when a device does have a display and a user watching it, the log messages often don't help the device's primary user. And displaying messages on a screen doesn't support remote troubleshooting; especially when the device is hidden from view inside a piece of equipment or located in remote geographic regions.

The Legato Logging API provides a toolkit allowing code to be instrumented with error, warning, informational, and debugging messages. These messages can be turned on or off remotely and pushed or pulled from the device through a secure shell, cloud services interfaces, e-mail, SMS, etc.

Logging Basics

Levels
Basic Logging
Conditional Logging
Fatal Errors
Tracing
Result Code Text

Levels

Log messages are categorized according to the severity of the information being logged. A log message may be purely informational, describing something that is expected to occur from time-to-time during normal operation; or it may be a report of a fault that might have a significant negative impact on the operation of the system. To differentiate these, each log entry is associated with one of the following log levels:

  • DEBUG: Handy during troubleshooting, but is normally just log clutter.
  • INFORMATION: Expected to happen; can be interesting even when not troubleshooting.
  • WARNING: Should not normally happen; may not have any real impact on system performance.
  • ERROR: Fault that may result in noticeable short-term system misbehaviour. Needs attention.
  • CRITICAL: Fault needs urgent attention. Will likely result in system failure.
  • EMERGENCY: Definite system failure.

Basic Logging

A series of macros are available to make logging easy.

None of them return anything.

All of them accept printf-style arguments, consisting of a format string followed by zero or more parameters to be printed (depending on the contents of the format string).

There is a logging macro for each of the log levels:

For example,

LE_INFO("Obtained new IP address %s.", ipAddrStr);

Conditional Logging

Similar to the basic macros, but these contain a conditional expression as their first parameter. If this expression equals true, then the macro will generate this log output:

Instead of writing

if (result == -1)
{
LE_WARN("Failed to send message to server. Errno = %m.");
}

you could write this:

LE_WARN_IF(result == -1, "Failed to send message to server. Errno = %m.");

Fatal Errors

There are some special logging macros that are intended for logging fatal errors:

  • LE_FATAL(formatString, ...)
    Always kills the calling process after logging the message at EMERGENCY level (never returns).
  • LE_FATAL_IF(condition, formatString, ...)
    If the condition is true, kills the calling process after logging the message at EMERGENCY level.
  • LE_ASSERT(condition)
    If the condition is true, does nothing. If the condition is false, logs the source code text of the condition at EMERGENCY level and kills the calling process.

For example,

if (NULL == objPtr)
{
LE_FATAL("Object pointer is NULL!");
}
// Now I can go ahead and use objPtr, knowing that if it was NULL then LE_FATAL() would have
// been called and LE_FATAL() never returns.

or,

LE_FATAL_IF(NULL == objPtr, "Object pointer is NULL!");
// Now I can go ahead and use objPtr, knowing that if it was NULL then LE_FATAL_IF() would not
// have returned.

or,

LE_ASSERT(NULL != objPtr);
// Now I can go ahead and use objPtr, knowing that if it was NULL then LE_ASSERT() would not
// have returned.

Tracing

Finally, a macro is provided for tracing:

This macro is special because it's independent of log level. Instead, trace messages are associated with a trace keyword. Tracing can be enabled and disabled based on these keywords.

If a developer wanted to trace the creation of "shape" objects in their GUI package, they could add trace statements like the following:

LE_TRACE(NewShapeTraceRef, "Created %p with position (%d,%d).", shapePtr, shapePtr->x, shapePtr->y);

The reference to the trace is obtained at start-up as follows:

NewShapeTraceRef = le_log_GetTraceRef("newShape");

This allows enabling and disabling these LE_TRACE() calls using the "newShape" keyword through configuration settings and runtime log control tools. See Log Controls below.

Applications can use LE_IS_TRACE_ENABLED(NewShapeTraceRef) to query whether a trace keyword is enabled.

These allow applications to hook into the trace management system to use it to implement sophisticated, application-specific tracing or profiling features.

Result Code Text

The le_result_t macro supports printing an error condition in a human-readable text string.

result = le_foo_DoSomething();
if (result != LE_OK)
{
LE_ERROR("Failed to do something. Result = %d (%s).", result, LE_RESULT_TXT(result));
}

Log Controls

Log Control Tool
Log Control Configuration Settings
Environment Variables
LE_LOG_LEVEL
LE_LOG_TRACE
Programmatic Log Control

Log level filtering and tracing can be controlled at runtime using:

  • the command-line Log Control Tool ("log")
  • configuration settings
  • environment variables
  • function calls.

Log Control Tool

The log control tool is used from the command-line to control the log level filtering, log output location (syslog/stderr), and tracing for different components within a running system.

Online documentation can be accessed from the log control tool by running "log help".

Here are some code samples.

To set the log level to INFO for a component "myComp" running in all processes with the name "myProc":

$ log level INFO myProc/myComp

To set the log level to DEBUG for a component "myComp" running in a process with PID 1234:

$ log level DEBUG 1234/myComp

To enable all LE_TRACE statements tagged with the keyword "foo" in a component called "myComp" running in all processes called "myProc":

$ log trace foo myProc/myComp

To disable the trace statements tagged with "foo" in the component "myComp" in processes called "myProc":

$ log stoptrace foo myProc/myComp

With all of the above examples "*" can be used in place of the process name or a component name (or both) to mean "all processes" and/or "all components".

Log Control Configuration Settings

Note
The configuration settings haven't been implemented yet.

Environment Variables

Environment variables can be used to control the default log settings, taking effect immediately at process start-up; even before the Log Control Daemon has been connected to.

Settings in the Log Control Daemon (applied through configuration and/or the log control tool) will override the environment variable settings when the process connects to the Log Control Daemon.

LE_LOG_LEVEL

LE_LOG_LEVEL can be used to set the default log filter level for all components in the process. Valid values are:

  • EMERGENCY
  • CRITICAL
  • ERROR
  • WARNING
  • INFO
  • DEBUG

For example,

$ export LE_LOG_LEVEL=DEBUG

LE_LOG_TRACE

LE_LOG_TRACE allows trace keywords to be enabled by default. The contents of this variable is a colon-separated list of keywords that should be enabled. Each keyword must be prefixed with a component name followed by a slash ('/').

For example,

$ export LE_LOG_TRACE=framework/fdMonitor:framework/logControl

Programmatic Log Control

Normally, configuration settings and the log control tool should suffice for controlling logging functionality. In some situations, it can be convenient to control logging programmatically in C.

To set the log filter level, le_log_SetFilterLevel() is provided.

Trace keywords can be enabled and disabled programmatically by calling le_log_EnableTrace() and le_log_DisableTrace().

Log Formats

Log entries can also contain any of these:

  • timestamp (century, year, month, day, hours, minutes, seconds, milliseconds, microseconds)
  • level (debug, info, warning, etc.) or trace keyword
  • process ID
  • component name
  • thread name
  • source code file name
  • function name
  • source code line number

Log messages have the following format:

Jan  3 02:37:56  INFO  | processName[pid]/componentName T=threadName | fileName.c funcName() lineNum | Message

Copyright (C) Sierra Wireless, Inc. 2014. All rights reserved. Use of this work is subject to license.