1、说明
spdlog是一款开源的、快速的日志库。使用简单、功能齐全,能满足日常开发使用。
下载地址:https://github.com/gabime/spdlog
它的优点有很多,使用过程主要体现的优点有以下几点:
(1)配置特别简单,仅包含头文件即可;
(2)写日志方式简单明了;
(3)可实现自动按日期创建日志文件/定时创建日志文件;
(4)可自定义日志格式;
(5)可以输出当前输出日志所在的文件及函数;
(6)可自定义文档大小;
(7)可将不同级别的信息输出到不同日志文件;
(8)多平台等。
结合自身项目经验,一般在实际工作中,日志最好要有以下3个功能:①自动按日期创建日志文件;②自动定期清理过期日志(spdlog好像没有这个功能…);③实时刷新日志。
为什么呢?第一:一天只创建一个日志文件(或者将各级别的日志单独创建为一个文件)是为了更加清晰简洁,若程序在每次开启时都创建一个日志文件,可能就会显得很混乱。第二:一般时间较久的日志不再具有参考价值,如果真是现场出现问题,用户也不会等很久才反馈。所以一般保存7天内的日志就足够了。第三:当我们需要查看日志时,但又不能关闭现场程序,那么就需要能实时刷新日志信息。不然我们可能会遇到日志文件打不开或者打开以后日志信息未更新的问题。
当然,根据项目性质或环境的不同,对日志的侧重点也可能会不一样。大家可根据自身需求选择合适的日志库来使用。总的来说,最适合当前项目的才是最好的。
2、spdlog下载与配置
1、下载并解压,很简单,直接DownLoad下来解压就就行。
2、将文件夹中的include文件夹拷贝到所要使用的项目目录下。
3、注意,是拷贝到与.cpp同级目录下。
.cpp的路径也可以直接通过以下方式打开,以vs为例:
4、配置,右键项目->选择“属性”,注意配置的环境要保持统一,Debug/Release,x64/win32。然后按图示依次选择,点击Edit。
5、添加头文件include,点击OK,应用即可。
6、到此,spdlog库就已经配置好了,接下来测试一下,添加如下头文件,看是否会报错,若没有,说明配置成功。若错误,再仔细检查一下,可能是环境不一致、或者头文件路径不对导致。
#include"include/spdlog/spdlog.h"
#include"include/spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"
#include"include/spdlog/sinks/rotating_file_sink.h"
#include"include/spdlog/sinks/daily_file_sink.h"
#include"include/spdlog/sinks/dist_sink.h"
#include <afxcontrolbars.h>
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE//必须定义这个宏,才能输出文件名和行号
using namespace spdlog;
3、使用
我主要是使用在MFC程序中,其实在控制台等其它地方使用是一样的。
1、首先对日志进行必要的设置,包括日志创建方式、日志输出格式、刷新方式等。
std::shared_ptr<logger> m_logger; //这里我将日志指针定义为全局变量,方便使用
void LogFileProcess()
{
//创建日志,路径为根目录下logs/下,每天创建一个日志文件,格式为log_2020-12-01.log
typedef spdlog::sinks::daily_file_sink<std::mutex, spdlog::sinks::daily_filename_calculator> dateonly_daily_file_sink_mt;
m_logger = spdlog::create<dateonly_daily_file_sink_mt>("m_logger", "logs/log.log", 0, 0);
//设置日志输出格式
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l][%s,%!]:%v");
//当遇到info消息级别以上的立刻刷新到日志,也可设置为遇到warning级别及其以上的立刻刷新
m_logger->flush_on(spdlog::level::info);
/***********至此,日志的创建、日志格式设置、日志刷新设置已经完成*************/
/***********下方内容为清理七天前的日志,判断方式不太好,其实只是判断日志文件夹中的.log文件个数,正常情况下应该按照日期进行判断********/
//获取当前日志名称
auto now = log_clock::now();
filename_t base_filename = "log";
auto current_log_name = spdlog::sinks::daily_filename_calculator::calc_filename(base_filename, spdlog::details::os::localtime(log_clock::to_time_t(now)));
//当前日志路径
std::string path = "logs";
//判断是否需要清理日志文件
std::vector<std::string> log_file_name_vec;
int file_num = 0;
struct _finddata_t file_info;
std::string curr_path = path + "\\*.log";
intptr_t handle = _findfirst(curr_path.c_str(), &file_info);
if (-1 == handle)
{
return;
}
std::string temp_file_name = path + "\\" + file_info.name;
log_file_name_vec.push_back(temp_file_name);
file_num++;
while (!_findnext(handle, &file_info))
{
temp_file_name = path + "\\" + file_info.name;
log_file_name_vec.push_back(temp_file_name);
file_num++;
}
_findclose(handle);
if (file_num >= 8)
{
for (int i = 0; i < log_file_name_vec.size(); ++i)
{
if (log_file_name_vec.at(i) != current_log_name)
{
std::string file_path_str = log_file_name_vec.at(i);
char* file_path_ptr = const_cast<char*>(file_path_str.c_str());
remove(file_path_ptr);
}
}
}
}
2、在需要打印日志的地方进行输出,很简单。此处只进行说明,大家可根据自己的情况打印需要的日志信息。
(1)使用SPDLOG_LOGGER_INFO、SPDLOG_LOGGER_WARN、SPDLOG_LOGGER_ERROR
等输出日志,才能输出文件名或所在函数(据说可以输出行号,但我试了下好像没有…,大家可以试一下),要是不能输出行号,也可通过示例中的方式输出。
示例1:
try
{
//一系列的处理操作
}
catch (std::out_of_range r) //捕获是否有数组越界的异常
{
//表示输出error级别的日志,m_logge为日志指针
//第二个参数为需要输出的内容
//说明:{}为占位符,可以输出第三个参数中的内容
SPDLOG_LOGGER_ERROR(m_logger, "数组越界:{}", r.what());
}
示例2:
SPDLOG_LOGGER_WARN(m_logger, "服务器未应答"); //warning级别的日志
SPDLOG_LOGGER_WARN(m_logger, "服务器未应答{}",__LINE__); //warning级别的日志,__LINE__可以获取当前行号,进行输出
SPDLOG_LOGGER_INFO(m_logger, "成功"); //info级别的日志
(2)也可使用以下方式进行日志的输出。
m_logger->info("info");
m_logger->warn("warn");
m_logger->error("error");
3、关于格式设置的一些参数展示: