st和mt
spdlog中各对象都分为多线程与单线程版本:
*_st
:单线程版本,不用加锁,效率更高。*_mt
:多线程版本,用于多线程程序是线程安全的。
s----single;
m---mutiple(倍数,多样的)
spdlog是基于C++11实现的一款纯头文件的日志管理库
git地址:https://github.com/gabime/spdlog
日志级别
error,warn,critical,info为不同等级的log,输出在控制台会以不同颜色表示
spdlog---命名空间
logger---类--日志对象
日志记录器
作用:
在 `spdlog` 日志库中,`logger` 是一个核心概念。`logger` 代表了一个具体的日志记录器,负责将日志消息输出到指定的目的地,比如控制台、文件等。
日志对象,每个logger内包含了一个vector用于存放sink,每个sink都是相互独立
因此一个日志对象在输出日志时可以同时输出到控制台和文件等位置。
各类logger类型
basic_logger_mt
第一个参数:logger对象的名称;
第二个参数:日志输出的文件。
作用:
`basic_logger_mt` 代表的最基本的日志功能包括以下几个方面:
1. 日志级别:提供不同级别的日志输出,如 `debug`、`info`、`warning`、`error`、`critical` 等。
2. 日志输出格式:使用占位符等方式,对日志信息进行格式化,以便更好地展示、存储和分析。
3. 多线程安全:确保在多线程环境下,日志操作不会出现互斥问题,如多线程同时操作同一文件。
4. 日志文件位置:确定日志输出的位置,常见的有控制台、文件、网络等。
这些功能组合在一起,能够满足绝大部分日志记录需求。对于需要更多高级功能如日志文件循环滚动、异步写入等的需求,可以使用 `rotating_logger_mt` 或 `async_logger` 等更为高级的 `logger` 类型。
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;
}
rotating_logger_mt
第一个参数:logger对象的名称;
第二个参数:日志输出的文件;
第三个参数:日志文件的大小--单位:字节;
第四个参数:最多日志文件的数量上限。
循环的日志对象
rotating_logger_mt中循环的日志文件是指所有日志文件,还是rotating_logger_mt创建的对象创建的日志?
`rotating_logger_mt` 中循环的日志文件指的是当前 `rotating_logger_mt` 对象创建的日志文件,这些日志文件会根据指定的大小或时间限制进行循环轮转。
如果系统中同时存在多个不同名称的 `rotating_logger_mt` 对象,其创建的日志文件将彼此独立,互不干扰,日志轮换也只会影响到自身的日志文件。每个 `rotating_logger_mt` 对象都会创建属于自己的日志文件,命名规则和轮转规则也由该对象的参数决定,因此不同的对象之间的日志文件数量、命名规则和轮转规则都可以不同。
需要注意的是,多个 `rotating_logger_mt` 对象如果使用相同名称的日志文件路径进行创建,将会发生文件被覆盖的情况。因此在创建 `rotating_logger_mt` 对象时,需要使用独立的日志文件路径和名称,以避免不必要的问题。
文件大小超出时
当 `rotating_logger_mt` 中的单个日志文件大小超过指定的上限时,会触发日志文件的轮转,即将当前的日志文件重命名并创建一个新的日志文件,以继续记录后续的日志信息。同时,如果前面设置了历史日志文件的数量,那么新增的日志文件会被命名为 `"filename.1"`,当前的文件会被重命名为 `"filename.2"`,之前的文件会被依次重命名为 `"filename.3"` 等等,以保留过去的历史日志。
文件数量超出时
当历史日志文件数量超过指定的上限时,较早的文件将会被自动删除。这一过程不会阻塞当前的应用程序,而是在后台运行,通过单独的线程实现。
如果没有指定历史日志文件的数量,那么旧的日志文件将一直被保留,直到用户手动清空或者删除它们。
需要注意的是,使用 `rotating_logger_mt` 时,必须保持日志文件的路径和名称不变,否则将无法进行日志轮转。如果需要更改日志文件名称或路径,应该重新创建一个新的 `rotating_logger_mt` 对象,并将旧对象销毁。
#include <iostream>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h" // support for rotating file logging
int main()
{
try
{
auto file_logger = spdlog::rotating_logger_mt("file_logger", "myfilename",
1024 * 1024 * 5, 10);
file_logger->set_level(spdlog::level::debug);
int i = 0;
while (i < 1000000)
{
file_logger->debug("Async message #{}", i);
i++;
}
}
catch (const spdlog::spdlog_ex& ex)
{
std::cout << "Log initialization failed: " << ex.what() << std::endl;
}
}
stdout_color_mt
只需要一个参数:logger对象的名称。
作用:
`stdout_color_mt()` 是 `spdlog` 库中的一个函数,用于创建一个多线程的输出到控制台的 `logger` 对象,即 `spdlog::stdout_color_mt`。与 `basic_logger_mt` 不同的是,`stdout_color_mt` 会在控制台输出带有颜色的日志,便于区分不同级别的日志。
使用 `stdout_color_mt` 需要在编译选项中开启 ANSI 控制台支持。如果编译器不支持 ANSI 控制台,`spdlog` 将会自动退化为不带颜色的输出方式。
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h> //这个头文件一定要加
int main() {
auto logger = spdlog::stdout_color_mt("my_logger");
logger->info("This is a colorful log message!");
logger->error("Oh no, this is an error!");
return 0;
}
logger的建立方式
1,直接利用指定logger类型创建
auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt");
// 设置该日志的显示级别
my_logger->set_level(spdlog::level::warn);
// 向该日志中写入信息
my_logger->warn("Hello, {}!", "World");
2,利用sink创建logger
auto log_console = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto log_file = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/basic-log.log");
std::vector<spdlog::sink_ptr> sinks {log_console, log_file};
auto combined_logger = std::make_shared<spdlog::logger>("log_out", sinks.begin(), sinks.end());
spdlog::register_logger(combined_logger);
获取已经创建并且注册了的logger对象
SPDLOG_INLINE std::shared_ptr<logger> get(const std::string &name)
{
return details::registry::instance().get(name);
}
创建有用多个输出路径的logger对象
auto temp_name=std::string("test_logger");
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto rotating_file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("/home/log/log1.log", 1048576 * 5, 3);
std::vector<spdlog::sink_ptr> sink_vector{console_sink, rotating_file_sink};
auto temp_logger = std::make_shared<spdlog::logger>(temp_name, sink_vec.begin(), sink_vec.end());
spdlog::register_logger(temp_logger);
sink-----类
日志记录器槽----可以简单理解为日志的目的地。
`spdlog` 中的 sink 是哟个类,它创建的对象记录着logger对象日志的输出位置和格式。简单来说,sink 的作用是控制日志的输出方式和存储位置。
使用 `spdlog`,你可以选择将日志输出到不同的 sink 上,它们可以同时存在,且每个 sink 可以被多个日志记录器同时使用。下面是 `spdlog` 支持的一些输出目的地类型:
- `stdout_sink_mt`:输出到控制台;
- `stderr_sink_mt`:输出到控制台标准错误输出流;
- `rotating_file_sink_mt`:输出到文件,支持按文件大小回滚;
- `daily_file_sink_mt`:每天创建一个新的日志文件;
- `syslog_sink_mt`:输出到 syslog(仅适用于类 UNIX 系统)。
logger(日志保存在logger对象中)---------》通过sink----------》输出到控制台。
日志的输出类型
就三种:
enum class ELogType : unsigned {
kConsole = 0, ///< Write into the console (e.g., Terminal)
kFile = 1, ///< Write into the specified file
kConsoleAndFile = 2, ///< Write into the console and the file
};
logger默认的sink(输出目的地)
默认是输出到控制台。
switch (type) {
case ELogType::kConsole: {
sinks.front()->set_level(static_cast<spdlog::level::level_enum>(level));
//vector中的第一个是控制台
} break;
case ELogType::kFile: {
sinks.back()->set_level(static_cast<spdlog::level::level_enum>(level));
//第二个是文件
} break;
case ELogType::kConsoleAndFile: {
//控制台和文件
for (auto& sink : sinks) {
sink->set_level(static_cast<spdlog::level::level_enum>(level));
}
} break;
}
sink类型
这些
stdout_color_sink_mt
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
int main() {
// 创建一个控制台输出目的地
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
// 创建一个日志对象并关联上述输出目的地
auto logger = std::make_shared<spdlog::logger>("console_logger", console_sink);
// 设置日志级别为 debug,输出一条 debug 级别的日志
spdlog::set_level(spdlog::level::debug);
SPDLOG_DEBUG(logger, "This is a debug message.");
return 0;
}
rotating_file_sink_mt
auto temp_name=std::string("test_logger");
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto rotating_file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("/home/log/log1.log", 1048576 * 5, 3);
std::vector<spdlog::sink_ptr> sink_vector{console_sink, rotating_file_sink};
auto temp_logger = std::make_shared<spdlog::logger>(temp_name, sink_vec.begin(), sink_vec.end());
spdlog::register_logger(temp_logger);
获取logger中保存sink的vector
直接调用logger对象的sinks接口就会返回vector
auot logger->sinks();
日志对象,每个logger内包含了一个vector用于存放sink,每个sink都是相互独立
因此一个日志对象在输出日志时可以同时输出到控制台和文件等位置。
日志的生成到输出完整实现
可以先生成logger对象,再生成sink对象,再通过logger对象的sinks()方法将sink对象加入logger中;
auto logger = spdlog::rotating_logger_mt("test", "test.log", 1048576 * 5, 3);
// 添加控制台输出
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(spdlog::level::info);
logger->sinks().push_back(console_sink);
// 添加文件输出
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("test.log", true);
file_sink->set_level(spdlog::level::err);
logger->sinks().push_back(file_sink);
也可以先生成所有的sink对象,再利用sink对象生成logger。
auto log_console = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto log_file = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/basic-log.log");
std::vector<spdlog::sink_ptr> sinks {log_console, log_file};
auto combined_logger = std::make_shared<spdlog::logger>("log_out", sinks.begin(), sinks.end());
spdlog::register_logger(combined_logger);
输出到文件
auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt");
// 设置该日志的显示级别
my_logger->set_level(spdlog::level::warn);
// 向该日志中写入信息
my_logger->warn("this is warn log.");
my_logger->set_level(spdlog::level::info);
my_logger->info("this is info log.");
输出到控制台
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h> //这个头文件一定要加
int main() {
auto logger = spdlog::stdout_color_mt("my_logger");
logger->info("This is a colorful log message!");
logger->error("Oh no, this is an error!");
return 0;
}
输出到文件和控制台
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iostream>
int main() {
// 添加控制台输出
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
// 添加文件输出
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("test.log", true);
std::vector<spdlog::sink_ptr> sinks{console_sink,file_sink};
auto logger=std::make_shared<spdlog::logger>("test_logger",sinks.begin(),sinks.end());
// 输出日志
logger->debug("this is debug log.");
logger->info("this is info log.");
logger->warn("this is warn log.");
logger->error("this is error log.");
logger->critical("this is critical log.");
return 0;
}
registry---logger的登记(注册)对象
registry管理所有的logger,用户创建的logger会自动注册到registry统一管理。例如可通过spdlog::get()
访问已创建的logger。
获取已经注册的logger对象
SPDLOG_INLINE std::shared_ptr<logger> get(const std::string &name)
{
return details::registry::instance().get(name);
}
#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
int main() {
// 创建一个控制台输出目的地
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
// 创建一个日志对象并关联上述输出目的地
auto logger = std::make_shared<spdlog::logger>("console_logger", console_sink);
// 将日志对象注册到 spdlog
spdlog::register_logger(logger);
// 在其他地方通过名称获取该日志对象
auto other_logger = spdlog::get("console_logger");
if (other_logger) {
std::cout<<"success get registered logger"<<std::endl;
}
return 0;
}
注册创建的logger对象
auto temp_name=std::string("test_logger");
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto rotating_file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("/home/log/log1.log", 1048576 * 5, 3);
std::vector<spdlog::sink_ptr> sink_vector{console_sink, rotating_file_sink};
auto temp_logger = std::make_shared<spdlog::logger>(temp_name, sink_vec.begin(), sink_vec.end());
spdlog::register_logger(temp_logger);
std::shared_ptr<logger> daily_logger_mt(..............){}
std::shared_ptr<logger> daily_logger_st(..............){}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> daily_logger_format_mt(const std::string &logger_name, const filename_t &filename, int hour = 0,
int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
{
return Factory::template create<sinks::daily_file_format_sink_mt>(
logger_name, filename, hour, minute, truncate, max_files, event_handlers);
}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0,
bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
{
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
}
建立日志记录器对象,第一个参数是记录器的名字,第二个是记录日志的文件(指定路径)。
日志等级
enum level_enum { 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,//最高 }; - `trace`:最低级别,用于记录一些系统运行状态和调试信息。 - `debug`:用于记录调试信息、变量的值等,主要是为了方便程序员调试程序。 - `info`:用于记录一些重要的信息,例如程序启动和退出、配置文件加载、网络连接等。 - `warn`:用于表示某些不严重的异常或警告信息,不会影响程序运行。 - `error`:用于表示发生了一些错误,并可能会影响程序的正常运行。 - `critical`:最高级别,表示发生了非常严重的问题,可能会导致程序崩溃或系统崩溃。
设置日志等级之后可以打印的日志等级最高位info,也就是debug和trace不打印
作用
1,设置日志等级可以帮助我们控制不同级别日志的输出与否,只输出一定等级及以上的日志消息到目的地;
注意:不是只输出这个级别。
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iostream>
int main() {
// 添加控制台输出
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
// 添加文件输出
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("test.log", true);
std::vector<spdlog::sink_ptr> sinks{console_sink,file_sink};
auto logger=std::make_shared<spdlog::logger>("test_logger",sinks.begin(),sinks.end());
// 设置日志等级
logger->set_level(spdlog::level::err);
// 输出日志
logger->debug("this is debug log.");
logger->info("this is info log.");
logger->warn("this is warn log.");
logger->error("this is error log.");
logger->critical("this is critical log.");
return 0;
}
输出:
2,根据需要将不同等级的日志消息输出到不同的地方。
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iostream>
int main() {
// 添加控制台输出
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
//控制台只需要输出info及以上级别的日志
console_sink->set_level(spdlog::level::info);
// 添加文件输出
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("test.log", true);
//文件中只需要输出err及以上级别的日志
file_sink->set_level(spdlog::level::err);
std::vector<spdlog::sink_ptr> sinks{console_sink,file_sink};
auto logger=std::make_shared<spdlog::logger>("test_logger",sinks.begin(),sinks.end());
// 输出日志
logger->debug("this is debug log.");
logger->info("this is info log.");
logger->warn("this is warn log.");
logger->error("this is error log.");
logger->critical("this is critical log.");
return 0;
}
控制台输出:
文件输出:
日志等级接口的作用
输出日志到指定位置。
日志等级和日志等级接口的关系
控制台输出日志
#include <iostream>
#include <spdlog/spdlog.h>
int main(){
spdlog::info("info--no format paramater.");
spdlog::info("info--has format param: {}","arg1");
spdlog::warn("warn--no format param.");
spdlog::warn("warn--has format param: {}","arg1");
spdlog::error("error--no format parama.");
spdlog::error("errpr--has param:{}","arg1");
return 0;
}
日志等级能不能同时设置多种
SPDLOG_INLINE void logger::set_level(level::level_enum log_level) { level_.store(log_level); }
logger日志等级变量本质
using level_t = std::atomic<int>;
就是一个原子变量;
原子变量store会替换原来的值,所以不可以同时设置一个logger对象的多个日志等级。
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iostream>
int main() {
// 创建循环日志对象,日志文件会自动滚动,并保存前三个日志文件
auto logger = spdlog::rotating_logger_mt("test", "test.log", 1048576 * 5, 3);
// 添加控制台输出
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
logger->sinks().push_back(console_sink);
// 添加文件输出
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("test.log", true);
logger->sinks().push_back(file_sink);
// 设置日志等级
logger->set_level(spdlog::level::info);
logger->set_level(spdlog::level::err);
logger->set_level(spdlog::level::debug);
// 输出日志
logger->info("this is info log.");
logger->error("this is error log.");
logger->debug("this is debug log.");
return 0;
}
控制台:
日志文件:
重复输出。
设置logger的各个sink的日志等级
通过sinks()接口获取再设置。
logger设置日志级别和logger中各个sink设置日志级别的区别
logger设置的日志等级最终会用于设置logger中的sink,而sink自己也有日志等级。
所以sink最终的日志等级是logger设置的日志等级和sink自己原有的日志等级取等级高的设置为sink的日志等级。
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iostream>
int main() {
// 添加控制台输出
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(spdlog::level::warn);
// 添加文件输出
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("test.log", true);
file_sink->set_level(spdlog::level::err);
std::vector<spdlog::sink_ptr> sinks{console_sink,file_sink};
auto logger=std::make_shared<spdlog::logger>("test_logger",sinks.begin(),sinks.end());
// 通过logger设置日志等级
logger->set_level(spdlog::level::info);
// 输出日志
logger->debug("this is debug log.");
logger->info("this is info log.");
logger->warn("this is warn log.");
logger->error("this is error log.");
logger->critical("this is critical log.");
return 0;
}
控制台输出warn+info=warn:
文件输出warn+err=err:
logger中日志等级保存对象
不是logger对象,而是sink对象,无论通过logger对象还是sink对象修改logger的日志等级,最终都是对sink对象中日志等级的修改。
日志文件
日志文件和目录不存在,会自动创建。
日志文件已经存在,日志是否覆盖
新建的文件会覆盖之前的文件。
日志记录器(logger)的成员函数
get(logger_name)
set_pattern
设定日志输出时的格式
set_level
设置日志等级
info()
/*创建一个控制台对象*/
auto console = spdlog::stdout_color_mt("ybhy");
/*在控制台输出以下数据 -- 不同的等级有不同的颜色对应 :info、error、warn、critical*/
console->info("信息");
console->warn("警告");
console->error("错误");
console->critical("危险");
/*上面的另一种写法*/
spdlog::info("信息");
spdlog::warn("警告");
spdlog::error("错误");
spdlog::critical("危险");
spdlog::drop_all();
/*关闭所有logger对象*/
宏
日志文件的使用
三种创建日志文件的方式
使用不同的方式创建logger对象,要包含不同的头文件:
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/basic_file_sink.h"
创建日志记录器
创建日志记录器之后,利用日志等级函数写的日志数据就会写入到日志记录器对应的日志文件中,不再输出到控制台。
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 256, 2);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);