您好,欢迎来到叨叨游戏网。
搜索
您的当前位置:首页spdlog日志库学习

spdlog日志库学习

来源:叨叨游戏网

spdlog的使用

单例模式

单例模式(Singleton Pattern)是一种常用的设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式用于控制对某个共享资源的访问,确保在应用程序的生命周期内只有一个实例存在。

单例模式的关键特点

单例模式的实现步骤

以下是单例模式的基本实现步骤:

  1. 私有构造函数:将类的构造函数设为私有,防止其他代码直接创建实例。
  2. 静态实例变量:创建一个静态变量用于保存类的唯一实例。
  3. 静态方法:提供一个公共静态方法来获取单例实例。在这个方法中,只在实例为 nullptr 时创建新实例。同时要确保线程安全(尤其是在多线程环境下)。
  4. 禁止拷贝和赋值:通常会删除拷贝构造函数和赋值操作符,防止复制实例。
#include <iostream>  
#include <mutex>  

class Singleton {  
public:  
    // 获取单例实例的静态方法  
    static Singleton& getInstance() {  
        static Singleton instance; // 饿汉式,保证线程安全  
        return instance;  
    }  

    // 公共方法供外部使用  
    void showMessage() {  
        std::cout << "Hello, Singleton!" << std::endl;  
    }  

private:  
    // 私有构造函数  
    Singleton() {  
        std::cout << "Singleton created!" << std::endl;  
    }  

    // 禁止拷贝构造函数和赋值操作符  
    Singleton(const Singleton&) = delete;  
    Singleton& operator=(const Singleton&) = delete;  
};  

int main() {  
    // 获取单例实例并调用方法  
    Singleton::getInstance().showMessage();  

    return 0;  
}

线程安全

在多线程环境下,单例模式的实现需要特别小心。一个常见的方式是使用 std::mutex 进行加锁,以确保初始化的线程安全。但更简单的实现是使用局部静态变量,这在 C++11 及以上版本中是线程安全的,因为其构造保证了只会被调用一次

spdlog使用

spdlog.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();
    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;
    }// 获取logger实例的静态方法。
public:
    std::shared_ptr<spdlog::logger> getLogger()
    {
        return m_pfilelogger;
    } // 返回logger的方法,返回一个指向spdlog::logger的共享指针,供外部使用和调用日志。
    
private:
    static void freeLoggerPtr();
    void unInit();
    void initEx();
    
private:
    static std::mutex m_mutex;  //静态互斥量
    static CLogger* m_plogger;  //静态指针,用于存储唯一的Clogger实例
    std::shared_ptr<spdlog::logger>  m_pfilelogger;  
};



 
// 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);  //注册清理函数,在程序退出时调用freeLoggerPtr。
}
 
CLogger::~CLogger()
{
	unInit();
}
 
void CLogger::freeLoggerPtr()
{
	if (m_pLogger) {
		delete m_pLogger;
	}
}
 
void CLogger::unInit()
{
	spdlog::drop_all();   //spdlog提供的函数,用于清理所有的日志记录器和释放相关资源。
}
 
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);   //rotating_file_sink_mt是spdlog提供的多线程安全的日志记录器。
 
		
		pFileLogger->set_pa
            ttern("%Y-%m-%d %H:%M:%S.%f <thread %t> [%l] %v");   //设置格式,前面是时间戳,<thread %t>打印线程ID,[%l] 打印线程级别 %v打印日志内容
        
		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);
 
		
		pUdpLogger->set_pattern("%Y-%m-%d %H:%M:%S.%f <thread %t> [%l] [%@] %v"); 
		pUdpLogger->set_level(spdlog::level::info);
 
		std::vector< spdlog::sink_ptr> sinks = { pFileLogger, pUdpLogger };
        
#else
		std::vector< spdlog::sink_ptr> sinks = { pFileLogger };  //创建一个 sinks 容器,将 pFileLogger(文件日志记录器)添加到其中
        
#endif
 
		m_pFileLogger = std::make_shared<spdlog::logger>("uvLinkLogger", sinks.begin(), sinks.end());
        //创建 Logger: 使用 sinks 向量创建一个新的 logger 对象 uvLinkLogger
		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;
        //捕获 spdlog 抛出的初始化异常,并输出错误信息。这有助于调试初始化日志系统时出现的问题。
	}
}

基本概念

核心组件

  • Registry (日志记录器注册表) 用于管理所有已创建的Logger对象
  • logger (日志记录器) 负责记录日志消息,可以根据需要创建多个Logger对象。
  • Sink(日志输出) Sink是日志输出的位置,每个Logger内容含一个sink组成的vector
  • Formatter(日志格式化) Formatter用于格式化日志消息的输出格式

日志格式

//代码示例
#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);   //打开一个名为log.txt的文件,模式为追加(std::ios::app)。
    
}

