Skip to content

Multi-level logger for C++. Supports logging to both console and file.

License

Notifications You must be signed in to change notification settings

lukaswagner/logger

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

94 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

logger

Multi-level logger for C++. Supports logging to both console and file. Based on logger from cginternals/cppassist. The documentation can be found here.

Contents

  1. Setup
    1. Basic Dependencies
    2. Dependency Setup
    3. Building the Project
  2. Usage
    1. Enabling the Logger
    2. Creating Output
    3. Further Customization
    4. Debug Macros
    5. Complete Example

Setup

Basic Dependencies

You'll need

  • CMake to generate build files.
  • A C++14-compatible compiler, e.g. g++7 or VS 2017.
  • Git to automatically clone googletest if you want to build the tests.
  • Doxygen if you want to generate the documentation.

Dependency Setup

tl;dr:

cd dependencies
cmake .

The tests use the googletest framework, so you'll have to download and build this if you want to build the tests. In order to do so, just run cmake . in the dependecy subdirectory. This will build googletest with the default generator. Hint for VS users: By default, a 32 bit compiler is used. You should specify the correct generator, for example by calling cmake . -G "Visual Studio 15 2017 Win64".

If you already have a compiled version of googletest lying around, you can skip this and just specify the installation's base dir by passing -DGTEST_ROOT="path/to/gtest" when configuring the logger.

Building the Project

tl;dr:

mkdir build
cd build
cmake .. -DBUILD_EXAMPLE=ON -DBUILD_TESTS=ON -DBUILD_DOCS=ON

Create a build dir and configure the project using CMake. There are three options to enable optional targets:

  • BUILD_EXAMPLE: Builds an example application using the logger.
  • BUILD_TESTS: Builds the tests checking the logger's functionality. Requires googletest.
  • BUILD_DOCS: Generates the documentation. Requires Doxygen.

Afterwards, you can build the project. Just open the .sln in Visual Studio and build it from there, or use make if you're on Unix/Linux. Note: The docs are not included in the default target, you'll have to explicitly build them.

Usage

Enabling the Logger

Enable the logger by calling enable() with the appropriate levels. Let's say you want to show only the important logs in the console to maintain a cleaner output, but write all messages to a log file in case you need to debug your application:

logger::enable(logger::Info, logger::Verbose);

Only messages with the level Info or higher will be visible in the console, while everything (Verbose is the lowest level) will be written to the log file when setting up the logger like this.

Creating Output

Use the functions resembling the log levels to obtain a line object. You can use this to pipe content as if it were a std::ostream.

logger::info() << "a string: " << anInt;

Once the line object gets destroyed, all content piped into it is concatenated with the message header and sent to the outputs.

Note: Message with the levels Verbose, Debug and Info are sent to stdout, while message with the levels Warning and Error are sent to stderr.

Since the << operator is implemented using a template, you can print everything you can send to an std::ostream. If you provide your own std::ostream& operator<< overload, you can even print your custom classes (see the Complete Example for an example).

Additionally, you can pass a context to the log functions. You can use this to provide a better structured output. For example, calling

logger::info("Main setup") << "Reading config file..."

results in the following output:

[Inf] [19:29:34] Main setup: Reading config file...

Further Customization

By default, contexts are fixed to 10 characters, resulting in the output being aligned cleanly. In order to change this number of characters according to your needs, you can use setContextLength():

logger::info("A very long context") << "It's cut off.";
logger::setContextLength(20);
logger::info("A very long context") << "Now it fits."

Output:

[Inf] [19:37:10] A very lon: It's cut off.
[Inf] [19:37:10] A very long context : Now it fits.

You can customize the time output by using setTimeFormat() (see std::put_time for the used syntax) or completely hide it by using setPrintTime():

logger::info("Time test") << "Default time format is %H:%M:%S.";
logger::setTimeFormat("%H:%M");
logger::info("Time test") << "Now the seconds are hidden.";
logger::setPrintTime(false);
logger::info("Time test") << "And now the time is omitted.";

Output:

[Inf] [19:47:26] Time test : Default time format is %H:%M:%S.
[Inf] [19:47] Time test : Now the seconds are hidden.
[Inf] Time test : And now the time is omitted.

The same time syntax is used for naming the generated log file. By default, log/%Y-%m-%d-%H-%M-%S.txt is used - resulting in a file like 2018-08-11-20-00-10.txt being created in the subdirectory log. You can customize this by passing a string containing the desired format to enable().

Debug Macros

The proper way of debugging a program is by using a debugger. But we both know you don't do this, and instead add a log to every line of code to track the progress. Don't despair, this logger comes with macros to encourage this bad behaviour. These are defined in macros.hpp.

DEBUG_EXPR can be used to log an expression before running it. Example usage:

DEBUG_EXPR(std::cout << "test" << std::endl;)

Output:

[Dbg] [14:30:52] DEBUG_EXPR     : std::cout << "test" << std::endl; (c:\users\lukas\documents\codekram\cpp\logger\src\example.cpp:56)
test

DEBUG_VAL can be used to debug a value. This evaluates a variable or exression, and logs both the expression and its value. Example usage:

DEBUG_VAL(a)
DEBUG_VAL(a * a)

Output:

[Dbg] [14:30:52] DEBUG_VAL      : a = 3
[Dbg] [14:30:52] DEBUG_VAL      : a * a = 9

Complete Example

This example is taken from the example application.

#include "logger.hpp"

// create a custom class to showcase operator overloading
class CustomClass
{
public:
    int m_memberVar;
};

// implement an operator for piping the class to an ostream - this will allow usage with the logger as well
std::ostream& operator<<(std::ostream& stream, const CustomClass& object)
{
    return stream << "The custom object contains the value " << object.m_memberVar;
}

int main(int argc, char* argv[])
{
    // enable the logger, only show messages with level >= Info and disable file output completely
    logger::enable(logger::Info, logger::Off);

    // let's disable the time output during the setup
    logger::setPrintTime(false);

    // we'll be using a long context name - let's increase the context length
    logger::setContextLength(15);

    // this will be shown
    logger::info("Setup") << "Loading config...";

    // load some file, iterate over inputs, whatever - let's pretend this block is called in a loop
    {
        const auto someIntInConfig = 17;
        // this will be hidden in output, since Debug < Info
        logger::debug("Setup") << "Found value " << someIntInConfig << " in config.";
    }

    // this will be shown again
    logger::info("Setup") << "Successfully loaded config.";

    // now we're re-enabling time output
    logger::setPrintTime(true);

    // by parsing some input we created this custom object
    const auto customObject = CustomClass{ 1338 };

    // warnings and errors are redirected to cerr instead of cout
    if (customObject.m_memberVar > 1337)
        logger::warning("Input parsing") << "Invalid input - " << customObject;

    // enable logging of debug messages
    logger::enable(logger::Debug);

    // log and execute line
    DEBUG_EXPR(auto a = 3;)

    // log value name and evaluation result
    DEBUG_VAL(a)
    DEBUG_VAL(a * a)
}

stdout output:

[Inf] Setup          : Loading config...
[Inf] Setup          : Successfully loaded config.
[Dbg] [19:25:48] DEBUG_EXPR     : auto a = 3; (c:\users\lukas\documents\codekram\cpp\logger\src\example.cpp:55)
[Dbg] [19:25:48] DEBUG_VAL      : a = 3
[Dbg] [19:25:48] DEBUG_VAL      : a * a = 9

stderr output:

[Wrn] [19:25:48] Input parsing  : Invalid input - The custom object contains the value 1338

About

Multi-level logger for C++. Supports logging to both console and file.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published