Use of spdlog log Library

Posted by ednark on Thu, 16 Dec 2021 12:56:22 +0100

1 spdlog first acquaintance

Today, we introduce an open source log library. You only need to copy the files under include to your own code directory, which can be used in the project. The use effect is shown in the following figure:

It is worth noting that the compiler needs to support C++11.

The above log output code is as follows:

#include "spdlog/spdlog.h"
#include "spdlog/fmt/fmt.h"
int main()
{
  spdlog::info("Welcome to spdlog!");
  spdlog::error("Some error message with arg: {}", 1);
  spdlog::warn("Easy padding in numbers like {:08d}", 12);
  spdlog::critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
  spdlog::info("Support for floats {:03.2f}", 1.23456);
  spdlog::info("Positional args are {1} {0}..", "too", "supported");
  spdlog::info("{:<30}", "left aligned");
  spdlog::set_level(spdlog::level::debug); // Set global log level to debug
  spdlog::debug("This message should be displayed..");
}

See here is not full of curiosity about spdlog. Spdlog is not only easy to use, diverse log output, but also very powerful.

Generally speaking, it has the following characteristics:

1. Fast performance

2. It is easy to use. You only need to include the header file

3. Rich formatting processing, using open source library FMT, address: https://github.com/fmtlib/fmt

4. Asynchronous mode, supporting asynchronous file writing

5. Custom log output format

6. Support multi-threaded log output

7. Set the log, such as log size, log generation frequency, system log and log color

8. The log output level takes effect immediately

9. Various log targets: cyclic output of log files; Log files can be generated daily; Support console log output (support color); System log; Windows debugger; It is easy to extend the custom log target;

10. You can load the log level through the program function entry or environment variable

11. When debugging, cache the log as needed and output it when needed

2 Introduction to spdlog

2.1 creating stdout/stderr logger objects

#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void stdout_example()
{
    // create color multi threaded logger
    auto console = spdlog::stdout_color_mt("console");    
    auto err_logger = spdlog::stderr_color_mt("stderr");    
    spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
}

2.2 basic document recorder

#include "spdlog/sinks/basic_file_sink.h"
void basic_logfile_example()
{
    try 
    {
        auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
    }
    catch (const spdlog::spdlog_ex &ex)
    {
        std::cout << "Log init failed: " << ex.what() << std::endl;
    }
}

2.3 setting file size

#include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{
    // Create a file rotating logger with 5mb size max and 3 rotated files
    auto max_size = 1048576 * 5;
    auto max_files = 3;
    auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
}

2.4 set the file generation frequency and generate by day

#include "spdlog/sinks/daily_file_sink.h"
void daily_example()
{
    // Create a daily logger - a new file is created every day on 2:30am
    auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}

2.5 log backtracking

The debug log can be temporarily saved in memory. When necessary, the cached log can be output through the interface. The log cache and the number of output log records can be set through parameters. The following code settings save the last 32 log messages.

spdlog::enable_backtrace(32); 
for(int i = 0; i < 100; i++)
{
  spdlog::debug("Backtrace message {}", i);
}
// e.g. if some error happened:
spdlog::dump_backtrace(); // log them now! show the last 32 messages
// or my_logger->dump_backtrace(32)..

2.6 output log by cycle

The following code implements log output every 3 seconds, but at the same time, pay attention to ensure that the log object is thread safe.

spdlog::flush_every(std::chrono::seconds(3));

2.7 output log by cycle

Print lifecycle time.

#include "spdlog/stopwatch.h"
void stopwatch_example()
{
    spdlog::stopwatch sw;    
    spdlog::debug("Elapsed {}", sw);
    spdlog::debug("Elapsed {:.3}", sw);       
}

2.8 save binary data in hexadecimal

#include "spdlog/fmt/bin_to_hex.h"
void binary_example()
{
    auto console = spdlog::get("console");
    std::array<char, 80> buf;
    console->info("Binary example: {}", spdlog::to_hex(buf));
    console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
}

