spdlog常用接口及使用

目录

前情提要

一、日志级别接口

二、输出控制(spdlog::set_level)

三、轮转日志器创建(spdlog::rotating_logger_mt)(输出到文件)

1. 功能概述

2. 构造函数参数

3.关键特性

4.代码示例

四、彩色日志记录器(spdlog::stdout_color_mt)(输出控制台)

1. 功能概述

2. 函数签名

3.基础用法

五、自定义日志输出格式(spdlog::set_pattern)

1. 常用占位符

2. 默认格式

3. 彩色日志

4.支持动态调整格式和多日志不同格式

六、刷新策略控制(flush_on)

1. 核心作用

2. 函数签名

3.典型使用场景

场景1:确保错误日志立即写入

场景2:调试时实时查看日志

4. 与 flush_every 的对比

5.底层实现原理

6. 注意事项

作者封装和使用spdlog静态库接口代码示例如下:


前情提要

spdlog源码生成静态库教程https://blog.csdn.net/Mason___/article/details/157584740?spm=1001.2014.3001.5501

有了静态库我们就要搞到项目里用起来,VS2019使用静态库和动态库

https://blog.csdn.net/Mason___/article/details/157472930?spm=1001.2014.3001.5501

加载完了之后就要着手封装调用静态库接口以及使用,这是本文要讲的,代码放在最下面。

一、日志级别接口

spdlog提供6个标准日志级别(按严重程度递增):

  1. trace:最详细的跟踪信息(如变量值、循环迭代、函数调用流程)。
  2. debug:调试阶段信息(如程序状态、分支条件)。
  3. info:常规运行信息(如服务启动、配置加载)。
  4. warn:潜在问题警告(如磁盘空间不足、无效输入)。
  5. error:可恢复的错误(如文件打开失败、网络超时)。
  6. critical:不可恢复的严重错误(如内存耗尽、关键组件崩溃)。

典型使用场景

级别适用场景
trace调试复杂算法、跟踪变量变化、分析性能瓶颈。
debug检查模块初始化、验证中间结果、确认流程分支。
info记录服务启动/停止、配置变更、关键业务事件。
warn提示非致命问题(如使用默认配置、临时降级)。
error记录可恢复的异常(如数据库连接断开后重试)。
critical触发紧急响应(如数据损坏、核心服务崩溃)。

二、输出控制(spdlog::set_level)

  • 级别过滤:低于当前设置级别的日志会被忽略。

    spdlog::set_level(spdlog::level::warn); // 仅输出warn及以上级别
    spdlog::trace("This won't appear");     // 被过滤
    spdlog::warn("This will appear");       // 正常输出
  • 性能优化:低于设置级别的日志不会触发I/O操作(减少开销)。

  • 注意事项:

        (1)生产环境建议:

                开发阶段:使用debugtrace级别。

                生产阶段:使用infowarn级别,减少日志量。

        (2)性能影响

      trace()调用本身开销极低(内联函数),但频繁输出可能影响I/O性能。

                异步日志(async_logger)可缓解此问题。

        (3)格式化支持

                所有级别均支持格式化字符串(如"Value: {}")和占位符(如%l表示级别缩写)。

三、轮转日志器创建(spdlog::rotating_logger_mt)(输出到文件)

1. 功能概述

  • 日志轮转(Rotating):当日志文件达到指定大小后,自动切换到新文件,并保留有限数量的历史文件。
  • 多线程安全:通过 _mt 后缀标识,内部使用锁保证线程安全。
  • 典型用途:需要控制日志文件大小且避免磁盘占满的场景(如长期运行的服务)。

2. 构造函数参数

