spdlog一个非常好用的C++日志库(一): 简介与使用

目录

1.简介

2.使用

3.基本概念

3.1.核心组件

3.2.日志格式

3.3.日志级别

4.spdlog 快速上手

4.1.Logger创建

4.1.创建控制台 Logger

4.2.创建基本文件 Logger

4.3.创建滚动文件 Logger

4.4.创建每日 Logger

4.5.创建异步日志

4.2.Sink创建

4.2.1.创建控制台 sink

4.2.2.创建文件 sink

4.2.3.创建每日文件 sink

4.2.4.创建滚动文件 sink

4.2.5.创建流输出 sink

4.2.6.创建单 sink 记录器

4.2.7.创建多 sink 记录器

4.2.8.创建共用 sink 记录器

4.3.Logger 注册与获取

4.3.1.Logger 注册

4.3.2.Logger 删除

4.4.Logger 的使用与设置

4.4.1.设置默认 Logger

4.4.2.设置日志级别

4.4.3.设置缓存刷新策略

5.spdlog 使用进阶

5.1.记录自定义类型

5.2.记录 vector 中数据

5.3.记录C语言原生数组

5.4.记录运行时间

5.5.记录十六进制数据

5.6.记录文件名及行号

6.其他特殊 Logger

6.1.qt sink

6.2.msvc sink

6.3.消息过滤 sink

6.4.ringbuffer sink

6.5.udp sink

6.6.支持回调的 Logger

7.总结

8.相关资源推荐


1.简介

        spdlog是一个开源的C++日志库,它提供了高性能和易用性的日志记录功能。它是为了满足现代C++应用程序的需要而创建的,可以在不同的平台上运行。

        spdlog 是一个快速、异步的、header-only 的 C++ 日志库。它提供了简单易用的 API 并具有高性能和可扩展性。

        它的主要特点和功能:

  • 高性能:spdlog设计用于高性能日志记录,支持异步日志记录,可以减少日志对应用程序性能的影响。
  • 简单易用:spdlog的API设计简洁,易于集成和使用。
  • 丰富的日志级别:支持多种日志级别,如TRACE、DEBUG、INFO、WARN、ERROR、CRITICAL等。
  • 多种日志输出:支持多种日志输出方式,包括控制台、文件、系统日志等。
  • 自定义格式:可以自定义日志输出格式。
  • 定时日志轮转:支持定时轮转日志文件。
  • 支持异步模式(可选):提供可选的异步日志记录机制,能够将日志操作放入后台线程执行,避免阻塞主线程。
  • 线程安全:对于多线程环境,spdlog提供了线程安全的日志接口,确保在并发环境下日志记录的正确性和完整性。

2.使用

        在使用时只需要 include 整个 /include/spdlog 文件夹即可。以下是项目中使用到的封装的日志类:

        Logger.h

#ifndef _LOGGER_H_
#define _LOGGER_H_
#include <mutex>
#include <memory>
//#include <QtGlobal>
#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"
#include "spdlog/sinks/udp_sink.h"
#include "spdlog/fmt/bin_to_hex.h"
//#include "ShortWaveConfig.h"

#define   BUFFER_PER_DEAL(buffer, size)   \
		std::vector<char>  buf;            \
		for (int i=0;i<size;i++)    \
		{   \
			buf.push_back(static_cast<char>(buffer[i] & 0xff)); \
		}

class CLogger
{
protected:
	CLogger();
	~CLogger();
    //Q_DISABLE_COPY(CLogger)
    CLogger(const CLogger&) =delete;
    CLogger& operator=(const CLogger&) =delete;

public:
	static CLogger* getInstance()
	{
		if (m_pLogger == nullptr)
		{
			std::unique_lock<std::mutex> lock(m_mutex);
			if (m_pLogger == nullptr)
				m_pLogger = new  CLogger();
		}
		return m_pLogger;
	}

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

private:
	static void freeLoggerPtr();
	void unInit();

	void initEx();

private:
	static std::mutex m_mutex;
	static CLogger* m_pLogger;

	std::shared_ptr<spdlog::logger>  m_pFileLogger;
	//CShortWaveConfig                 m_LogConfig;
};

