1 spdlog初识
今天介绍一个开源日志库,只需要将include下面的文件拷贝到自己的代码目录下,就可以在项目中使用。使用效果如下图所示:
值得注意的是,使用时编译器需要支持C++11。
上面日志输出的代码如下:
#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..");
}
看到这里是不是对spdlog充满了好奇。spdlog不仅使用方便,日志输出多样,且功能非常强大。
总体来说具有以下特点:
1、性能快
2、使用简单,只需要包含头文件即可
3、丰富的格式化处理,采用开源库fmt,地址:https://github.com/fmtlib/fmt
4、异步模式,支持异步写文件
5、自定义日志输出格式
6、支持多线程日志输出
7、对日志进行设置,如:日志大小、生成日志频率、系统日志、日志颜色设置
8、日志输出级别即时生效
9、各种日志目标:可对日志文件进行循环输出;可每日生成日志文件;支持控制台日志输出(支持颜色);系统日志;Windows debugger;较容易扩展自定义日志目标;
10、可以通过程序函数入口或者环境变量加载日志级别
11、调试时根据需要对日志进行缓存,并在需要的时候进行输出
2 spdlog使用介绍
2.1 创建stdout/stderr记录器对象
#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 基本文件记录器
#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 设置文件大小
#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 设置文件生成频率,按日生成
#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 日志回溯
调试日志可以暂时保存在内存中,需要的时候可以通过接口对缓存的日志进行输出,通过参数可以设置日志缓存和输出的日志记录数。下面的代码设置保存最近的32条日志消息。
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 按照周期输出日志
下面的代码实现了每3秒进行一次日志输出,但同时也要注意,使用时确保日志对象是线程安全的。
spdlog::flush_every(std::chrono::seconds(3));
2.7 按照周期输出日志
打印生命周期时间。
#include "spdlog/stopwatch.h"
void stopwatch_example()
{
spdlog::stopwatch sw;
spdlog::debug("Elapsed {}", sw);
spdlog::debug("Elapsed {:.3}", sw);
}
2.8 用16进制保存二进制数据
#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));
}
除此之外,还可以将字符串进行大小输出等。
功能如下:
{:X} - 转换为大写输出.
{:s} - 在十六进制中不要用空格分隔每个字节
{:p} - 不要在每行开始打印位置
{:n} - 不要将输出日志拆分为多行
{:a} - 如果没有设置:n,则显示ASCII码.
2.9 支持多种日志输出器
下面的代码支持两种,console日志将告警和错误输出到控制台,向文件中输出所有日志级别。
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 异步日志
#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 支持多个日志记录器的异步日志
#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 自定义类型
#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 用户在日志模式中定义标志
#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));
}
上面的代码实现了一个用户自定义的类型:%*
2.14 自定义错误句柄
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 从参数和环境变量中加载日志参数
#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);
}
程序执行时可以操作如下:
$ export SPDLOG_LEVEL=info,mylogger=trace
$ ./example
2.17 打开和关闭日志句柄
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 替换默认的日志记录器
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 总结
目前,spdlog的版本为v1.x,可以通过下面链接获取:
https://github.com/gabime/spdlog
spdlog库支持已经系统:
4 参考
链接:
1、https://github.com/gabime/spdlog