auto logger = spdlog::rotating_logger_mt(
    "logger_name",       // 日志器名称(用于全局注册)
    "logs/rotating.log", // 日志文件路径
    1024 * 1024 * 5,    // 单个文件最大大小(字节,此处为5MB)
    3                   // 保留的历史文件数量
);
  • 参数说明
    • name:日志器唯一标识,可通过 spdlog::get(name) 获取。
    • filename:当前日志文件路径。
    • max_size:文件轮转的大小阈值(字节)。
    • max_files:保留的历史文件数量(超过则删除最旧的文件)。

3.关键特性

    (1) 自动轮转逻辑

        当前文件达到 max_size 时:

                1.将当前文件重命名为 filename.1,原 .1 改为 .2,依此类推。

                2.创建新的 filename 文件继续写入。

                3.若历史文件数量超过 max_files,删除最旧的文件(如 .3)。

     (2) 线程安全

        内部使用 spdlog::details::thread_pool 处理并发写入,适合多线程环境。

     (3) 异常处理

        若初始化失败(如路径不可写),抛出 spdlog::spdlog_ex 异常:

try {
    auto logger = spdlog::rotating_logger_mt("logger", "invalid_path.log", 1024, 3);
} catch (const spdlog::spdlog_ex& ex) {
    std::cerr << "Logger init failed: " << ex.what() << std::endl;
}

4.代码示例

#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"

int main() {
    // 创建轮转日志器(5MB/文件,保留3个历史文件)
    auto logger = spdlog::rotating_logger_mt("rotating", "logs/app.log", 1024 * 1024 * 5, 3);
    
    // 设置日志级别和格式
    logger->set_level(spdlog::level::debug);
    logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");

    // 记录日志
    for (int i = 0; i < 10000; ++i) {
        logger->info("Log message {}", i);
    }

    return 0;
}

输出文件结构(日志量超过5MB后):

logs/
├── app.log       # 当前日志文件
├── app.log.1     # 第一个历史文件
├── app.log.2     # 第二个历史文件
└── app.log.3     # 第三个历史文件(最旧)

四、彩色日志记录器(spdlog::stdout_color_mt)(输出控制台)

   spdlog::stdout_color_mtspdlog 日志库中用于创建多线程安全的彩色控制台日志记录器的函数。 

1. 功能概述

  • 多线程安全:支持多线程环境下的日志输出。
  • 彩色控制台:自动为不同日志级别(如 infowarnerror)添加颜色,提升可读性。
  • 跨平台支持:在 Linux/macOS 使用 ANSI 转义码,在 Windows 通过 API 控制台颜色。

2. 函数签名

