NAV

The log library implements the logging system used by package in PID. Using this logging system is not mandatory and you can use another log facility if you want, but log helps in many ways:

  • homogeneous management of logs
  • easy configuration of log traces generation
  • allows to discriminate traces depending on libraries that generate them, wichi is a major feature for a PID based project with many dependencies.

Configuring your project

Basically, to use logging system of PID, a package needs to depend on the pid-log package. In root CMakeLists.txt of the package:

...
PID_Dependency(pid-log VERSION 3.1)
...

Then, let’s suppose you want a component to generate log traces you need to declare your component as loggable. In the CMakeLists.txt of the package, where the component is defined:

...
PID_Component(  my-component 
                ...
                LOGGABLE)
...

The LOGGABLE argument tells PID to generate a specific header file with pattern name <package>_<component>.h and that is located in the pid/log subfolder of your component public headers or sources (depending on your component has public headers or not).

If your component does not generate log traces by itself (i.e. its code is not using the log library API to write logs), there is no need to declare it as loggable. This is typically the case for an application whose generated traces are coming only from the libraries it uses. In this case your component may require to use the log library API to configure the logging system, so simply do this as a usual dependency to log library:

...
PID_Component(my-component DEPEND pid/log)
...

Now it is time to look inside API of pid-log to learn:

  • how to write log traces.
  • how to configure the logging system to get desired outputs.

Writing log traces

Log traces can be generated by any component using the pid-log API. Simply remember that only loggable components can be identified as specific emitting places, while non loggable components are considered as anonymous (and so their traces cannot be discriminated from those of other non loggable components).

To generate log traces a component needs to use a proxy. A proxy is automatically generated, configured and registered in logger using pid_log. pid_log is a macro that provides the adequate proxy object (either specific to a loggable component or anonymous) that is usable quite like a c++ stream:

Let’s suppose that a loggable component my-component needs to generate log traces:

...
#include <pid/log/my-package_my-component.h>
...
void my_function(){
	int error_number=0;
	...
	if(bad_situation){
		pid_log << pid::error << "error number: " << error_number << pid::align_on << pid::endl<<" one more line ..."<<pid::flush;
	}
	...
	if(strange_case_to_test > 0.0){
		pid_log << pid::debug << "bad situation: "<< strange_case_to_test << pid::flush;
	}
}

For a non loggable component the only difference is that you cannot use the header generated by the LOGGABLE property of the component so you simply have to use the default log library header:

...
#include <pid/log.h>
...
void my_function(){
	//same code

In both cases the pid_log proxy object is used to write log traces. As the example shows, you can use standard output formatters for specific types as well as other stream modifiers:

  • pid::endl adds a newline to the current log message but does not flush the output.
  • pid::flush flush the proxy. This will launch the message filtering process.
  • pid::info, pid::debug, pid::warning, pid::error and pid::critical are severity specifiers used to specify the nature of the new log message to generate. The flush of the proxy is automatic whenever the current severity specifier changes.
  • pid::align_on and pid::align_off are used respectively to activate and deactivate the automatic alignment of multi lines messages. Alignment consists in adding same padding spaces for all lines of the message.

Writing logs does not mean they will be finally print somewhere at runtime. Their printing is controlled by the logging system depending on its configuration.

Configuring generated outputs

The configuration of generated output is generally performed at application level (typically the main() function of your program).

The configuration of the logging system is accessed using the PID_LOGGER macro or equivalent pid::logger() function. Then you can use the functions provided by the logger object to configure the logging system. There are basically two approaches to achieve that:

  • configuring by using related logger object methods.
  • configuring by using logger configure method, which makes the configuration a bit less customizable but more simple and furthermore reusable. The configure method allows you to define a custom configuration of the logging system based on YAML configuration files. The log library provides a set of default configuration that you can use, and remaining explainations are based on those configuration files. Please read the package tutorial to know how to write such configuration files.

By default the logging system is configured to write all logs to standard output without any filtering, so if PID_LOGGER is used nowhere in the code everything will be outputed to standard ouput.

Otherwise you can use configuration files to configure the logging system, by calling the configure function:

#include <my/component.h>
#include <pid/log.h>

int main(int argc, char * argv[]){
	...
	pid::logger().configure("path to/name of a configuration file");
	...
}

log library is provided with default configurations files, that can be accessed through the pid_log runtime resource folder:

  • default_logs.yaml: this is the default configuration that prints everythong to standard output.
  • deactivated_logs.yaml: the logging system is deactivated so nothing will be printed. configuring the logging system with this file is the same as directly calling the disable() method on PID_LOGGER object.
  • all_problems_as_errors.yaml: report all potential problems like warning, errors and critical errors to standard output. Usefull for instance if you want to have an idea of what wrong happenned.
  • dev_full_logs.yaml: report all logs with full information about code sources that generate the logs.
  • only_debug_traces.yaml: report only debug logs, usefull to have a developper only vision of code execution, usefull for debugging purpose.
  • only_errors_logs.yaml: report only errors, mostly usefull for end users.

So, for instance, to deactivate all logs for a realtime execution without log generation:

#include <my/component.h>
#include <pid/log.h>

int main(int argc, char * argv[]){
	...
	pid::logger().configure("pid_log/deactivated_logs.yaml");
	//or PID_LOGGER.disable();
	...
}

And if we want to get only errors reported:

#include <my/component.h>//global header for my-component
#include <pid/log.h>

int main(int argc, char * argv[]){
    ...
	PID_LOGGER.configure("pid_log/only_errors_logs.yaml");
	...
}

Then at execution time, when my_function() is called we may have the following output:

[error] error number: 0
[error] error number: 1
...

And if we wanted only debug traces:

#include <my/component.h>//global header for my-component
#include <pid/log.h>

int main(int argc, char * argv[]){
    ...
	PID_LOGGER.configure("pid_log/only_debug_traces.yaml");
	...
}

Could then generate logs in standard output that look like:

[debug] bad situation: 0.987
[debug] bad situation: 0.987
[debug] bad situation: 456.752145
...

For an in depth understandaing and customization of log outputs, and particularly to learn how to discriminate log traces depending on their emitting place (component, pasckage or framework), please refer to the pid-log package tutorial.