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:
Level | Function | Enum Value | Comment |
LEVEL_VERBOSE | CI_LOG_V | 0 | (lowest logging level) |
LEVEL_DEBUG | CI_LOG_D | 1 | |
LEVEL_INFO | CI_LOG_I | 2 | |
LEVEL_WARNING | CI_LOG_W | 3 | |
LEVEL_ERROR | CI_LOG_E | 4 | |
LEVEL_FATAL | CI_LOG_F | 5 | (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 callingLoggerBreakpoint::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
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 Level | Event Log Level |
LEVEL_VERBOSE | LOG_NOTICE |
LEVEL_DEBUG | LOG_NOTICE |
LEVEL_INFO | LOG_NOTICE |
LEVEL_WARNING | LOG_WARNING |
LEVEL_ERROR | LOG_ERR |
LEVEL_FATAL | LOG_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 Level | Event Log Level |
LEVEL_VERBOSE | EVENTLOG_INFORMATION_TYPE |
LEVEL_DEBUG | EVENTLOG_INFORMATION_TYPE |
LEVEL_INFO | EVENTLOG_INFORMATION_TYPE |
LEVEL_WARNING | EVENTLOG_WARNING_TYPE |
LEVEL_ERROR | EVENTLOG_ERROR_TYPE |
LEVEL_FATAL | EVENTLOG_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/Loggingtest/DebugTest