// use embedded macro to support file and line number
#define UVLOG_TRACE(...)  SPDLOG_LOGGER_CALL(CLogger::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__)
#define UVLOG_DEBUG(...)  SPDLOG_LOGGER_CALL(CLogger::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__)
#define UVLOG_INFO(...)   SPDLOG_LOGGER_CALL(CLogger::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__)
#define UVLOG_WARN(...)   SPDLOG_LOGGER_CALL(CLogger::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__)
#define UVLOG_ERROR(...)  SPDLOG_LOGGER_CALL(CLogger::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__)

/***********************************************************************************
//格式化的例子:
	UVLOG_INFO("print data,{},{}", 1000, "hello!");
	UVLOG_INFO("Hello, {}!", "World");
	UVLOG_INFO("Welcome to spdlog!");
	UVLOG_ERROR("Some error message with arg: {}", 1);
	UVLOG_WARN("Easy padding in numbers like {:08d}", 12);
	UVLOG_INFO("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
	UVLOG_INFO("Support for floats {:03.2f}", 1.23456);
	UVLOG_INFO("Positional args are {1} {0}..", "too", "supported");
	UVLOG_INFO("{:<30}", "left aligned");
**********************************************************************************/
#endif

        Logger.cpp

//#include "stdafx.h"
#include "Logger.h"
#include <iostream>

std::mutex CLogger::m_mutex;
CLogger* CLogger::m_pLogger = nullptr;

CLogger::CLogger()
	: m_pFileLogger(nullptr)
{
	//[1]
	//m_LogConfig.loadConfig();

	//[2]
	//init();
	initEx();

	//[N]
	atexit(freeLoggerPtr);
}

CLogger::~CLogger()
{
	unInit();
}

void CLogger::freeLoggerPtr()
{
	if (m_pLogger) {
		delete m_pLogger;
	}
}

void CLogger::unInit()
{
	spdlog::drop_all();
}

void CLogger::initEx()
{
	try
	{
		auto max_size = 1048576 * 50;
		auto max_files = 3;
        auto pFileLogger = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("logs/test_log.txt", max_size, max_files);

		// custom format
		//pFileLogger->set_pattern("%Y-%m-%d %H:%M:%S.%f <thread %t> [%l] [%@] %v"); // with timestamp, thread_id, filename and line number
		pFileLogger->set_pattern("%Y-%m-%d %H:%M:%S.%f <thread %t> [%l] %v"); // with timestamp, thread_id, filename and line number
		pFileLogger->set_level(spdlog::level::info);
#if  0
		spdlog::sinks::udp_sink_config config(m_LogConfig.m_remoteServerIP, m_LogConfig.m_remoteServerPort);
		auto pUdpLogger = std::make_shared<spdlog::sinks::udp_sink_mt>(config);

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

		std::vector< spdlog::sink_ptr> sinks = { pFileLogger, pUdpLogger };
#else
		std::vector< spdlog::sink_ptr> sinks = { pFileLogger };
#endif

		m_pFileLogger = std::make_shared<spdlog::logger>("uvLinkLogger", sinks.begin(), sinks.end());
		m_pFileLogger->set_level(spdlog::level::info);
		m_pFileLogger->flush_on(spdlog::level::info);
	}
	catch (const spdlog::spdlog_ex& ex)
	{
		std::cout << "Log initialization failed: " << ex.what() << std::endl;
	}
}

测试代码:

#include "Logger.h"
int main() {
    UVLOG_INFO("hello world, {}", "my friend");
    return 0;
}

可以看到,spdlog 库上手使用非常简单。

3.基本概念

3.1.核心组件

spdlog 有以下基本组成部分:

  • Registry(日志记录器注册表):Registry 用于管理所有已创建的 Logger 对象。

  • Logger(日志记录器):Logger 是打印日志的核心对象,负责记录日志消息。可以根据需要创建多个Logger 对象。

  • Sink(日志输出):Sink 是 Logger 的目标输出位置,它指定了日志消息的最终存储位置。每个 Logger 内包含一个 Sink 组成的 vector。

  • Formatter(日志格式化):Formatter 用于格式化日志消息的输出格式。

通过以上组件,可以灵活地配置和使用 spdlog,以满足不同的日志需求。例如,可以创建多个 Logger 对象,并将它们的日志消息输出到不同的文件中;也可以自定义日志消息的格式,添加时间戳和其他元数据。

