文章目录
- spdlog的介绍与安装
- 使用样例
- 二次封装
spdlog的介绍与安装
spdlog 是一个高性能、超快速、零配置的 C++ 日志库,它旨在提供简洁的 API 和丰富的功能,同时保持高性能的日志记录。它支持多种输出目标、格式化选项、线程安全以及异步日志记录。
特点
高性能:spdlog 专为速度而设计,即使在高负载情况下也能保持良好的性能。
零配置:无需复杂的配置,只需包含头文件即可在项目中使用。
异步日志:支持异步日志记录,减少对主线程的影响。
格式化:支持自定义日志消息的格式化,包括时间戳、线程 ID、日志级别等。
多平台:跨平台兼容,支持 Windows、Linux、macOS 等操作系统。
丰富的 API:提供丰富的日志级别和操作符重载,方便记录各种类型的日志。
安装
centos
sudo yum install -y spdlog-devel
unbuntu
sudo apt-get install libspdlog-dev
源码安装
git clone https://github.com/gabime/spdlog.git
cd spdlog/
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make && sudo make install
使用
包含头文件:
在你的 C++ 源文件中包含 spdlog 的头文件:
#include <spdlog/spdlog.h>
日志输出等级枚举
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};
}
日志输出格式自定义:
可以自定义日志消息的格式:
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)。
日志记录器类:
创建一个基本的日志记录器,并设置日志级别和输出模式:
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);};
}
异步日志记录类:
为了异步记录日志,可以使用 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");
日志记录器工厂类:
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);
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 voidset_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;void set_level(level::level_enum log_level);};using stderr_color_sink_mt;using stderr_sink_mt;using stdout_color_sink_mt;using stdout_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>;//*_st:单线程版本,不用加锁,效率更高。//*_mt:多线程版本,用于多线程程序是线程安全的。}
}
全局接口:
// 输出等级设置接口
void set_level(level::level_enum log_level);
// 日志刷新策略-每隔 N 秒刷新一次
void flush_every(std::chrono::seconds interval);
// 日志刷新策略-触发指定等级立即刷新
void flush_on(level::level_enum log_level);
记录日志:
使用日志记录器记录不同级别的日志:
logger->trace("This is a trace message");
logger->debug("This is a debug message");
logger->info("This is an info message");
logger->warn("This is a warning message");
logger->error("This is an error message");
logger->critical("This is a critical message");
使用样例
同步日志输出
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <iostream>int main()
{// 设置全局的日志输出等级spdlog::set_level(spdlog::level::level_enum::debug);// 设置全局的刷新策略--每隔 N 秒刷新一次spdlog::flush_every(std::chrono::seconds(1));// 遇到debug以上等级日志立即刷新spdlog::flush_on(spdlog::level::level_enum::debug);// 创建同步日志器(标准输出/文件)--工厂模式默认创建的就是同步日志器auto logger = spdlog::stdout_color_mt("default-logger");// auto logger = spdlog::basic_logger_mt("file-logger","sync.log");// 设置日志刷新策略以及设置日志的输出等级// logger->flush_on(spdlog::level::level_enum::debug);// logger->set_level(spdlog::level::level_enum::debug);// 设置日志的输出格式logger->set_pattern("[%n][%H:%M:%S][%t][%-8l] %v");logger->trace("hello {}","world");logger->debug("hello {}","world");logger->warn("hello {}","world");logger->error("hello {}","world");logger->critical("hello {}","world");std::cout << "日志输出完毕" << std::endl;return 0;
}
异步日志输出
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/async.h>
#include <iostream>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);// 初始化异步日志输出线程配置// void init_thread_pool(size_t q_size,size_t thread_count);spdlog::init_thread_pool(3072, 1);// 创建同步日志器(标准输出/文件)--工厂模式默认创建的就是同步日志器auto logger = spdlog::stdout_color_mt<spdlog::async_factory>("async-logger");//设置日志输出格式logger->set_pattern("[%n][%H:%M:%S][%t][%-8l] %v");//进行简单的日志输出logger->trace("你好!{}", "小明");logger->debug("你好!{}", "小明");logger->info("你好!{}", "小明");logger->warn("你好!{}", "小明");logger->error("你好!{}", "小明");logger->critical("你好!{}", "小明");std::cout << "日志输出演示完毕!\n";return 0;
}
二次封装
原因:
1.避免单例的锁冲突,影响直接创建全局的线程安全的日志器进行使用
2.因为日志输出没有文件名和行号,因为使用宏进行二次封装输出日志的文件名和行号
3.封装一个初始化接口,便于使用:调试模式测输出到标准输出,否则则输出到文件中
思想:
封装出一个全局接口,用户进行日志器的创建与初始化
初始化接口接收3个参数:运行模式,输出文件名–用户发布模式,输出日志等级–用于发布模式
对日志输出的接口,进行宏的封装,加入文件名和行号的输出
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/async.h>
#include <iostream>// mode - 运行模式: true-发布模式; false调试模式
namespace hdp
{std::shared_ptr<spdlog::logger> g_default_logger;void init_logger(bool mode, const std::string &filename, int32_t level){if (mode == false){// 如果是调试模式,则创建标准输出日志器,输出等级为最低g_default_logger = spdlog::stdout_color_mt("default-logger");g_default_logger->set_level(spdlog::level::level_enum::trace);g_default_logger->flush_on(spdlog::level::level_enum::trace);}else{// 如果是发布模式,则创建文件输出日志器,输出等级根据参数而定g_default_logger = spdlog::basic_logger_mt("default-logger", filename);g_default_logger->set_level((spdlog::level::level_enum)level);g_default_logger->flush_on((spdlog::level::level_enum)level);}g_default_logger->set_pattern("[%n][%H:%M:%S][%t][%-8l]%v");}#define LOG_TRACE(format, ...) hdp::g_default_logger->trace(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__);
#define LOG_DEBUG(format, ...) hdp::g_default_logger->debug(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__);
#define LOG_INFO(format, ...) hdp::g_default_logger->info(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__);
#define LOG_WARN(format, ...) hdp::g_default_logger->warn(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__);
#define LOG_ERROR(format, ...) hdp::g_default_logger->error(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__);
#define LOG_FATAL(format, ...) hdp::g_default_logger->critical(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__);
}
测试代码:
#include <gflags/gflags.h>
#include "logger.hpp"DEFINE_int32(run_mode, 0, "程序的运行模式,0-调试,1-发布");
DEFINE_string(log_file, "", "发布模式下,用于指定日志的输出文件");
DEFINE_int32(log_level, 0, "发布模式下,用于指定日志的输出等级");int main(int argc, char *argv[])
{google::ParseCommandLineFlags(&argc, &argv, true);init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);LOG_DEBUG("Hello {}", "World");LOG_INFO("Hello {}", "World");LOG_WARN("Hello {}", "World");LOG_ERROR("Hello {}", "World");LOG_FATAL("Hello {}", "World");return 0;
}
makefile
all:sync async main
sync:sync.ccg++ -o $@ $^ -std=c++17 -lspdlog -lfmt
async:async.ccg++ -o $@ $^ -std=c++17 -lspdlog -lfmt
main:main.ccg++ -o $@ $^ -std=c++17 -lspdlog -lfmt -lgflags.PHONY:clean
clean:rm -rf async sync main
测试结果
NFO(“Hello {}”, “World”);
LOG_WARN(“Hello {}”, “World”);
LOG_ERROR(“Hello {}”, “World”);
LOG_FATAL(“Hello {}”, “World”);
return 0;
}
makefile```makefile
all:sync async main
sync:sync.ccg++ -o $@ $^ -std=c++17 -lspdlog -lfmt
async:async.ccg++ -o $@ $^ -std=c++17 -lspdlog -lfmt
main:main.ccg++ -o $@ $^ -std=c++17 -lspdlog -lfmt -lgflags.PHONY:clean
clean:rm -rf async sync main
测试结果