1.介绍
spdlog
是一个高性能、超快速、零配置的C++日志库,它旨在提供简洁的API和丰富的功能,同时保持高性能的日志记录。它支持多种输出目标、格式化选项、线程安全以及异步日志记录
Github
特点 :
高性能 :spdlog
专为速度而设计,即使在高负载情况下也能保持良好的性能
零配置 :无需复杂的配置,只需包含头文件即可在项目中使用
异步日志 :支持异步日志记录,减少对主线程的影响
格式化 :支持自定义日志消息的格式化,包括时间戳、线程ID、日志级别等
多平台 :跨平台兼容,支持Windows、Linux、MacOS等操作系统
丰富的API :提供丰富的日志级别和操作符重载,方便记录各种类型的日志
2.安装
直接命令安装 :sudo apt install libspdlog-dev
源码安装 :clone
后,使用cmake
安装即可,详细可参照官方文档
3.使用
1.包含头文件
# include <spdlog/spdlog.h>
2.日志输出等级枚举
namespace level
{
enum level_enum : int
{
trace = SPDLOG_LEVEL_TRACE,
debug = SPDLOG_LEVEL_DEBUG,
info = SPDLOG_LEVEL_INFO,
warn = SPDLOG_LEVEL_WARN,
err = SPDLOG_LEVEL_ERROR,
critical = SPDLOG_LEVEL_CRITICAL,
off = SPDLOG_LEVEL_OFF,
n_levels
} ;
}
3.日志输出格式自定义
可以自定义日志消息的格式logger-> set_pattern ( "%Y-%m-%d %H:%M:%S [%t] [%-7l] %v" ) ;
% t - 线程ID(Thread ID)
% n - 日志器名称(Logger name)
% l - 日志级别名称(Level name),如 INFO, DEBUG, ERROR 等
% v - 日志内容(message)
% Y - 年(Year)
% m - 月(Month)
% d - 日(Day)
% H - 小时(24 - hour format)
% M - 分钟(Minute)
% S - 秒(Second)
4.日志记录器类
创建一个基本的日志记录器,并设置日志级别和输出模式namespace spdlog
{
class logger
{
logger ( std:: string name) ;
logger ( std:: string name, sink_ptr single_sink)
logger ( std:: string name, sinks_init_list sinks)
void set_level ( level:: level_enum log_level) ;
void set_formatter ( std:: unique_ptr< formatter> f) ;
template < typename . . . Args>
void trace ( fmt:: format_string< Args. . . > fmt, Args && . . . args)
template < typename . . . Args>
void debug ( fmt:: format_string< Args. . . > fmt, Args && . . . args)
template < typename . . . Args>
void info ( fmt:: format_string< Args. . . > fmt, Args && . . . args)
template < typename . . . Args>
void warn ( fmt:: format_string< Args. . . > fmt, Args && . . . args)
template < typename . . . Args>
void error ( fmt:: format_string< Args. . . > fmt, Args && . . . args)
template < typename . . . Args>
void critical ( fmt:: format_string< Args. . . > fmt, Args && . . . args)
void flush ( ) ;
void flush_on ( level:: level_enum log_level) ;
} ;
}
5.异步日志记录类
为了异步记录日志,可以使用spdlog::async::logger
class async_logger final : public logger
{
async_logger ( std:: string logger_name,
sinks_init_list sinks_list,
std:: weak_ptr< details:: thread_pool> tp,
async_overflow_policy overflow_policy =
async_overflow_policy:: block) ;
async_logger ( std:: string logger_name,
sink_ptr single_sink,
std:: weak_ptr< details:: thread_pool> tp,
async_overflow_policy overflow_policy =
async_overflow_policy:: block) ;
class SPDLOG_API thread_pool
{
thread_pool ( size_t q_max_items,
size_t threads_n,
std:: function< void ( ) > on_thread_start,
std:: function< void ( ) > on_thread_stop) ;
thread_pool ( size_t q_max_items, size_t threads_n,
std:: function< void ( ) > on_thread_start) ;
thread_pool ( size_t q_max_items, size_t threads_n) ;
} ;
}
std:: shared_ptr< spdlog:: details:: thread_pool> thread_pool ( )
{
return details:: registry:: instance ( ) . get_tp ( ) ;
}
inline void init_thread_pool ( size_t q_size, size_t thread_count)
auto async_logger = spdlog:: async_logger_mt ( "async_logger" ,
"logs/async_log.txt" ) ;
async_logger-> info ( "This is an asynchronous info message" ) ;
6.日志记录器工厂类
using async_factory = async_factory_impl< async_overflow_policy:: block> ;
template < typename Sink , typename . . . SinkArgs>
inline std:: shared_ptr< spdlog:: logger> create_async ( std:: string logger_name,
SinkArgs && . . . sink_args)
template < typename Factory = spdlog:: synchronous_factory>
std:: shared_ptr< logger> stdout_color_mt ( const std:: string & logger_name,
color_mode mode = color_mode:: automatic) ;
template < typename Factory = spdlog:: synchronous_factory>
std:: shared_ptr< logger> stderr_color_mt ( const std:: string & logger_name,
color_mode mode = color_mode:: automatic) ;
template < typename Factory = spdlog:: synchronous_factory>
std:: shared_ptr< logger> basic_logger_mt ( const std:: string & logger_name,
const filename_t & filename,
bool truncate = false ,
const file_event_handlers & event_handlers = {
} )
template < typename Factory = spdlog:: synchronous_factory>
std:: shared_ptr< logger> rotating_logger_mt ( const std:: string & logger_name,
const filename_t & filename,
size_t max_file_size,
size_t max_files,
bool rotate_on_open = false )
. . .
7.日志落地类
namespace spdlog
{
namespace sinks
{
class SPDLOG_API sink
{
public :
virtual ~ sink ( ) = default ;
virtual void log ( const details:: log_msg & msg) = 0 ;
virtual void flush ( ) = 0 ;
virtual void set_pattern ( const std:: string & pattern) = 0 ;
virtual void set_formatter ( std:: unique_ptr< spdlog:: formatter> sink_formatter) = 0 ;
void set_level ( level:: level_enum log_level) ;
} ;
using stdout_sink_mt;
using stderr_sink_mt;
using stdout_color_sink_mt;
using stderr_color_sink_mt;
sink_ptr rotating_file_sink ( filename_t base_filename,
std:: size_t max_size,
std:: size_t max_files,
bool rotate_on_open = false ,
const file_event_handlers & event_handlers = {
} ) ;
using rotating_file_sink_mt = rotating_file_sink< std:: mutex> ;
sink_ptr basic_file_sink ( const filename_t & filename,
bool truncate = false ,
const file_event_handlers & event_handlers = {
} ) ;
using basic_file_sink_mt = basic_file_sink< std:: mutex> ;
using kafka_sink_mt = kafka_sink< std:: mutex> ;
using mongo_sink_mt = mongo_sink< std:: mutex> ;
using tcp_sink_mt = tcp_sink< std:: mutex> ;
using udp_sink_mt = udp_sink< std:: mutex> ;
. . . . .
}
}
8.全局接口
void set_level ( level:: level_enum log_level) ;
void flush_every ( std:: chrono:: seconds interval)
void flush_on ( level:: level_enum log_level) ;
9.记录日志
4.使用示例
0.makefile
all:sync_stdout sync_file async
sync_stdout:sync_stdout.cc
g++ -o $@ $^ -std=c++17 -lspdlog -lfmt
sync_file:sync_file.cc
g++ -o $@ $^ -std=c++17 -lspdlog -lfmt
async:async.cc
g++ -o $@ $^ -std=c++17 -lspdlog -lfmt
.PHONY:clean
clean:
rm sync_stdout
rm sync_file
1.同步日志并输出到标准输出
main.cc
:# include <iostream>
# include <spdlog/spdlog.h>
# include <spdlog/sinks/stdout_color_sinks.h>
int main ( )
{
spdlog:: flush_every ( std:: chrono:: seconds ( 1 ) ) ;
spdlog:: flush_on ( spdlog:: level:: level_enum:: debug) ;
spdlog:: set_level ( spdlog:: level:: level_enum:: debug) ;
auto logger = spdlog:: stdout_color_mt ( "default-logger" ) ;
logger-> set_pattern ( "[%H:%M:%S][%t][%-8l] %v" ) ;
logger-> trace ( "Hello {}" , "SnowK" ) ;
logger-> debug ( "Hello {}" , "SnowK" ) ;
logger-> info ( "Hello {}" , "SnowK" ) ;
logger-> warn ( "Hello {}" , "SnowK" ) ;
logger-> error ( "Hello {}" , "SnowK" ) ;
logger-> critical ( "Hello {}" , "SnowK" ) ;
std:: cout << "spdlog 日志输出完毕" << std:: endl;
return 0 ;
}
输出结果 :[15:26:28][27831][debug ] Hello SnowK
[15:26:28][27831][info ] Hello SnowK
[15:26:28][27831][warning ] Hello SnowK
[15:26:28][27831][error ] Hello SnowK
[15:26:28][27831][critical] Hello SnowK
spdlog 日志输出完毕
2.同步日志并输出到文件
main.cc
:# include <iostream>
# include <spdlog/spdlog.h>
# include <spdlog/sinks/basic_file_sink.h>
int main ( )
{
spdlog:: flush_every ( std:: chrono:: seconds ( 1 ) ) ;
spdlog:: flush_on ( spdlog:: level:: level_enum:: debug) ;
spdlog:: set_level ( spdlog:: level:: level_enum:: debug) ;
auto logger = spdlog:: basic_logger_mt ( "file_logger" , "log" ) ;
logger-> set_pattern ( "[%H:%M:%S][%t][%-8l] %v" ) ;
logger-> trace ( "Hello {}" , "SnowK" ) ;
logger-> debug ( "Hello {}" , "SnowK" ) ;
logger-> info ( "Hello {}" , "SnowK" ) ;
logger-> warn ( "Hello {}" , "SnowK" ) ;
logger-> error ( "Hello {}" , "SnowK" ) ;
logger-> critical ( "Hello {}" , "SnowK" ) ;
std:: cout << "spdlog 日志输出完毕" << std:: endl;
return 0 ;
}
输出结果 :# stdout
spdlog 日志输出完毕
# log文件
[15:34:45][28382][debug ] Hello SnowK
[15:34:45][28382][info ] Hello SnowK
[15:34:45][28382][warning ] Hello SnowK
[15:34:45][28382][error ] Hello SnowK
[15:34:45][28382][critical] Hello SnowK
3.异步日志并输出到标准输出
main.cc
:# include <iostream>
# include <spdlog/spdlog.h>
# include <spdlog/sinks/stdout_color_sinks.h>
# include <spdlog/async.h>
int main ( )
{
spdlog:: flush_every ( std:: chrono:: seconds ( 1 ) ) ;
spdlog:: flush_on ( spdlog:: level:: level_enum:: debug) ;
spdlog:: set_level ( spdlog:: level:: level_enum:: debug) ;
spdlog:: init_thread_pool ( 2048 , 1 ) ;
auto logger = spdlog:: stdout_color_mt< spdlog:: async_factory> ( "async-logger" ) ;
logger-> set_pattern ( "[%H:%M:%S][%t][%-8l] %v" ) ;
logger-> trace ( "Hello {}" , "SnowK" ) ;
logger-> debug ( "Hello {}" , "SnowK" ) ;
logger-> info ( "Hello {}" , "SnowK" ) ;
logger-> warn ( "Hello {}" , "SnowK" ) ;
logger-> error ( "Hello {}" , "SnowK" ) ;
logger-> critical ( "Hello {}" , "SnowK" ) ;
std:: cout << "spdlog 日志输出完毕" << std:: endl;
return 0 ;
}
输出结果 :spdlog 日志输出完毕
[15:41:47][28793][debug ] Hello SnowK
[15:41:47][28793][info ] Hello SnowK
[15:41:47][28793][warning ] Hello SnowK
[15:41:47][28793][error ] Hello SnowK
[15:41:47][28793][critical] Hello SnowK
5.spdlog与glog组件对比
0.前言
glog
和spdlog
都是流行的C++日志库,它们各自具有不同的特点和优势
以下是对这两个库的对比分析,包括性能测试的结果和使用场景的考量
1.glog
glog
是由Google开发的一个开源C++日志库,它提供了丰富的日志功能,包括多种日志级别、条件日志记录、日志文件管理、信号处理、自定义日志格式等
glog
默认情况下是同步记录日志的,这意味着每次写日志操作都会阻塞直到日志数据被写入磁盘
性能 :根据张生荣的性能对比测试分析,glog
在同步调用的场景下的性能较spdlog
慢
在一 台低配的服务器上,glog
耗时1.027秒处理十万笔日志数据,而在固态硬盘上的耗时 为0.475
秒
2.spdlog
spdlog
是一个开源的、高性能的C++日志库,它支持异步日志记录,允许在不影响主线程的情况下进行日志写入
性能 :在同样的性能测试中,spdlog
在同步调用的场景下比glog
快
在低配服务器上的耗时为0.135秒,而在固态硬盘上的耗时为0.057秒
此外,spdlog
还提供了异步日志记录 的功能,其简单异步模式的耗时为0.158秒
3.对比总结
性能 :从性能测试结果来看,spdlog
在同步调用场景下的性能优于glog
,当涉及到大量日志数据时,spdlog
显示出更快的处理速度。
异步日志 :spdlog
支持异步日志记录,这在处理高负载应用程序时非常有用,可以减少日志操作对主线程的影响。
易用性 :spdlog
提供了更简单的集成和配置方式,只需包含头文件即可使用,而glog
可能需要额外的编译和配置步骤。
功能 :glog
提供了一些特定的功能,如条件日志记录和信号处理,这些在某些场景下可能非常有用
使用场景 :
glog
可能更适合那些对日志性能要求不是特别高,但需要一些特定功能的场景
spdlog
则适合需要高性能日志记录和异步日志能力 的应用程序
在选择日志库时,开发者应根据项目的具体需求和性能要求来决定使用哪个库
如果项目对日志性能有较高要求,或者需要异步日志记录来避免阻塞主线程,spdlog
可能是更好的选择
如果项目需要一些特定的日志功能,或者已经在使用glog
且没有显著的性能问题,那么继续使用glog
也是合理的