Event Reporting (ER)
The ER framework in RCSW is meant to be a flexible and adaptable “front-end” to any number of implementations (plugins), providing a common interface and facilities. Important aspects/terminology:
The unit into which related functionality (and therefore event reporting) is grouped is called a module. Modules are file-based; that is, each
.c
file can correspond to at most one module.A module is active if it has a valid handle; that is, if
RCSW_ER_PLUGIN_INSMOD()
was previously called for the module.
Usage
To use one of the existing RCSW ER plugins in your code, you need to:
#define RCSW_ER_MODID
and/or#define RCSW_ER_MODNAME
to be the numeric UUID and string UUID for a module in your code and then#include <rcsw/er/client.h>
.Tip
Not all ER plugins use both
RCSW_ER_MODID
andRCSW_ER_MODNAME
; see documentation for relevant plugins for details. Still, it’s generally good practice to always define both to make your code more robust and compatible with any RCSW ER plugin.For example, in
src/ds/rbuffer.c
we have something like this near the top of the file:#define RCSW_ER_MODID 0x0000100000000000 #define RCSW_ER_MODNAME "rcsw.ds.rbuffer" #include "rcsw/er/client.h"
Call
RCSW_ER_MODULE_INIT()
somewhere in your.c
file, ideally in an initialization function. If a.c
file you want to define a module for does not have such a function, you can put it in a common initialization function for your library/application. E.g.:void library_init(void) { #define RCSW_ER_MODID 0x1 #define RCSW_ER_MODNAME "myapp.module1" RCSW_ER_MODULE_INIT() #define RCSW_ER_MODID 0x2 #define RCSW_ER_MODNAME "myapp.module2" RCSW_ER_MODULE_INIT() }
or:
void library_init(void) { RCSW_ER_INSMOD(0x1, "myapp.module1") RCSW_ER_INSMOD(0x2, "myapp.module1h2") }
The two forms are equivalent. The first is generally more convenient for more OOP-oriented modules in separate files.
Note
If you want to be able to use the same code to define modules for
different plugins, use RCSW_ER_MODNAME_BUILDER
to define
the names so that they will work with every plugin.
Levels
The reporting levels which RCSW uses across all its plugins should be familiar if you’ve dealt with logging before. All plugins support all logging levels. The levels are, ordered from most to least severe:
NONE
- No event reportingFATAL
- Only FATAL events are emittedERROR
- [FATAL, ERROR] events emittedINFO
- [FATAL, ERROR, INFO] events emittedDEBUG
- [FATAL, ERROR, INFO, DEBUG] events emittedTRACE
- [FATAL, ERROR, INFO, DEBUG, TRACE] events emittedALL
- All events emitted
A somewhat unique feature: because RCSW works with LIBRA, you can set the levels for modules in two ways:
At compile-time, effectively disabling any events are lower severity levels; i.e., they will be compiled out.
At run-time, as you would expect (if the selected plugin supports it).
Plugins
The simple bare-bones logger.
This plugin uses RCSW’s built-in stdio_printf()
and minimal stdlib
implementation, to provide the necessary logging functionality in environments
where stdlib is not available, and/or using stdlib hogs too much space.
In this plugin, each source file within RCSW and of each project which links
with RCSW can define a logging “module”; modules are file-based, and therefore
you can’t have multiple modules/loggers in a single file. If you architect your
projects well, this should not be a burdensome restriction. You can split a
logging module across several files if you want by defining
RCSW_ER_MODNAME
equivalently.
The modules in this plugin:
Are unconditionally enabled and cannot be disabled; that is, each time a logging statement is encountered during execution, it is always emitted.
Do not have a “level” associated with them which determines if a given logging statement should be emitted. Put another way, there is only a single global module whose level is set at compile time; statements less than the compile-time level are compiled out.
The name of each module is prepended to each logging statement to help identify the logging source.
Each emitted logging statement is of the form:
<RCSW_ER_MODNAME> [LVL] <message>
LVL
is one of [FATAL, ERROR, INFO, WARN, DEBUG, TRACE], and
<message>
is the rendered message. RCSW_ER_MODNAME
defines the
logical name of the module.
This plugin is useful in:
Bare metal environments such as bootstraps without an OS.
Bare metal hardware validation tests.
Plugin Configuration Details
Configuration Item |
Notes |
---|---|
|
Defined as |
|
Idempotent/not used by this plugin. |
|
Idempotent/not used by this plugin. |
|
None. |
|
Idempotent/not used by this plugin. |
|
Not used by this plugin/always |
The name of the module. Can have any format; it can be convenient to use
a hierarchical format such as |
|
Not used by this plugin. |
A lighter/simpler version of log4c.
In this plugin, each source file within RCSW and of each project which links
with RCSW can define a logging “module”; modules are file-based, and therefore
you can’t have multiple modules/loggers in a single file. If you architect your
projects well, this should not be a burdensome restriction. You can split a
logging module across several files if you want by defining
RCSW_ER_MODNAME
equivalently.
Each module can be enabled/disabled independently in a lightweight manner; unlike log4j loggers, the enable/disable status of one logger/module does not have any effect on the status of another. That is, the hierarchy is “flat”.
This plugin does not provide most features found in log4c, with the exception of levels: INFO, WARN, DEBUG, TRACE, FATAL. If you want something with features comparable to log4c, use the zlog plugin.
Each emitted logging statement is of the form:
<RCSW_ER_MODNAME> [LVL] <message>
LVL
is one of [FATAL, ERROR, INFO, WARN, DEBUG, TRACE], and <message>
is
the rendered message. RCSW_ER_MODNAME
defines the logical name of the
module.
This plugin is useful in:
Medium-complexity embedded systems with limited resources.
Systems where you want to have multiple modules, only some of which should be enabled, but don’t need the hierarchical logging of log4c. In this scheme, modules are either enabled or not whether a given module is enabled has no effect on other modules. In addition, the name given to a specific module is purely for debugging purposes, and has no effect on event reporting; you can have multiple modules with the same name and different IDs, if you want.
Plugin Configuration Details
Configuration Item |
Notes |
---|---|
|
Defined as |
|
Idempotent. Takes no arguments. After initialization, no modules are installed. |
|
Idempotent. Takes no arguments. |
|
None. |
|
Idempotent for the same arguments. Default reporting level after installation is INFO. |
|
Thread-safe, as long as module installation/removal is not done simultaneously. |
The name of the module. Can have any format; it can be convenient to use
a hierarchical format such as |
|
Must be defined uniquely for each module and/or file involved in logging. Well, you don’t have to defined it uniquely, but if you don’t messages will be emitted in surprising ways. |
The zlog plugin.
In this plugin, each source file within RCSW and of each project which links
with RCSW can define a logging “module”; modules are file-based, and therefore
you can’t have multiple modules/loggers in a single file. If you architect your
projects well, this should not be a burdensome restriction. You can split a
logging module across several files if you want by defining
RCSW_ER_MODNAME
equivalently.
Configuration can be controlled via the zlog configuration file or via the zlog API as expected; RCSW does not interfere with/wrap that functionality–only the bits necessary to use logging macros.
Notes:
Because this is a 3rd party plugin, you will have to point RCSW at the headers and the libraries by installing them to
LIBRA_DEPS_PREFIX
. See LIBRA for details.Because zlog does not come with a TRACE level, which RCSW requires, RCSW adds one as documented in the zlog developer docs. In order to get TRACE events to come out as expected, you need to ensure you have the following in your
.conf
file:[levels] # 2023-11-13 [JRH]: zlog doesn't have a TRACE level by default, so we # add it here. The value MUST match the value specified in the zlog ER # plugin, or things won't work correctly. TRACE = 10
There are not any other restrictions/requirements on the
.conf
file.
This plugin is useful in:
Linux targets and/or targets with filesystem.
Systems where you want to have multiple modules, only some of which should be enabled, but and you want hierarchical logging power.
Plugin Configuration Details
Configuration Item |
Notes |
---|---|
|
Defined as |
|
Defined as |
|
Defined as |
|
None. |
|
Idempotent/not used by this plugin. |
|
None. |
The name of the module. Cannothave dots ( |
|
Not used by this plugin. |
A custom plugin which is defined exactly how you want in terms of modules,
levels, etc. To integrate your plugin with RCSW, you must create a .h
file
with a few #define
macros. Your .h
file must be specified at configure
time via -DRCSW_CONFIG_ER_PLUGIN_PATH=/path/to/your/file
. If your plugin
uses RCSW_ER_MODID
, it should support 64-bit IDs if you want to use
RCSW with it.
Important
Your .h
file is NOT installed/packaged with RCSW, so you
will need to ensure that it is findable by any applications you
want to use with RCSW+your custom ER plugin.
Required plugin file contents (you can of course have whatever else you want in the file):
The main ER plugin hook. Will be called as part of every
ER_WARN()
, etc. statement. Arguments:
LVL
- The level of the statement. See Levels for details.HANDLE
- Whatever was returned fromRCSW_ER_PLUGIN_HANDLE()
.ID
- The ID of the current module (file). This will expand to nothing ifRCSW_ER_MODID
is not defined.NAME
- The name of the current module (file)MSG
- The message string...
- Any additional arguments for the message string
Note
Because this macro is used as a statement inside RCSW’s ER
machinery, it must end in ;
.
Optional plugin file contents (you can of course have whatever else you want in the file):
The name of the printf()
-like function which has the same signature;
used to define the PRINTF()
/ DPRINTF()
macros.
If omitted, you can’t use the PRINTF()
/ DPRINTF()
macros. If you want to omit it, #define
as nothing.
A framework initialization hook which RCSW will call in its internal
modules; should be idempotent. Can take any number of arguments of any
type. If it is not needed by your plugin, #define
as nothing.
A framework shutdown hook; should be idempotent. Can take any number of
arguments of any type. If it is not needed by your plugin #define
as
nothing.
Arguments:
ID
- The numeric UUID for the module.NAME
- The string UUID for the module.
Install/enable a module with the specified ID and name. If not needed by
your plugin, #define
as nothing.
Arguments:
ID
- The numeric UUID for the module.NAME
- The string UUID for the module.
Get a logger “handle” of some kind which contains the necessary
information to determine if a given module is enabled. For example, in the
LOG4CL plugin, the log4cl_mod_query()
function serves this
purpose.
If the module with the specified ID, NAME
is not enabled, then the
handle should be a false-y value, like 0 or NULL.
If not needed by your plugin #define
as nothing.
Arguments:
HANDLE
- The module handle returned byRCSW_ER_PLUGIN_HANDLE()
.LVL
- The level associated with the current reporting statement.
Given an active module HANDLE
, determine if the statement with the
specified LVL
should be emitted or not.
If not needed by your plugin, #define
as a truth-y value,
such as 1.