3.2.日志格式

        在spdlog中,你可以通过设置模式字符串(pattern string)来自定义日志的输出格式。这个模式字符串使用特殊的占位符,每个占位符都代表了日志消息中的一部分,例如时间、日志级别、消息文本等。

        例如,一个常见的模式字符串可能是这样的:

spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] %v");

在这个模式中:

  • %Y-%m-%d %H:%M:%S 代表日期和时间。
  • %l 代表日志级别。
  • %v 代表实际的日志消息文本。

当 spdlog 处理一个日志消息时,它会根据设置的模式字符串来构造最终的输出字符串。这个过程涉及到解析模式字符串,匹配占位符,并用实际的日志数据(如时间、级别、消息等)替换这些占位符,最后将格式化后的字符串输出到日志的目标位置(如控制台、文件等)。

  • 时间相关:cCYDmdHIMSe、 f、 F、 E、 p、 r 、 R 、 T (X)
  • 日期时间相关:星期几(a, A)、月份名称(b/h, B)、时区(z)
  • 线程相关:线程 ID(t)
  • 日志级别相关:日志级别完整名称(l)、日志级别简短名称(L)
  • 消息文本相关:消息文本(v)
  • 进程相关:进程 ID(P)
  • 源代码位置相关:源代码位置(@)、源文件名简短和完整形式(s, g)、源代码行号 (#)和函数名 (!)
  • 时间间隔相关:自上一条日志消息以来的时间间隔,以纳秒 (u)、微秒 (i)、毫秒 (o)和秒 (O)为单位
  • 其他:文字百分号 (%)字符

        这些类型提供了丰富的选项,可以用于构建具有不同格式要求的日志消息。此外 spdlog 还支持用户自定义处理逻辑来处理特定的标志,从而实现更灵活和定制化的日志消息格式化功能。

        假设我们有一个自定义标志%U,表示当前用户的用户名。我们想要在日志消息中包含当前用户的用户名。我们可以按照以下步骤实现:

#include "spdlog/spdlog.h"

#include "spdlog/fmt/ostr.h"
#include "spdlog/fmt/fmt.h"
#include "spdlog/pattern_formatter.h"
#include "spdlog/sinks/basic_file_sink.h"
// 示例函数,获取当前用户名
std::string getCurrentUserName() {
    // 这里应根据你的平台编写相应的代码
    // 例如,在UNIX系统上,可以是 return getlogin();
    return "example_user";
}

// 自定义标志处理器
class CustomFlagFormatter : public spdlog::custom_flag_formatter {
public:
    // 当遇到自定义标志时调用
    void format(const spdlog::details::log_msg&, const std::tm&, spdlog::memory_buf_t& dest) override {
        std::string username = getCurrentUserName();
        dest.append(username.data(), username.data() + username.size());
    }

    // 创建一个自定义标志处理器的副本
    std::unique_ptr<custom_flag_formatter> clone() const override {
        return spdlog::details::make_unique<CustomFlagFormatter>();
    }
};

int main() {
    // 创建一个控制台logger
    auto logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt");

    // 获取默认的格式化器
    auto formatter = std::make_unique<spdlog::pattern_formatter>();

    // 添加自定义标志处理器
    formatter->add_flag<CustomFlagFormatter>('U').set_pattern("[%U] %v");

    // 设置格式化器
    logger->set_formatter(std::move(formatter));

    // 测试日志
    logger->info("This is a test log message");

    return 0;
}

//输出: This is a test log message

通过以上步骤,我们成功地实现了在日志消息中包含当前用户的用户名。这个例子展示了如何使用自定义标志处理器来扩展 spdlog 的功能,以满足特定需求。

使用fmt库实现内容格式化

        spdlog 利用fmt库来执行实际的格式化操作。fmt库支持类型安全的字符串格式化,这意味着它可以在编译时检查格式字符串是否与提供的参数类型匹配。这种类型安全性减少了运行时错误,并提高了代码的稳定性和安全性。

        内部定义的模板类:

template <typename... Args>

inline void info(format_string_t<Args...> fmt, Args &&...args) {

    default_logger_raw()->info(fmt, std::forward<Args>(args)...);

}

例如,当你使用 spdlog 记录一个带有整数和字符串的日志消息时,如下:

spdlog::info("User {} has {} messages", "Alice", 10);

在这个例子中,fmt库负责将"Alice"10这两个参数按照类型安全的方式插入到格式化字符串中,生成最终的日志消息。

改造下我们的代码

#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <fmt/core.h> // 包含 fmt 库的核心功能

enum LogLevel {
    INFO,
    WARNING,
    ERROR
};

// 使用模板和参数包来支持格式化的参数
template <typename... Args>
void log_message(LogLevel level, const std::string& format, Args... args) {
    std::ofstream log_file("log.txt", std::ios::app);
    if (log_file.is_open()) {
        // 获取当前时间
        std::time_t now = std::time(nullptr);
        std::string dt = std::ctime(&now);
        dt.pop_back(); // 移除换行符

        std::string level_str;
        switch(level) {
            case INFO:
                level_str = "INFO";
                break;
            case WARNING:
                level_str = "WARNING";
                break;
            case ERROR:
                level_str = "ERROR";
                break;
        }

        // 使用 fmt::format 格式化日志消息,包括传入的参数
        auto formatted_message = fmt::format(format, args...);
        auto log_entry = fmt::format("[{} {}] {}\n", dt, level_str, formatted_message);

        log_file << log_entry;
        log_file.close();
    } else {
        std::cerr << "Failed to open log file!" << std::endl;
    }
}

// 使用示例
int main() {
    // 使用 INFO 级别记录一条简单消息
    log_message(INFO, "This is an info message.");

    // 使用 ERROR 级别记录一条格式化消息
    log_message(ERROR, "Error code: {}. Error message: {}", 404, "Not Found");

    return 0;
}
> [Sat Mar  2 14:59:10 2024 ERROR] Error code: 404. Error message: Not Found

3.3.日志级别

spdlog 提供了一组日志级别,用于控制记录哪些级别的日志消息:

  • trace:最详细的日志级别,提供追踪程序执行流程的信息。

  • debug:调试级别的日志信息,用于调试程序逻辑和查找问题。

  • info:通知级别的日志信息,提供程序运行时的一般信息。

  • warn:警告级别的日志信息,表明可能发生错误或不符合预期的情况。

  • error:错误级别的日志信息,表明发生了某些错误或异常情况。

  • critical:严重错误级别的日志信息,表示一个致命的或不可恢复的错误。

通过设置日志记录器的级别,可以控制哪些级别的日志进行输出:

#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
    auto logger = spdlog::stdout_color_mt("console");
    logger->set_level(spdlog::level::warn);

    logger->trace("trace message");
    logger->debug("debug message");
    logger->info("info message");
    logger->warn("warn message");
    logger->error("error message");
    logger->critical("critical message");

    return 0;
}

