Flogger Logo

Flogger B.1


Anders Sundman

Copyright © 2006 Anders Sundman

Distributed under the GNU Lesser General Public License, Version 2.1. (See accompanying file LICENSE.txt or copy at http://www.gnu.org/licenses/lgpl.html)

Table of Contents


Introduction

Flogger is a general purpose C++ logging library. The primary design goals of the library are ease of use and ease of extendibility (e.g. custom log destinations or custom log levels).

A sane reaction to flogger is to ask the question: Does the world really need another logging library? The short answer is: yes. The slightly longer answer is: yes, because there are no other free, open source, platform independent, easy to use and flexible logging libraries out there.

Features

Here follows a brief overview of the features present in the flogger library.
  • Easy to get started with + plenty of examples.
  • Minimal typing required for vanilla usage.
  • Thread safe or not? You decide-two versions are available.
  • Logging in the calling thread or in a dedicated logging thread are both supported.
  • Platform independence. Flogger works on Windows, Mac OS X, Linux and more.
  • Many log writers available (console, file, etc.) and more are coming.
  • Source is free and open. It won't cost you a dime and you know what you get.
  • Context sensitive logging is supported (but not required).
  • Plugable log entry formatters selects how the log entries should be output.
  • Custom log levels are easy to add and use.

Building Flogger

To build the flogger library, the cross platform build system CMake is used. Please refer to http://www.cmake.org for documentation on how to use and install CMake.

Specify the flogger-dir/build directory as the target for an out of source build. The CMake source directory is flogger-dir/src

The "install" target builds all components and moves them to their proper place in the distribution tree. When finished, the flogger library should be created and installed in flogger-dir/lib and the examples should be installed in flogger-dir/bin.

Build Options

When building flogger with CMake, there are a number of options that can be set. These options determine the way the flogger library will operate.

FLOGGER_IMPL_BG This CMake option selects whether to build the library with support for foreground or background logging. If this option is set to ON, all logging will be performed by a dedicated logging thread. If it is OFF all logging is performed by the calling thread.
FLOGGER_RUNTIME_MT If this CMake option is set to ON, a thread safe implementation of the library will be build. If OFF, the library is not thread safe. (It is still safe to use background logging with FLOGGER_MT OFF).
BOOST_DECORATION The Boost library files will have different file names depending on what tool was used to build it. The files will be suffixed by "-[toolset]-[run time]-[arc]-etc.". Usually flogger can guess a good value for this, but it's not correct; please specify the string suitable for your version of the Boost library.

Platform Dependent Modules

Some components of the flogger library are only available on certain platforms.

The OutputDebugString writer and the example demonstrating it's use is only available on Windows and will only be build if the windows.h header file is found.

The optional process ID (PID) field in log entries is demonstrated in the Formatter example. The PID field is only available on Posix (Unix, Linux, Free BSD, Mac OS X, etc.) systems and on Windows systems.

Prerequisites

To build the flogger library, the cross platform build system CMake is used. Please refer to http://www.cmake.org for documentation on how to use and install CMake.

The flogger library depends on the Boost libraries smart_ptr, date_time and boost_thread (if creating a multi-threaded library). Please refer to http://www.boost.org for documentation on how to use and install the Boost libraries.

Using Flogger

A typical use of the library performs four basic steps.
  1. Create logger
  2. Register log writers
  3. Log messages
  4. Destroy logger
Steps 1 and 2 are done during program initialization, typically very early. Step 3 is performed repeatedly through out the entire execution of the application. Finally, step 4 is preformed at application termination.

Here is some sample code to demonstrate the four steps:
// Application startup, create a logger
logger_factory::create_logger();
shared_ptr<logger> lgr(logger_factory::get_logger());

// And register a log writer
shared_ptr<file_log_writer> flw(new file_log_writer("log.txt"), log_level::debug);
lgr->register_log_writer(flw);

// During program execution, log messages
log(log_level::debug, std::string("This is a debug log entry!"));

// At program termination, destroy logger
logger_factory::destroy_logger();
Log levels and log writers are two central concepts that are explained in greater detail in the following sections.

Log Level

The log level determines the severity of the log message. The pre-defined (in log_level.hpp) levels are: finest, debug, info, warning, error and fatal. Debug and info are messages that does not indicate an error; debug level is suitable for messages directed at developers and the info level for messages directed at the user. The fatal message is used for errors that will result in program termination.

It is straight forward to add additional levels by adding more log_level_t constants to the log_level namespace, e.g. to get a verbose_debug level. Please refer to the example Adding Log Levels for details on how to do this.

Every log writer has a minimum log level value and will not log any messages below that level. During development it is convenient to set this level to finest (to log all messages) and to raise the level (to info) in production code.

The following code will log both messages to the file, but only the warning message to the console:
// Create two log writers with different log levels shared_ptr<file_log_writer> flw(new file_log_writer("log.txt"), log_level::debug);
shared_ptr<ostream_log_writer> olw(new ostream_log_writer(log_level::info);

// Register log writers
lgr->register_log_writer(flw);
lgr->register_log_writer(olw);

// During program execution, log messages
log(log_level::debug, std::string("This is a debug message!"));
log(log_level::warning, std::string("This is a warning!"));

Log Writer

A log writer is a component that processes a log entry sink (e.g. writing it to a file). There are a number of different log writers that write log messages to files, console, etc. and several writers can be used at once.

Log writers are registered with a logger. The logger is the core logging engine. Once a log writer has been registered, the logger will see to that it get all log entries.

A nice thing is that different log writers can work with different log levels. Every writers has a minimum log level, and will only process log entries above that level. Typically, one will register a file log writer (file_log_writer) with a low minimum log level (eg. debug) to get detailed output to a file, and perhaps an output stream log writer (ostream_log_writer) with a higher minimum log level (eg. warning) to get less verbose output to an ostream (e.g. std::cerr).

Context Sensitive Logging

The basic idea is that the logger maintains a state (a context) in a stack. Each time a log entry is written the context is displayed together with the entry. New sub-contexts can be added or removed from the logger by pushing or popping them on and off the context stack.

The following snippet demonstrates the use of a context:
push_context("Network Comm");

// Write a log entry in the current 'Network Comm' context
log(log_level::error, "Socket is busy, can not connect");

pop_context();
The log writers can be configured to only process log entries that are generated from a specific context (or any of it's sub-contexts).

For example, it is possible to get the network communication, gui and application logic log entries in three different files. Simply use three contexts (e.g. "comm", "gui" & "sys") and three different log writers that listen to one context each.

The following code snippet creates a log writer that will only show log messages that were sent while the logger was in the "Network Comm" context.
// Context output must be explicitly enabled in the formatter
shared_ptr<string_formatter> formatter(new single_line_formatter(true, true, true));

// Create a log writer with the context "Network Comm"
shared_ptr<ostream_log_writer> olw_cout(new ostream_log_writer(std::cout, formatter));

// This enables the context filter for the writer
olw_cout->set_context("Network Comm");

Examples

The following sections presents a number of examples that demonstrates different uses of the flogger library.

A Minimal Example

File: src/examples/minimal
This is the absolute minimal example. Take a look at this first. It creates a logger, writes a log entry to the console and then destroys the logger.

Logging to a File

File: src/examples/logfile
This example will write log entries to files. Two file writers are created with different logging levels.

Adding Log Levels

File: src/examples/customlevel
Demonstrates how one can create a new (custom) logging level. A fine_debug level is created that is finer than the build in debug level.

Windows OutputDebugString

File: src/examples/outputdebugstring
Only available on W32 versions. This application is a minimal app that writes a log entry to the windows OputputDebugString. To see the result of this logging you need some kind of debugger, e.g. DebugView from SysInternals.

Formatters

File: src/examples/formatter
Formatters are used to format log entries in some of the common log writers (e.g. the ostream writer and the file writer). They determine how the log entries are output. This example demonstrates the use of the two built in formatters, the single line formatter and the multi line formatter.

Context Sensitive Logging

File: src/examples/context
This example demonstrates context sensitive logging.

Developing Flogger

This section contains information about the design of the flogger library. This information is useful when developing or extending the library.

This image shows how some of the core logger classes are related.

Logger Class Hierarchy

A note on the create_logger/destroy_logger design. The rationale for this design (instead of e.g. a singleton with static create/destroy) is that there might be resources needed by the logger that are not available during the static initialization and destruction (e.g. std::cerr). Without explicit creation/destruction there is no way to guarantee that the logger is created after the resource and destroyed before it.

Threading Issues

The default implementation of the log function is sequential, ie. it is the calling thread that also performs all the log writing. By defining the macro LOGGER_MULTI_THREADED_IMPL and recompiling the library, a multi threaded implementation of the logging is selected. This implementation uses a dedicated thread to write the log entries - the calling thread simply adds them to a queue. See logger_impl_fg.hpp and logger_impl_mt.hpp for the respective implementations.

Roadmap

Current version is B.1 (beta release). Release dates for future versions have not yet been set. But work is on the way of a 1.0 release.

Alpha release (versioned A.x), Project and infrastructure improvements
  • Fix bugs in CMake build script
  • Verify compilation on Windows XP (cygwin & Visual Studio), Mac OS X, Linux.
  • Setup project at sourceforge.net
  • Verify that homepage looks ok with Firefox, Explorer and Opera
  • Add homepage to flogger.sourceforge.net page
  • Update documentation to reflect alpha version
  • Release a src package
Beta release (versioned B.x), Context logging & Code maturity
  • Modify build script to build all versions of the library each time.
  • Develop context sensitive logging
  • Develop optional PID logging
  • Test & review of code
  • Update documentation to reflect beta version
  • Release a src package
Production release (versioned 1.x), Stability and field testing
  • Update CMake script to better reflect CMake >= 2.4
  • Thorough testing & Code review
  • Update documentation to reflect 1.x version
  • Release a src package
  • Release bin packages for W32 (cygwin, visual studio), *nix (gcc)
Future releases (versioned => 2.x), More log writers
  • Add Syslogd log writer (for *nix systems) + example
  • Add mySQL log writer + example
  • Add postgreSQL log writer + example
  • Add a network log writer (ACE or sockets?) + example
  • Release a src package
  • Release bin packages for W32 (cygwin, visual studio), *nix (gcc)
SourceForge.net Logo Valid XHTML 1.0 Transitional