Logging in Cinder

This document provides an overview of the logging capabilities in Cinder. You can use this, along with the sample in samples/Logging, to quickly begin utilizing Cinder's built in logging capabilities.

Overview

Cinder logging resides in the ci::log namespace, and provides a set of tools for straight forward, and relatively flexible logging. The purpose of Cinder logging is not to circumvent advanced logging packages, but rather to provide Cinder users with basic, integrated functionality that will cover most practical use cases.

All Cinder apps start with the default LoggerConsole enabled, meaning any Cinder logging calls will appear in the application console window. You can confirm this by making a logging call in a new Cinder app. The example code:

CI_LOG_D( "Hello world!" );
Would produce the following trace in the application console:
|debug  | virtual void TestApp::setup()[40] Hello world!

Using concise, but flexible calls, a user can enable any number of additional loggers. Below is an example of how you would log to a file by adding an instance of LoggerFile to your Cinder project:

log::makeLogger<log::LoggerFile>( "/tmp/cinder/cinder.log" );

Before getting deeper into the features in ci::log, let's review some high level information.

Log Levels

ci::log::Level defines standard logging levels. Opinions vary on the exact use of each logging level, so this is left up to the developer. What is important to understand is that LEVEL_VERBOSE is considered the lowest logging level, and LEVEL_FATAL the highest. This is important when understanding how to configure logging levels in Cinder. The supported logging levels and their corresonding functions are below:

LevelFunctionEnum ValueComment
LEVEL_VERBOSECI_LOG_V0(lowest logging level)
LEVEL_DEBUGCI_LOG_D1
LEVEL_INFOCI_LOG_I2
LEVEL_WARNINGCI_LOG_W3
LEVEL_ERRORCI_LOG_E4
LEVEL_FATALCI_LOG_F5(highest logging level)

Logger Types

  • Logger is the base class for all loggers. This class is never directly used.
  • LoggerConsole directs log messages into the application console window. This logger is enabled by default. This logger takes no parameters.
  • LoggerFile directs log messages to a specified file.
  • LoggerFileRotating directs log messages to a specified file. The name of that file is evaluated at the first logging call after application startup, and re-evaluated at the first logging call after midnight.
  • LoggerBreakpoint doesn't actually log messages, but triggers a breakpoint on log events above a specified threshold. triggerLevel defines the minimum logging level that will break execution.
    The trigger level can be controlled by calling LoggerBreakpoint::setTriggerLevel().
  • LoggerSystem will write log messages to the platform specific system log. This currently means the system log on OS X /var/log/system.log and the Event Log on Windows with MSW applications. WinRT system logging is still being developed.

LoggerSystem Notes

Minimum System Logging Level

The minimum system logging level can be controlled by calling LoggerSystem::setLoggingLevel(). This level can be controlled independently of the Cinder minimum logging level, but the system logging level can never be lower than the Cinder minimum logging level. For more information on this, see the section on Compile Time Settings & Optimization.

OS X Specific Notes

Note: By default, logging messages that are lower than LOG_NOTICE do not show up in the system log. This can be adjusted by configuring the /etc/syslog.conf file, but in order for OS X system logging to work out of the box, Cinder logging levels are never mapped to a logging level lower than LOG_NOTICE. The full mapping is shown below. The log messages themselves will contain the Cinder logging level.

Cinder LevelEvent Log Level
LEVEL_VERBOSELOG_NOTICE
LEVEL_DEBUGLOG_NOTICE
LEVEL_INFOLOG_NOTICE
LEVEL_WARNINGLOG_WARNING
LEVEL_ERRORLOG_ERR
LEVEL_FATALLOG_CRIT

Windows Specific Notes

Note: On Windows, there is no direct conversion of Cinder logging levels to Event Log logging levels. Event levels are mapped as follows. The log messages themselves will contain the Cinder logging level.

Cinder LevelEvent Log Level
LEVEL_VERBOSEEVENTLOG_INFORMATION_TYPE
LEVEL_DEBUGEVENTLOG_INFORMATION_TYPE
LEVEL_INFOEVENTLOG_INFORMATION_TYPE
LEVEL_WARNINGEVENTLOG_WARNING_TYPE
LEVEL_ERROREVENTLOG_ERROR_TYPE
LEVEL_FATALEVENTLOG_ERROR_TYPE

Note: For deployments on Windows systems, you may find that you need to create and register an application manifest file. We are exploring how this may be integrated into the Cinder workflow, but in the mean time, please reference issue #1109 for information.

Logger Management

Multiple logger types aren't much use if you can't easily create, enable, and disable loggers. The ci::log::LogManager manages a stack of all active loggers, and provides functions to aid in the management of loggers.

Usage

The LogManager allows you to add individual logs to the stack of active loggers. You can either keep track of loggers after you've added them to the stack, or you can search for the logger at a later point in time.

Example 1: Adding and forgetting a LoggerFile:

log::makeLogger<log::LoggerFile>( "/tmp/logging/cinder.log", true );
This causes a ci::log::LoggerFile to be created and added to the logger stack.

Example 2: Adding and keeping a ci::log::LoggerRef:

log::LoggerRef logger = log::makeLogger<log::LoggerFile>( "/tmp/logging/cinder.log", true );
... log some things to the console and file
// remove the logger
log::manager()->removeLogger( logger );
... log some things to the console only

Example 3: Getting an active logger:

// gets the active LoggerSystem and sets the minimum level to LEVEL_ERROR
log::manager()->getLoggers<log::LoggerSystem>()[0]->setLoggingLevel( log::LEVEL_ERROR );

Compile Time Settings & Optimization

The CI_MIN_LOG_LEVEL directive allows you to configure the minimum logging level that is enabled. For example, if you you included the following code in your main application:

#define CI_MIN_LOG_LEVEL 4
#include "cinder/Log.h"
Then the lowest enabled logging level would be LEVEL_ERROR. Calls to lower logging levels, such as CI_LOG_D would incur no runtime costs because the method is optimized away at compile time.

By default, when compiling your application in debug mode, all logging levels are supported.

By default, when compiling your application in release mode, all levels including and above LEVEL_INFO are supported.

When you set CI_MIN_LOG_LEVEL these default settings are overwritten.
Note: this is why the minimum system logging level can never be lower than the currently enabled Cinder logging level.

Advanced Usage

Sticking with the Cinder mantra "make easy things easy, and hard things possible", Cinder allows for the insertion of custom loggers. For example, if I felt like creating a logger that prepended text to my logging string, I could use the following code:

class SpecialLogger: public log::LoggerConsole
{
    virtual void write( const log::Metadata &meta;, const std::string &text; ) override
    {
        LoggerConsole::write( meta, "Special: " + text );
    }
};
...
log::makeLogger<SpecialLogger>();
CI_LOG_D( "nothing special.");
Which would print the following to the console output:
|debug  | virtual void LoggingApp::setup()[51] Special: nothing special.

Limitations

Message Formatting

Currently you can not change the message format.

File Rotating Interval

Currently the ci::log::LoggerFileRotating will only rotate log files during the first logging event after midnight. This is not configurable to a different interval period.

Example Projects

samples/Logging
test/DebugTest