4.spdlog 快速上手

        spdlog 提供了一系列工厂函数用于创建 Logger。其中以 _mt 后缀的表示创建多线程的日志记录器、以 -st 后缀的表示创建单线程的日志记录器。

4.1.Logger创建

4.1.创建控制台 Logger

#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
    auto logger = spdlog::stdout_color_mt("console");
    logger->info("hello world");
    return 0;
}

4.2.创建基本文件 Logger

#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
int main() {
    auto logger = spdlog::basic_logger_mt("file","my_log.log");
    logger->info("hello world");
    return 0;
}

4.3.创建滚动文件 Logger

可以设置日志文件的大小及数量限定:

#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
int main() {
    auto max_size = 1024*2;// 每个文件最大 2 k
    auto max_files = 3;//最多滚动 3 次
    auto logger = spdlog::rotating_logger_mt("file","my_log.log", max_size, max_files);

    for( int i = 0 ;i < 100;++i)
    {
        logger->info("rotating file log test");
    }
    return 0;
}

4.4.创建每日 Logger

可以在每天固定时间点创建一个新的日志文件:

#include "spdlog/spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
int main() {
    auto logger = spdlog::daily_logger_mt("daily_logger", "my_log.log", 0, 0);
    return 0;
}

4.5.创建异步日志

可以与 spdlog::async_factory 搭配使用,以实现异步日志。异步记录可以提高程序的性能,因为日志写入操作不会阻塞主线程。

#include "spdlog/async.h"
#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
    auto logger = spdlog::stdout_color_mt<spdlog::async_factory>("async_logger");
    logger->info("123");
    return 0;
}