In addition, you can also output the string size, etc.

The functions are as follows:

{: X} - convert to uppercase output

{: s} - do not separate each byte with a space in hexadecimal

{: p} - do not print at the beginning of each line

{: n} - do not split the output log into multiple rows

{: a} - if: n is not set, ASCII code is displayed

2.9 support multiple log output devices

The following code supports two types. The console log outputs alarms and errors to the console and outputs all log levels to the file.

void multi_sink_example()
{
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::warn);
    console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");

    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
    file_sink->set_level(spdlog::level::trace);

    spdlog::logger logger("multi_sink", {console_sink, file_sink});
    logger.set_level(spdlog::level::debug);
    logger.warn("this should appear in both console and file");
    logger.info("this message should not appear in the console, only in the file");
}

2.10 asynchronous log

#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
void async_example()
{
    auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
}

2.11 asynchronous logs supporting multiple loggers

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"

void multi_sink_example2()
{
    spdlog::init_thread_pool(8192, 1);
    auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt >();
    auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("mylog.txt", 1024*1024*10, 3);
    std::vector<spdlog::sink_ptr> sinks {stdout_sink, rotating_sink};
    auto logger = std::make_shared<spdlog::async_logger>("loggername", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
    spdlog::register_logger(logger);
}

2.12 custom types

#include "spdlog/fmt/ostr.h" // must be included
struct my_type
{
    int i;
    template<typename OStream>
    friend OStream &operator<<(OStream &os, const my_type &c)
    {
        return os << "[my_type i=" << c.i << "]";
    }
};

void user_defined_example()
{
    spdlog::get("console")->info("user defined type: {}", my_type{14});
}

2.13 user defined flags in log mode

#include "spdlog/pattern_formatter.h"
class my_formatter_flag : public spdlog::custom_flag_formatter
{
public:
    void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
{
        std::string some_txt = "custom-flag";
        dest.append(some_txt.data(), some_txt.data() + some_txt.size());
    }

    std::unique_ptr<custom_flag_formatter> clone() const override
    {
        return spdlog::details::make_unique<my_formatter_flag>();
    }
};

void custom_flags_example()
{    
    auto formatter = std::make_unique<spdlog::pattern_formatter>();
    formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
    spdlog::set_formatter(std::move(formatter));
}

The above code implements a user-defined type:%*

2.14 custom error handle

void err_handler_example()
{
    // can be set globally or per logger(logger->set_error_handler(..))
    spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); });
    spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
}

2.15 syslog

#include "spdlog/sinks/syslog_sink.h"
void syslog_example()
{
    std::string ident = "spdlog-example";
    auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID);
    syslog_logger->warn("This is warning that will end up in syslog.");
}

2.16 loading log parameters from parameters and environment variables

#include "spdlog/cfg/env.h"
#include "spdlog/cfg/argv.h"
int main (int argc, char *argv[])
{
    spdlog::cfg::load_env_levels();
    spdlog::cfg::load_argv_levels(argc, argv);
}

The following operations can be performed during program execution:

$ export SPDLOG_LEVEL=info,mylogger=trace
$ ./example

2.17 opening and closing log handles

void file_events_example()
{
    handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); };
    handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); };
    handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); };
    handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); };
    auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers);        
}

2.18 replacing the default logger

void replace_default_logger_example()
{
    auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true);
    spdlog::set_default_logger(new_logger);
    spdlog::info("new logger log message");
}

3 Summary

At present, the version of spdlog is v1.0 x. It can be obtained through the following link:

https://github.com/gabime/spdlog

The spdlog library supports the following systems:

  • User defined Linux, FreeBSD, OpenBSD, Solaris, AIX
  • Windows (msvc 2013+, cygwin)
  • MacOS (Jingle 3.5 +)
  • Androidflags in log mode

4 reference

Link:

1,https://github.com/gabime/spdlog