template<typename Factory = spdlog::synchronous_factory>
std::shared_ptr<spdlog::logger> stdout_color_mt(
    const std::string& logger_name, 
    spdlog::color_mode mode = spdlog::color_mode::automatic
);

  参数说明:

      1.  logger_name:日志记录器的唯一标识,用于后续获取或管理该实例(如通过spdlog::get(logger_name))。    

      2. mode(可选,默认为 automatic

            color_mode::automatic:自动检测终端是否支持颜色(如重定向到文件时禁用颜色)。

            color_mode::always:强制启用颜色,即使终端可能不支持(可能导致乱码)。

            color_mode::never:完全禁用颜色。

3.基础用法

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

int main() {
    // 创建默认彩色日志记录器(自动检测颜色支持)
    auto logger = spdlog::stdout_color_mt("console");
    logger->info("Welcome to spdlog!");  // 输出绿色 info 消息
    logger->error("Something went wrong"); // 输出红色 error 消息
}

五、自定义日志输出格式(spdlog::set_pattern)

   spdlog::set_pattern 是用于自定义日志输出格式的核心函数,通过占位符灵活控制时间、级别、线程ID等信息的显示方式。

1. 常用占位符

占位符含义示例
%Y年(4位)2024
%m月(01-12)02
%d日(01-31)02
%H时(00-23)14
%M分(00-59)30
%S秒(00-59)45
%e毫秒(3位)123
%l日志级别(小写)info
%L日志级别(大写)INFO
%t线程ID1234
%s源文件名main.cpp
%#行号42
%v日志内容This is a message
%^级别颜色开始(需配合%$彩色日志(如红色error
%$级别颜色结束恢复默认颜色

2. 默认格式

// 设置全局日志格式(所有日志器默认使用此格式)
    spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v");

    spdlog::info("This is an info message");
    // 输出示例:[2024-02-02 14:30:45.123] [info] [1234] This is an info message

3. 彩色日志

// 设置包含颜色的格式(需包含stdout_color_sink)
spdlog::set_pattern("[%^%l%$] %v");
spdlog::info("This is colored info"); // 默认绿色
spdlog::error("This is colored error"); // 默认红色

4.支持动态调整格式和多日志不同格式

auto logger = spdlog::stdout_color_mt("console");
logger->set_pattern("[%H:%M:%S] %v"); // 简洁格式
logger->info("Simple message");

logger->set_pattern("[%Y-%m-%d] [%l] %v"); // 详细格式
logger->warn("Detailed warning");
// 创建两个日志器,分别设置不同格式
auto file_logger = spdlog::basic_logger_mt("file", "app.log");
file_logger->set_pattern("[%Y-%m-%d %H:%M:%S] %v");

auto console_logger = spdlog::stdout_color_mt("console");
console_logger->set_pattern("[%^%l%$] %v");

file_logger->info("This goes to file");      // 无颜色,含时间戳
console_logger->info("This goes to console"); // 彩色,无时间戳

六、刷新策略控制(flush_on)

  flush_on 是 spdlog 中用于控制日志刷新策略的核心函数,其作用是指定在特定日志级别及以上时强制刷新缓冲区,确保日志立即写入目标(如文件/控制台),避免数据滞留

1. 核心作用

  • 默认行为:spdlog 为提升性能,会先将日志存入内存缓冲区,按条件(如缓冲区满、程序退出)批量写入。
  • flush_on 的功能:强制在记录指定级别及以上日志时,立即刷新缓冲区,确保日志实时落地。

2. 函数签名

void flush_on(spdlog::level::level_enum log_level);
  • 参数log_level(如 spdlog::level::warnspdlog::level::error)。
  • 效果:当记录 log_level 或更高级别日志时,缓冲区会被刷新。

3.典型使用场景

场景1:确保错误日志立即写入
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>

int main() {
    auto logger = spdlog::basic_logger_mt("file_logger", "app.log");
    logger->set_level(spdlog::level::debug);  // 设置全局日志级别
    logger->flush_on(spdlog::level::error);   // 仅在记录 error 及以上时刷新

    logger->info("This info may stay in buffer");  // 不触发刷新
    logger->error("This error will flush immediately"); // 触发刷新,info 也会被写入
}
场景2:调试时实时查看日志
auto debug_logger = spdlog::stdout_color_mt("debug");
debug_logger->flush_on(spdlog::level::debug);  // 所有 debug 及以上日志立即显示
debug_logger->debug("Real-time debug message");  // 立即输出到控制台

4. 与 flush_every 的对比

函数触发条件适用场景
flush_on记录指定级别及以上日志时关键日志(如错误)需实时写入
flush_every固定时间间隔(如每3秒)非关键日志的定期刷新

示例:组合使用

logger->flush_on(spdlog::level::error);  // 错误日志立即刷新
spdlog::flush_every(std::chrono::seconds(5)); // 其他日志每5秒刷新一次

5.底层实现原理

  • 缓冲区管理:spdlog 内部维护一个日志消息队列,flush_on 会修改队列的刷新阈值。
  • 线程安全:在多线程环境下,flush_on 的设置和触发均通过锁保护,确保安全。

6. 注意事项

  • 性能权衡:频繁刷新(如 flush_on(spdlog::level::debug))会降低性能,建议仅对关键级别使用。
  • 与 set_level 的区别
    • set_level 控制哪些日志被记录
    • flush_on 控制已记录的日志何时写入目标

作者封装和使用spdlog静态库接口代码示例如下:

BDLog.h

#pragma once
#include "plccore_global.h"
#include <string>
PLCCORE_EXPORT void InitLogModule(const QString& strLogPath, int level = 0);
 
PLCCORE_EXPORT void BDLogTrace(const std::string& msg);
PLCCORE_EXPORT void BDLogDebug(const std::string& msg);
PLCCORE_EXPORT void BDLogInfo(const  std::string& msg);
PLCCORE_EXPORT void BDLogWarn(const  std::string& msg);
PLCCORE_EXPORT void BDLogError(const std::string& msg);
PLCCORE_EXPORT void BDLogCritical(const std::string& msg);

BDLog.cpp

#include "BDLog.h"
#include <memory>
#include <iostream>
#include <QString>
#include <QTextCodec>
#include <QDir>
#include <algorithm>
#include "../spdlog-1.x/include/spdlog/spdlog.h"
#include "../spdlog-1.x/include/spdlog/common.h"
#include "../spdlog-1.x/include/spdlog/sinks/rotating_file_sink.h"

std::shared_ptr<spdlog::logger> mylogger = nullptr;
std::string ConvertPath(const QString& strPath)
{
    auto path = QDir::toNativeSeparators(strPath);
    auto code = QTextCodec::codecForName("GB2312");
    std::string retpath = code->fromUnicode(path.toStdString().c_str()).data();
    return std::move(retpath);
}

void InitLogModule(const QString& strLogPath, int level/* = 0*/)
{
    try
    {
        QString path = strLogPath.left(strLogPath.lastIndexOf(QLatin1Char('/')));
        QDir dir(path);
        if (!dir.exists()){
            return;
        }
        QStringList filters;
        filters << QString("*.log");
        dir.setFilter(QDir::Files | QDir::NoSymLinks);
        dir.setNameFilters(filters);
        auto fileList = dir.entryInfoList();
        const size_t keep_count = 5;
        if (fileList.size() > keep_count) {
           
            std::partial_sort(fileList.begin(), fileList.begin() + keep_count, fileList.end(),
                [&](const QFileInfoList::value_type& _left, const QFileInfoList::value_type& _right) {
                    return _left.fileName() > _right.fileName();
            });
            for (auto it = fileList.begin() + keep_count; it != fileList.end(); ++it) {
                QFile file(it->absoluteFilePath());
                if (file.exists()) {
                    file.remove();
                }
            }
        }
        // 参数1 日志标识符, 参数2 日志文件名
        mylogger = spdlog::rotating_logger_mt("Log", ConvertPath(strLogPath).c_str(), 1024 * 1024 * 5, 10);
        // 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [数据]
        mylogger->set_pattern("[%n][%Y-%m-%d %H:%M:%S.%e] [%l] [%t]  %v");
        mylogger->set_level((spdlog::level::level_enum)(level));
        mylogger->flush_on(spdlog::level::trace);
    }
    catch (const spdlog::spdlog_ex& ex)
    {
        std::cout << "Log initialization failed: " << ex.what() << std::endl;
    }
}

void BDLogTrace(const std::string& msg)
{
    if (mylogger) {
        mylogger->trace(msg);
    }
}

void BDLogDebug(const std::string& msg)
{
    if (mylogger) {
        mylogger->debug(msg);
    }
}

void BDLogInfo(const  std::string& msg)
{
    if (mylogger) {
        mylogger->info(msg);
    }
}

void BDLogWarn(const  std::string& msg)
{
    if (mylogger) {
        mylogger->warn(msg);
    }
}

void BDLogError(const std::string& msg)
{
    if (mylogger) {
        mylogger->error(msg);
    }
}

void BDLogCritical(const std::string& msg)
{
    if (mylogger) {
        mylogger->critical(msg);
    }
}
      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值