4.2.Sink创建

        spdlog 提供的工厂方法封装了 sink 的创建过程,也可以根据需要先创建 sink ,再创建 Logger。sink 是将日志实际写入其目标(文件、控制台、数据库等)的对象,且应仅负责单个目标。

4.2.1.创建控制台 sink

 auto sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();

4.2.2.创建文件 sink

auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("my_log.log");

4.2.3.创建每日文件 sink

auto sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("my_log.log",23,59);

4.2.4.创建滚动文件 sink

auto sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("my_log.log",1048576 * 5, 3,false);

4.2.5.创建流输出 sink

std::ostringstream oss;
auto ostream_sink = std::make_shared<spdlog::sinks::ostream_sink_mt> (oss);

4.2.6.创建单 sink 记录器

可以直接在构造函数中传入 sink 来创建 Logger:

int main() {
    auto sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
    auto logger = std::make_shared<spdlog::logger>("my_logger", sink);
    logger->info("hello world ");
    return 0;
}

4.2.7.创建多 sink 记录器

也可以传入一个 sink 列表来创建  Logger,每个 sink 可单独设置日志级别和样式:

int main() {
    auto sink1 = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
    auto sink2 = std::make_shared<spdlog::sinks::basic_file_sink_mt>("my_log.log");
    spdlog::sinks_init_list sinks = {sink1,sink2};

    auto logger = std::make_shared<spdlog::logger>("my_logger", sinks.begin(),sinks.end());

    logger->info("hello world ");
    return 0;
}

4.2.8.创建共用 sink 记录器

多个 Logger 也可以共用相同的 sink :

int main() {
    auto sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
    auto logger1 = std::make_shared<spdlog::logger>("logger1", sink);
    auto logger2 = std::make_shared<spdlog::logger>("logger2", sink);
    auto logger3 = std::make_shared<spdlog::logger>("logger3", sink);

    logger1->info("hello world ");
    logger2->info("hello world ");
    logger3->info("hello world ");
    return 0;
}

4.3.Logger 注册与获取

spdlog 提供了一个全局注册和获取 logger 的方法。

4.3.1.Logger 注册

使用 spdlog 工厂方法创建的 logger 无需手动注册即可根据名称获取,手动创建的 logger 需要注册。

#include "spdlog/sinks/stdout_color_sinks.h"
#include <spdlog/spdlog.h>

void register_logger()
{
    auto sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
    auto logger = std::make_shared<spdlog::logger>("my_logger", sink);
    spdlog::register_logger(logger);
}
int main() {
    register_logger();
    
    auto logger = spdlog::get("my_logger");
    logger->info("hello world");
    return 0;
}

4.3.2.Logger 删除

手动注册的全局 logger 也可以删除:

spdlog::drop("my_logger");//全局注册中删除指定 logger
spdlog::drop_all();// 删除所有注册的 logger

4.4.Logger 的使用与设置

4.4.1.设置默认 Logger

spdlog 提供了最为便捷的默认 logger,注意,该logger在全局公用:

spdlog::info("hello world");

可以设置自定义的 logger 为全局默认:

auto logger = spdlog::stdout_color_mt("my_log");
spdlog::set_default_logger(logger);

4.4.2.设置日志级别

logger 和 sink 都可以单独指定日志级别。

设置指定 Logger 级别

auto logger = spdlog::stdout_color_mt("my_log");
logger->set_level(spdlog::level::debug);

设置指定 sink 级别

auto sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
sink->set_level(spdlog::level::debug);

设置默认 Logger 级别

spdlog::set_level(spdlog::level::debug);

4.4.3.设置缓存刷新策略

创建好 Logger 后建议设置 flush 方式,否则可能无法立刻在文件中看到 logger 的内容:

定时刷新

spdlog::flush_every(std::chrono::seconds(5));// 定期为所有注册的logger隔5秒刷新

基于级别刷新

auto logger = spdlog::stdout_color_mt("my_log");
logger->flush_on(spdlog::level::warn);//遇到 warn 就立即刷新

手动刷新

auto logger = spdlog::stdout_color_mt("my_log");
logger->flush()// logger 将依次在每个 sink 上调用 flush

5.spdlog 使用进阶

