Lomse library. API documentation  0.30.0
The logging system

Logging disabling and customization

Lomse has a logging system initially created as a help for developing the Lomse library but that can also be used by your application.

The logging system consists in two log streams: a normal log that should receive all log messages and a forensic log that was intended to be submitted, if the user agrees, to your server and that would contain only dumps and other debugging information for severe errors causing a crash. By default Lomse initializes the logging system to log messages in two files: lomse-log.txt and forensic_log.txt. These files are, by default, placed in the user home folder.

Applications using Lomse can customize the way Lomse does its logging, to disable it, to put the logs in an application-specific directory, or to redirect the logs to another destination, such as standard output/error streams (stdout/stderr) or a platform-specific logging system (such Android Logcat). For this, your application has two options. The first one is to specify the logging configuration when creating the LomseDoorway object, by providing custom std::ostream objects to be used by the logger. For example:

//e.g. use std::cout for normal log and std::cerr for forensic log
LomseDoorway m_lomse(&std::cout, &std::cerr);

The other option is to initialize the logging system before creating the LomseDoorway object, by calling lomse::get_global_logger().init() and providing custom std::ostream objects to be used by the logger, for example:

//e.g. use std::cout for normal log and std::cerr for forensic log
lomse::get_global_logger().init(&std::cout, &std::cerr);

If custom log streams are not provided, Lomse initializes the logging system for using the default logging configuration (the predefined normal and forensic log files):

//use default files lomse-log.txt and forensic_log.txt for logging
LomseDoorway m_lomse;

Your application can also disable logging by supplying an std::ostream which does nothing with its input. Lomse provides a default nullLogger stream for this. For instance:

//initialize lomse with logging disabled
LomseDoorway m_lomse(&lomse::nullLogger, &lomse::nullLogger);

At any moment, your application can reset the logging system to the default configuration in case the provided streams somehow get invalidated:

lomse::get_global_logger().deinit();

An can re-initialize again at any moment after de-initialization with any desired configuration:

//e.g. use std::cout for normal log and disable forensic log
lomse::get_global_logger().init(&std::cout, &lomse::nullLogger);

Logging methods

Lomse provides several macros for recording log messages, all of them having the same syntax as printf(): they take the format string as the first argument and a variable number of arguments. For instance:

LOMSE_LOG_INFO("key=%s, Path=%s", key.c_str(), fullpath.c_str());

The generated log message always includes the file, line and method where the message was generated, as well as a short tag (ERROR, INFO, etc.) for each message type, for example, the previous example generates the following log message:

lomse_linux.cpp, line 189. INFO: [lomse::FontSelector::find_font] key=anyBravura00, Path=/usr/local/share/fonts/Bravura.otf

Three macros are always usable and generate log messages in both debug and release builds:

  • LOMSE_LOG_ERROR(...) is the macro to use for error messages, e.g. messages that must be investigated as they suggest some kind of bug.
  • LOMSE_LOG_INFO(...) is for all normal, informational messages, that you would like to log for saving information that can be useful in case of having to investigate an error, e.g. configuration parameters.
  • LOMSE_LOG_WARN(...) is for warnings, you define what is it, e.g. informative messages that should be investigated.

Two additional macros have a parameter area before the message format string. This parameter allows to restrict the amount of messages generated by only logging those related to the area being debugged. This area parameter is explained in the next section. The two macros oriented to debugging code are:

  • LOMSE_LOG_DEBUG(area, ...) is the right macro for debug output.
  • LOMSE_LOG_TRACE(area, ...) is also for debug messages. The reason for making it a separate function from LOMSE_LOG_DEBUG is that usually debug messages are required only in very specific situations and are 'intrusive' (makes code less legible); therefore, these logging statements will probably be removed from the code as soon as it is debugged. But trace messages are normally less intrusive (e.g. just a line to record entering or exiting a method) and could be left in place to facilitate future debugging needs.
Attention
When using LOMSE_LOG_DEBUG and LOMSE_LOG_TRACE in your application they will only generate code if option LOMSE_ENABLE_DEBUG_LOGS was set in lomse_config.h when building the Lomse library. It is irrelevant if Lomse built was debug or release.

Logging areas and messages selection

As you develop your application code, it could contain many LOMSE_LOG_XXXX statements. Probably, as your application code is more mature and complete you will not need many of the logging statements but you could be reluctant to remove them as they can be of help in case of having to do some debugging in future. But the problem of maintaining them is that the log get flooded with messages and this makes more difficult and tedious to focus on the relevant messages for the current problem to debug.

To limit logging to only relevant messages without having to remove logging statements in other places, you may use Logger::set_logging_areas() and Logger::set_logging_mode() methods.

Logger::set_logging_mode() controls what messages will be generated. Currently there exist three modes:

  • In k_normal_mode only ERROR, INFO and WARN messages are logged.
  • In k_debug_mode also DEBUG messages are logged.
  • In k_trace_mode all messages are logged.

The default configuration is k_normal_mode so debug and trace messages will not be recorded.

When debug and/or trace messages are enabled, you can restrict theses messages to only those relevant to one or more code areas. An area is just an arbitrary identifier for the component, modules, or methods relevant to debug a functionality. For instance, for Lomse code there are currently six areas defined:

  • k_events, used for all logging statements relevant for debugging lomse events.
  • k_mvc, used for all messages related to the creation/destruction of the MVC model objects.
  • k_score_player, used for all messages related to score playback.
  • k_render, used for all messages related to rendering.
  • k_layout, used for all messages related to layout.
  • k_gmodel, used for all messages related to graphic model creation

In Lomse there are 16 areas reserved for your application, named k_user1 to k_user16. They do not have any predefined meaning and it is your application who define the purpose of each area.

If a LOMSE_LOG_DEBUG or LOMSE_LOG_TRACE macro is applicable to several areas you can specify all them by using the logical or operator, for instance:

LOMSE_LOG_TRACE(Logger::k_user5 | Logger::k_user7, "This is the value: %d", value);

Special area Logger::k_all can be used to make a message applicable to any area. It can also be used in Logger::set_logging_areas() for selecting all areas.

For using the logging system in your application, after initializing Lomse you need to include two logging configuration statements:

lomse::logger.set_logging_mode(Logger::k_trace_mode); //enable normal, debug and trace msgs.
lomse::logger.set_logging_areas(Logger::k_user2 | Logger::k_user7); //but only for areas user1 and user7
//AWARE: debug and trace messages will be recorded only when lomse is built with option LOMSE_ENABLE_DEBUG_LOGS

As you develop your code you will need to change these options. And when building the release version you will need to change the log mode to normal:

lomse::logger.set_logging_mode(Logger::k_normal_mode); //enable only error, info and warning messages