基于C++ spdlog日志库的完善封装

spdlog是一个C++编写的极速日志打印库,支持异步写日志以及多种模式和格式化选项。以下基于spdlog库封装了一个简单易用的功能类,采用的是header-only方式,便于项目集成。

代码

#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <time.h>
#include <chrono>
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/stdout_color_sinks.h" // or "../stdout_sinks.h" if no color needed
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"

static inline int NowDateToInt()
{
	time_t now;
	time(&now);

	// choose thread save version in each platform
	tm p;
#ifdef _WIN32
	localtime_s(&p, &now);
#else
	localtime_r(&now, &p);
#endif // _WIN32
	int now_date = (1900 + p.tm_year) * 10000 + (p.tm_mon + 1) * 100 + p.tm_mday;
	return now_date;
}

static inline int NowTimeToInt()
{
	time_t now;
	time(&now);
	// choose thread save version in each platform
	tm p;
#ifdef _WIN32
	localtime_s(&p, &now);
#else
	localtime_r(&now, &p);
#endif // _WIN32

	int now_int = p.tm_hour * 10000 + p.tm_min * 100 + p.tm_sec;
	return now_int;
}

class XLogger
{
public:
	static XLogger* getInstance()
	{
		static XLogger xlogger;
		return &xlogger;
	}

	std::shared_ptr<spdlog::logger> getLogger()
	{
		return m_logger;
	}

private:
	// make constructor private to avoid outside instance
	XLogger()
	{
		// hardcode log path
		const std::string log_dir = "./log"; // should create the folder if not exist
		const std::string logger_name_prefix = "test_";

		// decide print to console or log file
		bool console = false;

		// decide the log level
		std::string level = "debug";

		try
		{
			// logger name with timestamp
			int date = NowDateToInt();
			int time = NowTimeToInt();
			const std::string logger_name = logger_name_prefix + std::to_string(date) + "_" + std::to_string(time);

			if (console)
				m_logger = spdlog::stdout_color_st(logger_name); // single thread console output faster
			else
				//m_logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>(logger_name, log_dir + "/" + logger_name + ".log"); // only one log file
				m_logger = spdlog::create_async<spdlog::sinks::rotating_file_sink_mt>(logger_name, log_dir + "/" + logger_name + ".log", 500 * 1024 * 1024, 1000); // multi part log files, with every part 500M, max 1000 files

			// custom format
			m_logger->set_pattern("%Y-%m-%d %H:%M:%S.%f <thread %t> [%l] [%@] %v"); // with timestamp, thread_id, filename and line number

            if (level == "trace")
			{
				m_logger->set_level(spdlog::level::trace);
				m_logger->flush_on(spdlog::level::trace);
			}
			else if (level == "debug")
			{
				m_logger->set_level(spdlog::level::debug);
				m_logger->flush_on(spdlog::level::debug);
			}
			else if (level == "info")
			{
				m_logger->set_level(spdlog::level::info);
				m_logger->flush_on(spdlog::level::info);
			}
			else if (level == "warn")
			{
				m_logger->set_level(spdlog::level::warn);
				m_logger->flush_on(spdlog::level::warn);
			}
			else if (level == "error")
			{
				m_logger->set_level(spdlog::level::err);
				m_logger->flush_on(spdlog::level::err);
			}
		}
		catch (const spdlog::spdlog_ex& ex)
		{
			std::cout << "Log initialization failed: " << ex.what() << std::endl;
		}
	}

	~XLogger()
	{
		spdlog::drop_all(); // must do this
	}

	void* operator new(size_t size)
	{}

	XLogger(const XLogger&) = delete;
	XLogger& operator=(const XLogger&) = delete;

private:
	std::shared_ptr<spdlog::logger> m_logger;
};

// use embedded macro to support file and line number
#define XLOG_TRACE(...) SPDLOG_LOGGER_CALL(XLogger::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__)
#define XLOG_DEBUG(...) SPDLOG_LOGGER_CALL(XLogger::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__)
#define XLOG_INFO(...) SPDLOG_LOGGER_CALL(XLogger::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__)
#define XLOG_WARN(...) SPDLOG_LOGGER_CALL(XLogger::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__)
#define XLOG_ERROR(...) SPDLOG_LOGGER_CALL(XLogger::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__)

int main()
{
	// print log test, you can transfer any param to do format
	int param = 1;

	XLOG_TRACE("this is trace log record, param: {}", ++param); // int type param is ok
	XLOG_DEBUG("this is debug log record, param: {}", ++param);
	XLOG_INFO("this is info log record, param: {}", ++param);
	XLOG_WARN("this is warn log record, param: {}", double(++param)); // double type param is ok
	XLOG_ERROR("this is error log record, param: {}", std::to_string(++param)); // string type param is ok

	return 0;
}
  • 支持打印到控制台和文件两种方式
  • 采用单例模式,多种日志级别的宏调用封装
  • 格式化打印,尽可能的打印调试所需信息
  • 可以选择将每一行都立即刷新到文件,但要注意级别,尽量不要在info或者debug级别上开启,这个选项会导致日志刷新时间戳延迟

输出

例如,日志文件:test_20201230_154656.log

2020-12-30 15:46:56.681969 <thread 91920> [debug] [main.cpp:135] this is debug log record, param: 2
2020-12-30 15:46:56.681969 <thread 91920> [info] [main.cpp:136] this is info log record, param: 3
2020-12-30 15:46:56.682457 <thread 91920> [warning] [main.cpp:137] this is warn log record, param: 4
2020-12-30 15:46:56.682457 <thread 91920> [error] [main.cpp:138] this is error log record, param: 5

spdlog是一个快速、异步的C++日志,支持多线程和跨平台,具有简单易用的接口和高性能的日志记录能力。以下是spdlog的一些主要特点: 1. 快速:spdlog使用高效的缓冲区实现快速的日志记录,可以轻松处理高负载的日志记录场景。 2. 异步:spdlog支持异步日志记录,可以将日志写入缓冲区后立即返回,不会阻塞主线程。 3. 多线程:spdlog可以安全地在多个线程中使用,支持多个线程同时进行日志记录,且不需要额外的同步机制。 4. 跨平台:spdlog可以在多个平台上运行,包括Windows、Linux、OS X等。 5. 简单易用:spdlog提供简洁明了的API,可以轻松地实现日志记录功能。 以下是spdlog的使用示例: ```cpp #include "spdlog/spdlog.h" void log_example() { // 创建一个控制台日志记录器 auto console = spdlog::stdout_color_mt("console"); // 创建一个文件日志记录器 auto file = spdlog::basic_logger_mt("file_logger", "logs/mylogfile.txt"); // 设置日志记录级别 console->set_level(spdlog::level::info); file->set_level(spdlog::level::trace); // 记录日志 console->info("Hello, spdlog!"); file->trace("This is a trace message."); } ``` 以上代码演示了如何创建一个控制台日志记录器和一个文件日志记录器,并设置不同的日志记录级别,最后分别记录了一条信息和一条跟踪信息。 更多关于spdlog的使用说明,请参考spdlog的官方文档。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值