5.1.记录自定义类型

用户自定义类型对象作为日志参数进行记录,需要重置输出运算符:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/ostr.h"

class demo{
public:
    demo(int id_,const std::string &name_):id(id_),name(name_){}

public:
    template<typename OStream>
    friend OStream& operator<<(OStream& os, const demo& d){
        fmt::format_to(std::ostream_iterator<char>(os), "id:{},name:{}", d.id, d.name);
        return os;
    }
private:
    int         id ;
    std::string name;
};

int main() {
    demo d(10,"demo_1");
    auto logger = spdlog::stdout_color_mt("console");
    logger->info("log message {}",d);
    return 0;
}

或者

#include "spdlog/fmt/fmt.h"

//自定义对象的打印功能
struct stSelfDataType
{
	int  m_age;
	std::string m_name;
public:
	stSelfDataType(int age, const std::string& name) : m_age(age), m_name(name) {}
};

template<>
struct fmt::formatter<stSelfDataType> : fmt::formatter<std::string>
{
	auto format(const stSelfDataType& my, format_context& ctx) const -> decltype(ctx.out())
	{
		return format_to(ctx.out(), "[stSelfDataType age={},name={}]", my.m_age, my.m_name.c_str());
	}
};

int main() {
    auto logger = spdlog::stdout_color_mt("console");
    logger->info("user defined type: {}", stSelfDataType(14, "xiaozjongde"));
    return 0;
}

5.2.记录 vector 中数据

vector 对象可以直接作为参数进行输出:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/ranges.h"
int main() {
    auto logger = spdlog::stdout_color_mt("console");
    std::vector<int> vec{1,2,3,4,5};

    logger->info("vector data :{}",vec);
    return 0;
}

输出: vector data : {1,2,3,4,5}

5.3.记录C语言原生数组

spdlog官方是不支持原生数组直接输出的,所以我们需要像记录自定义类型那样来封装一个类,代码如下:

#include <iostream>
#include "spdlog/fmt/ostr.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/ranges.h"

template <typename T, size_t N>
class CCArrayFormat
{
public:
	CCArrayFormat(const T(&data)[N]) : m_data(data) {}

public:
	template<typename OStream>
	friend OStream& operator<<(OStream& os, const CCArrayFormat<T, N>& d) {
		for (int i = 0; i < N; i++) {
			fmt::format_to(std::ostream_iterator<char>(os), "{} ", d.m_data[i]);
		}
		return os;
	}

private:
	const T(&m_data)[N];
};

int main() {
    auto logger = spdlog::stdout_color_mt("console");
    int data[] = {16,23,38,4,5};

    logger->info("array data :{}", CCArrayFormat(data));
    return 0;
}

5.4.记录运行时间

使用 spdlog::stopwatch 对象可以记录代码运行时间:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/stopwatch.h"
#include <thread>