std::time_t now = std::time(nullptr);  //获取当前时间。
std::string dt = std::ctime(&now);   //ctime将时间转换为字符串
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;  
}

//格式化日志消息
auto formatted_message = fmt::format(format,args...); //使用 fmt::format 来格式化日志消息
auto log_entry = fmt::format("[{} {}] {}\n", dt,level_str, formatted_message); //log_entry 是最终的日志条目,包含时间戳、日志级别和格式化后的消息

log_file << log_entry;  
log_file.close();

if (!log_file.is_open()) {  
    std::cerr << "Failed to open log file!" << std::endl;  
}

int main() {  
    log_message(INFO, "This is an info message.");  
    log_message(ERROR, "Error code: {}. Error message: {}", 404, "Not Found");  
    return 0;  
}
[Sat Mar  2 14:59:10 2024 INFO] This is an info message.  
[Sat Mar  2 14:59:10 2024 ERROR] Error code: 404. Error message: Not Found

日志级别

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

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

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

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

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

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

#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
    auto logger = spdlog::stdout_color_mt("console");
    //spdlog::stdout_color_mt 创建了一个多线程安全的 (multi-thread) 彩色输出 logger,命名为 "console"。这里使用 auto 关键字自动推断 logger 的类型。
	//这个 logger 将输出日志消息到控制台,并且消息将以不同的颜色显示,取决于日志级别。
    
    logger->set_level(spdlog::level::warn);
 	//set_level 方法设置了这个 logger 的日志级别为 warn,这意味着只有警告级别及以上的日志消息会被记录并输出。spdlog::level::warn 表示日志级别为警告,因此,trace 和 debug 的日志消息将不会被输出。
    
    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;
}

sink和logger的区别

sinkloggerspdlog 库中两个核心概念,它们承担不同的角色并且各自有不同的功能。以下是这两个概念的区别:

1. Definition (定义)

  • Sink (输出目标)

    :

    • Sink 是日志消息的输出目标。它负责将生成的日志消息写入特定的地方,比如控制台、文件、网络等。
    • Sink 可以有多种类型,如:
      • spdlog::sinks::stdout_sink_mt:将日志输出到控制台标准输出。
      • spdlog::sinks::daily_file_sink_mt:将日志输出到每日日志文件。
      • spdlog::sinks::rotating_file_sink_mt:将日志输出到滚动文件,支持设置最大文件大小和文件数量。
  • Logger (记录器)

    :

    • Logger 是日志系统的主要接口,用户通过 Logger 来记录日志消息。它是一个创作和级别控制的枢纽。
    • Logger 可以使用一个或多个 Sink,将日志消息传递给它们。
    • Logger 负责处理不同的日志级别(如 debug、info、warn、error、critical)、格式化输出、以及决定哪些消息应该被记录(基于设定的日志级别)。

2. 角色和功能

  • Sink:
    • 主要负责日志消息的写入方式和目标,即“往哪里去”。
    • 可以存在多个 sink,支持同时向多个输出目标发送日志。
    • 处理具体的输出逻辑,比如文件到达滚动应创建新文件,如何处理文件名等。
  • Logger:
    • 主要负责提供接口,使得用户可以方便地记录和格式化日志消息。
    • 管理日志级别,决定是否输出特定级别的日志消息。
    • 可以与一个或多个 sinks 关联,并将日志消息传递给它们。

3. 代码示例

为了更好理解它们的区分,下面是一个简单的示例,展示如何在 spdlog 中使用 sink 和 logger。

cpp#include "spdlog/spdlog.h"  
#include "spdlog/sinks/daily_file_sink.h"  

int main() {  
    // 创建一个每天自动滚动的日志 sink  
    auto sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("my_log.log", 23, 59);  

    // 创建一个 logger,并绑定到创建的 sink  
    auto logger = std::make_shared<spdlog::logger>("daily_logger", sink);  

    // 设置日志级别  
    logger->set_level(spdlog::level::info);  

    // 记录一些日志信息  
    logger->info("This is an info message.");  
    logger->warn("This is a warning message.");  

    return 0;  
}  

在这个示例中:

  • sink 是一个指向 daily_file_sink_mt 的共享指针,它负责将日志写入到 "my_log.log" 文件中。
  • logger 是一个指向 logger 实例的共享指针,它使用上述 sink 来记录日志。通过 logger,你可以调用记录不同日志级别的方法(例如 infowarn)来记录消息。

总结

  • Sink 是日志消息的输出目标,负责具体的写入逻辑。
  • Logger 是用户记录日志的接口,负责处理消息的记录和级别管理。
  • Logger 可以关联一个或多个 sinks,以实现灵活的日志记录方案。

创建多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;
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- gamedaodao.net 版权所有 湘ICP备2024080961号-6

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务