void test()
{
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
int main() {
    auto logger = spdlog::stdout_color_mt("console");
    spdlog::stopwatch sw;
    test();
    logger->info("test run {} seconds",sw);
    return 0;
}

5.5.记录十六进制数据

使用 to_hex 可以把二进制数据转十六进制进行记录:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/bin_to_hex.h"
int main() {
    auto logger = spdlog::stdout_color_mt("console");

    unsigned char data[] = {0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56};
    logger->info("Hex string:  {} ", spdlog::to_hex(std::begin(data), std::begin(data)+sizeof(data)));

    return 0;
}

还可以显示对应 ASCII 值:

int main() {
    auto logger = spdlog::stdout_color_mt("console");

    unsigned char data[] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
    
    logger->info("Hex string:  {:a} ", spdlog::to_hex(std::begin(data), std::begin(data)+sizeof(data),4));
    return 0;
}

还可以显示原生const char* 的十六进制数据:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/bin_to_hex.h"

void logInfo(std::shared_ptr<spdlog::logger> pLogger, const char* data, int len)
{
    //方法1
	pLogger->log("original data is: {}", spdlog::to_hex(std::initializer_list(data, data+len)));

    //或

    //方法2
    pLogger->log("original data is: {}", spdlog::to_hex(std::string_view(data, len)));
}

int main() {
    auto logger = spdlog::stdout_color_mt("console");

    char data[] = {0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56};
    logInfo(logger, data, sizeof(data));

    return 0;
}

5.6.记录文件名及行号

在使用 spdlog 记录日志时,可以通过格式化字符串来包含方法名、行号和文件名的信息:

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
#include <spdlog/spdlog.h>
#include "spdlog/sinks/stdout_color_sinks.h"

int main() {
    spdlog::set_pattern("[%H:%M:%S] [%n] [%^---%L---%$] [%s:%#] [%!] %v");
    auto logger = spdlog::stdout_color_mt("my_log");
    SPDLOG_LOGGER_INFO(logger, "This is a log message");
    return 0;
}

一定确保 SPDLOG_ACTIVE_LEVEL 定义的日志级别低于或等于你期望的日志级别,并且在包含 spdlog.h 之前定义了它。

6.其他特殊 Logger

6.1.qt sink

qt_sink 可以向 QTextBrowser、QTextEdit 等控件输出日志消息:

#include "spdlog/sinks/qt_sinks.h"
auto logger = spdlog::qt_logger_mt("QLogger",ui->textBrowser);
logger->info("hello QTextBrowser");
logger->warn("this msg from spdlog");

6.2.msvc sink

msvc_sink 使用 OutputDebugStringA 向 Windows调试接收器发生日志记录:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/msvc_sink.h"
int main() {
    auto sink = std::make_shared<spdlog::sinks::msvc_sink_mt>();
    auto logger = std::make_shared<spdlog::logger>("msvc_logger", sink);
    logger->info("debug log test...");
    return 0;
}

6.3.消息过滤 sink

dup_filter_sink 可以实现重复消息删除:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/dup_filter_sink.h"
int main() {
    auto sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    auto dup_filter_sink= std::make_shared<spdlog::sinks::dup_filter_sink_mt>(std::chrono::seconds(5));
    dup_filter_sink->add_sink(sink);

    auto logger = std::make_shared<spdlog::logger>("msvc_logger", dup_filter_sink);
    logger->info("hello world");
    logger->info("hello world");
    logger->info("hello world");
    logger->info("hello world");
    logger->info("log msg");
   
    return 0;
}

6.4.ringbuffer sink

ringbuffer_sink 将最新的日志消息保存在内存中:

#include <spdlog/spdlog.h>
#include "spdlog/sinks/ringbuffer_sink.h"
#include "spdlog/fmt/ranges.h"

int main() {
    auto ringbuffer_sink = std::make_shared<spdlog::sinks::ringbuffer_sink_mt>(5);
    auto logger = std::make_shared<spdlog::logger>("ringbuffer_logger", ringbuffer_sink);

    for (int i = 0; i < 20; ++i) {
        logger->info("Log message {}", i);
    }

    std::vector<std::string> log_messages = ringbuffer_sink->last_formatted(1);
    spdlog::info("{}",log_messages);

    return 0;
}

6.5.udp sink

spdlog 提供的一个封装了 UDP 传输的 logger。它可以将日志记录通过 UDP 协议发送到指定的目标地址和端口:

#include <spdlog/spdlog.h>
#include "spdlog/sinks/udp_sink.h"
int main() {
    spdlog::sinks::udp_sink_config cfg("127.0.0.1", 11091);
    auto my_logger = spdlog::udp_logger_mt("udplog", cfg);
    my_logger->info("hello world");
    
    return 0;
}

6.6.支持回调的 Logger

callback_logger_mt 是一个支持设置调用回调函数的日志记录器:

#include <spdlog/spdlog.h>
#include <iostream>
#include "spdlog/sinks/callback_sink.h"
int main() {
    auto logger = spdlog::callback_logger_mt("custom_callback_logger", [](const spdlog::details::log_msg & msg) {
        std::cout << msg.payload.data() << std::endl;
    });

    logger->info("123");
    return 0;
}

7.总结

        spdlog因其高性能、易用性和丰富的功能,在C++开发中常被用作日志记录工具,帮助开发人员更好地追踪代码执行、排查问题、分析性能等。

8.相关资源推荐

github源码地址:GitHub - gabime/spdlog: Fast C++ logging library.

官方wiki文档说明地址:Home · gabime/spdlog Wiki · GitHub

QuickStart(快速学习)地址:QuickStart · gabime/spdlog Wiki · GitHub

  • 21
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
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的官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值