c++日志库实战——spdlog,是不是感觉log4cxx有点笨重,不妨试一试spdlog

  • 背景
  • 更新记录
  • spdlog是什么
  • spdlog快速入门
    • 编译
      • CMake手动方式
      • Vcpkg全自动方式(推荐)
    • 原生用法
  • 实战代码
    • SpdlogWarper
    • 使用
  • 常见问题
    • 打印行号
    • 怎么控制台看不到log
    • 同时输出控制台和文件
    • 文件按天分割
    • 停止调试log没有写文件?
    • 完整代码
  • 附录
    • CMakeList.txt
  • 关于

背景

在最近新入职同事的推荐下,作者在一个小工具中学习和使用了spdlog,且已发布到线上运行,以下是学习记录。

更新记录

  • 2021.05.14 增加封装spdlog头文件,快速集成到项目
  • 2021.05.14 通过vcpkg编译安装,1秒集成使用,推荐

spdlog是什么

Fast C++ logging library
按照官方介绍,是一个高性能的C++日志组件,支持跨平台,兼容 C++11。原来项目中使用的是log4cxx,我感觉稍微有点笨重,并且很久没有更新了。

在新项目中,我只需要一款轻量级的日志组件,能:

  • 存文件
  • 按照天数切分

快速的浏览了spdlog,满足我的需求,于是开搞!

spdlog快速入门

github:https://github.com/gabime/spdlog

以下内容来自spdlog的 README

编译

CMake手动方式

$ git clone https://github.com/gabime/spdlog.git
$ cd spdlog && mkdir build && cd build
$ cmake .. && make -j

PS:使用cmake来编译,cmake 命令会生成makefile。如果机器上没有cmake,请先安装一下,我的cmake3.14.5macos 10.15

meki-mac-pro:~ xuyc$ cmake -version
cmake version 3.14.5CMake suite maintained and supported by Kitware (kitware.com/cmake).

Vcpkg全自动方式(推荐)

首先,确保安装了vcpkg,可以参见 Github文档

  1. 安装spdlog包
$ vcpkg search spdlog # 搜索
$ vcpkg install spdlog # 下载,编译,VS2017中会自动发现
  1. 配置CMake,使用Vcpkg
  • CentOs/Ubuntu 使用 Clion IDE的配置见:vcpkg-with-clion
  • Windows下VS2017的配置见:vcpkg-with-visual-studio-cmake-projects。

附(vcpkg更多install语法):

# windows 下使用静态库(mt运行时)
$ vcpkg install spdlog:x86-windows-static-mt
# windows 下使用静态库(md运行时)
$ vcpkg install spdlog:x86-windows-static-md

原生用法

#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"int main()
{spdlog::info("Welcome to spdlog!");spdlog::error("Some error message with arg: {}", 1);spdlog::warn("Easy padding in numbers like {:08d}", 12);spdlog::critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);spdlog::info("Support for floats {:03.2f}", 1.23456);spdlog::info("Positional args are {1} {0}..", "too", "supported");spdlog::info("{:<30}", "left aligned");spdlog::set_level(spdlog::level::debug); // Set global log level to debugspdlog::debug("This message should be displayed..");    // change log patternspdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");// Compile time log levels// define SPDLOG_ACTIVE_LEVEL to desired levelSPDLOG_TRACE("Some trace message with param {}", 42);SPDLOG_DEBUG("Some debug message");// Set the default logger to file loggerauto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");spdlog::set_default_logger(file_logger);
}

官方的代码大概知道怎么使用spdlog了,点个赞。但是可能会遇到一些问题,下面笔者遇到问题的记录。

实战代码

SpdlogWarper

  1. log.h
#ifndef _LOG_0B0512CC_B1CC_4483_AF05_1914E7F7D4DA_
#define _LOG_0B0512CC_B1CC_4483_AF05_1914E7F7D4DA_#include <string>
#include <corecrt_io.h>#ifndef SPDLOG_TRACE_ON
#define SPDLOG_TRACE_ON
#endif#ifndef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG_ON
#endif#ifdef _WIN32
#define __FILENAME__ (strrchr(__FILE__, '\\') ? (strrchr(__FILE__, '\\') + 1):__FILE__)
#else
#define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1):__FILE__)
#endif#include "zim/zim_dll.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"namespace zim {class ZIM_DLL_API ZLogger {public:auto GetLogger() {return nml_logger;}ZLogger();~ZLogger();ZLogger(const ZLogger&) = delete;ZLogger& operator=(const ZLogger&) = delete;private:std::shared_ptr<spdlog::logger> nml_logger;};
}
ZIM_DLL_API zim::ZLogger& GetInstance();#define SPDLOG_LOGGER_CALL_(level, ...) GetInstance().GetLogger()->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
#define LogTrace(...)  SPDLOG_LOGGER_CALL_(spdlog::level::trace,__VA_ARGS__)
#define LogDebug(...)  SPDLOG_LOGGER_CALL_(spdlog::level::debug,__VA_ARGS__)
#define LogInfo(...)   SPDLOG_LOGGER_CALL_(spdlog::level::info,__VA_ARGS__)
#define LogWarn(...)   SPDLOG_LOGGER_CALL_(spdlog::level::warn,__VA_ARGS__)
#define LogError(...)  SPDLOG_LOGGER_CALL_(spdlog::level::err,__VA_ARGS__)
#define LogCritical(...) SPDLOG_LOGGER_CALL_(spdlog::level::critical,__VA_ARGS__)
#define LogCriticalIf(b, ...)               \do {                                           \if ((b)) {                           \SPDLOG_LOGGER_CALL_(spdlog::level::critical,__VA_ARGS__); \}                                      \} while (0)#ifdef WIN32
#define errcode WSAGetLastError()
#endif#endif//_LOG_0B0512CC_B1CC_4483_AF05_1914E7F7D4DA_
  1. Log.cpp
#include "pch.h"
#include "Log.h"zim::ZLogger& GetInstance() {static zim::ZLogger m_instance;return m_instance;
}namespace zim {ZLogger::ZLogger() {if (::_access("logs", 0) == -1) {::_mkdir("logs");}//设置为异步日志//spdlog::set_async_mode(32768);  // 必须为 2 的幂std::vector<spdlog::sink_ptr> sinkList;#if 1auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();consoleSink->set_level(spdlog::level::debug);//consoleSink->set_pattern("[multi_sink_example] [%^%l%$] %v");//consoleSink->set_pattern("[%m-%d %H:%M:%S.%e][%^%L%$]  %v");consoleSink->set_pattern("%Y-%m-%d %H:%M:%S [%l] [%t] - <%s>|<%#>|<%!>,%v");sinkList.push_back(consoleSink);
#endifauto dailySink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/log.log", 2, 30);dailySink->set_level(spdlog::level::debug);sinkList.push_back(dailySink);nml_logger = std::make_shared<spdlog::logger>("both", begin(sinkList), end(sinkList));//register it if you need to access it globallyspdlog::register_logger(nml_logger);// 设置日志记录级别
#ifdef _DEBUGnml_logger->set_level(spdlog::level::trace);
#elsenml_logger->set_level(spdlog::level::info);
#endif//设置当出发 err 或更严重的错误时立刻刷新日志到  disk .nml_logger->flush_on(spdlog::level::warn);spdlog::set_pattern("%Y-%m-%d %H:%M:%S [%l] [%t] - <%s>|<%#>|<%!>,%v");spdlog::flush_every(std::chrono::seconds(2));}ZLogger::~ZLogger() {spdlog::drop_all();}
}

使用

#include "Log.h"int main(){LogTrace("trach");// 和其他日志库最大的区别所在,也是灵魂所在// 自动识别类型,避免%d,%s类型错误,输出不了内容或者崩溃LogDebug("cmd_id={},bodyLen={}", 1, 2);LogInfo("user_id={},app_id={},domainId={},ip={},port={}", 222, 222, 222, "127.0.0.1", 8888);// log.cpp中是flush on Warn,所以,这一条日志打印后,才刷到文件LogWarn("bad packet");LogError("error");return 0;
}

常见问题

打印行号

// 先设置日志输出格式
// %s:文件名,my_file.cpp
// %#:行号,123
// %!:函数名,my_func
spdlog::set_pattern("%Y-%m-%d %H:%M:%S [%l] [%t] - <%s>|<%#>|<%!>,%v");// 使用宏才会有行号
SPDLOG_DEBUG("Some debug message");spdlog::info("Welcome to spdlog!");

具体见:https://github.com/gabime/spdlog/wiki/3.-Custom-formatting

推荐写法

#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__)DEBUG("debug");
LOG("info");

怎么控制台看不到log

设置默认logger为控制台即可

// 设置默认logger,这里是控制台,所以spdlog::info的内容会输出到控制台
auto console = spdlog::stdout_color_mt("console");
spdlog::set_default_logger(console);

官方代码

#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void stdout_example()
{// create color multi threaded loggerauto console = spdlog::stdout_color_mt("console");    //auto err_logger = spdlog::stderr_color_mt("stderr");    spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
}

同时输出控制台和文件

  1. 先注册
// 每天2:30 am 新建一个日志文件
auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// 遇到warn flush日志,防止丢失
logger->flush_on(spdlog::level::warn);
  1. 通过宏来同时输出console和文件,注意logger名字和上面的对应。
// spd 带行号的打印,同时输出console和文件
#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_DEBUG(spdlog::get("daily_logger"), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_INFO(spdlog::get("daily_logger"), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_WARN(spdlog::get("daily_logger"), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_ERROR(spdlog::get("daily_logger"), __VA_ARGS__)

文件按天分割

#include "spdlog/sinks/daily_file_sink.h"
void daily_example()
{// Create a daily logger - a new file is created every day on 2:30amauto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}

停止调试log没有写文件?

spdlog为了提高性能,降低对磁盘的写操作,通过flush机制来一次性把日志写入到文件里面持久化。所以如果没有恰当的配置,停止调试或者进程崩溃的时候会有日志丢失的问题。

定时flush到文件:

//每三秒刷新一次
spdlog::flush_every(std::chrono::seconds(3));

遇到error级别,立即flush到文件:

enum level_enum
{trace = SPDLOG_LEVEL_TRACE, // 最低debug = SPDLOG_LEVEL_DEBUG,info = SPDLOG_LEVEL_INFO,warn = SPDLOG_LEVEL_WARN,err = SPDLOG_LEVEL_ERROR,critical = SPDLOG_LEVEL_CRITICAL, // 最高off = SPDLOG_LEVEL_OFF,n_levels
};auto logger = spdlog::daily_logger_mt("daily_logger", "log/daily.txt", 2, 30);
// 遇到warn或者更高级别,比如err,critical 立即flush日志,防止丢失
logger->flush_on(spdlog::level::warn);

完整代码

// spdlog
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include <iostream>
#include <memory>// spd 带行号的打印,同时输出console和文件
#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_DEBUG(spdlog::get("daily_logger"), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_INFO(spdlog::get("daily_logger"), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_WARN(spdlog::get("daily_logger"), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_ERROR(spdlog::get("daily_logger"), __VA_ARGS__)int main(int argc, char *argv[]) {// 按文件大小//auto file_logger = spdlog::rotating_logger_mt("file_log", "log/log.log", 1024 * 1024 * 100, 3);// 每天2:30 am 新建一个日志文件auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);// 遇到warn flush日志,防止丢失logger->flush_on(spdlog::level::warn);//每三秒刷新一次spdlog::flush_every(std::chrono::seconds(3));// Set the default logger to file loggerauto console = spdlog::stdout_color_mt("console");spdlog::set_default_logger(console);spdlog::set_level(spdlog::level::debug); // Set global log level to debug// change log pattern// %s:文件名// %#:行号// %!:函数名spdlog::set_pattern("%Y-%m-%d %H:%M:%S [%l] [%t] - <%s>|<%#>|<%!>,%v");LOG("test info");ERROR("test error");// Release and close all loggersspdlog::drop_all();
}

控制台输出

/Users/xuyc/repo/sd_linux/cmake-build-debug/sd_linux
[2020-04-30 16:14:41.816] [console] [info] [main.cpp:608] test info
[2020-04-30 16:14:41.816] [console] [error] [main.cpp:609] test errorProcess finished with exit code 0

文件

附录

网上说只需要头文件即可,不过我还是链接了。。。

CMakeList.txt

cmake_minimum_required(VERSION 3.15)
project(sd_linux)set(CMAKE_CXX_STANDARD 14)
AUX_SOURCE_DIRECTORY(./ SRC_LIST)
AUX_SOURCE_DIRECTORY(./jsoncpp SRC_LIST)// 包含spdlog的头文件
INCLUDE_DIRECTORIES(./jsoncpp ./spdlog/include)
// 包含spdlog的动态库目录
LINK_DIRECTORIES(./ ./spdlog/build)add_executable(sd_linux ${SRC_LIST})// 链接spdlog动态库
TARGET_LINK_LIBRARIES(sd_linux curl iconv spdlog)

关于

推荐下自己的开源IM,纯Golang编写:

CoffeeChat:
https://github.com/xmcy0011/CoffeeChat
opensource im with server(go) and client(flutter+swift)

参考了TeamTalk、瓜子IM等知名项目,包含服务端(go)和客户端(flutter),单聊和机器人(小微、图灵、思知)聊天功能已完成,目前正在研发群聊功能,欢迎对golang和跨平台开发flutter技术感兴趣的小伙伴Star加关注。

————————————————
版权声明:本文为CSDN博主「许非」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xmcy001122/article/details/105665732

c++日志库实战——spdlog,是不是感觉log4cxx有点笨重,不妨试一试spdlog相关推荐

  1. linux下编译和安装log4cxx,RedHat如何安装log4cxx日志库

    log4cxx日志库是一种动态库,用于记录c++的日志,那么RedHat系统下要如何安装log4cxx日志库呢?下面小编就给大家介绍下RedHat安装log4cxx日志库的步骤,感兴趣的朋友不妨来了解 ...

  2. spdlog日志库的封装使用

    文章目录 前言 spdlog的基本使用 spdlog日志库的封装 前言 编码过程中,日志是必要的一个组件,我们选择使用哪个日志库呢? 参考:15 best C++ Logging libraries ...

  3. 日志服务Python消费组实战(三):实时跨域监测多日志库数据

    解决问题 使用日志服务进行数据处理与传递的过程中,你是否遇到如下监测场景不能很好的解决: 特定数据上传到日志服务中需要检查数据内的异常情况,而没有现成监控工具? 需要检索数据里面的关键字,但数据没有建 ...

  4. spdlog日志库说明文档(超详细)

    spdlog日志库说明文档(超详细) spdlog是一个开源.快速.只有头文件的C++11日志库,code地址在https://github.com/gabime/spdlog,基础示例在https: ...

  5. 【c++】SPDLOG动态库和静态库、异步日志库hang 问题、registry核心类

    spdlog 官方支持动态库和静态库 静态库 SPDLOG_COMPILED_LIB SPDLOG_API 这个就是空的 静态库里 SPDLOG_INLINE 也是空的: 动态库: SPDLOG_SH ...

  6. Go语言学习之11 日志收集系统kafka库实战

    本节主要内容: 1. 日志收集系统设计 2. 日志客户端开发 1. 项目背景     a. 每个系统都有日志,当系统出现问题时,需要通过日志解决问题     b. 当系统机器比较少时,登陆到服务器上查 ...

  7. spdlog 日志库学习,简易封装

    spdlog wiki:https://github.com/gabime/spdlog/wiki 别人的学习笔记:https://www.cnblogs.com/oucsheep/p/8426548 ...

  8. java applog_Java Web App: 选择与配置日志库

    Java世界里,日志库就和许多其他库[1]一样,你有多个选择,多个还不错的选择,比如log4j, java.util.logging, logback, 另外还有一些统一的log api,比如slf4 ...

  9. 号称C/C++高性能日志库

    ** 以下都是号称高性能日志库 ** 腾讯 C++日志库:Mars XLOG https://github.com/Tencent/mars 美团 C/C++日志库 https://github.co ...

最新文章

  1. 幸运数字Ⅱ(树型结构构造答案,打表)难度⭐⭐
  2. Python3 调用ffmpeg
  3. python写了代码_Python写代码的用法建议
  4. 使用最新目标跟踪框mmtracking实现自己的目标跟踪项目
  5. linux 操作mysql 数据库命令_在Linux上用命令怎么连接数据库
  6. 阿里云服务器被挖矿病毒minerd***的解决方法
  7. Linux下配置LVM
  8. mysql范式与反范式_MySQL 三种范式以及反范式 | 剑花烟雨江南
  9. 我发誓:下辈子再也不学JavaScript了
  10. 蓝牙最新版本6.0_低功耗蓝牙的有趣事实
  11. python经济统计_给统计人讲python(3)模拟城市_数据分析
  12. [C/C++] ccpuid:CPUID信息模块 V1.02版,支持Mac OS X,支持纯C,增加CPUF常数
  13. 使用SQL语句创建数据库
  14. Tkinter:事件绑定
  15. win10添加计算机语言,win10输入法,详细教您怎么在win10里添加输入法
  16. python接私活王者_Python从青铜到王者这5个实战项目要会
  17. 运维工程师的日常工作内容
  18. oracle apex接口文件,Oracle_APEX开发指南
  19. 单链表-荷兰国旗问题
  20. 程序化那么神秘,究竟在做什么事情

热门文章

  1. Electron 使用Pepper Flash插件
  2. 算法与数据结构1800题 之栈和队列 (一)
  3. t2_Deciphering the Market_ticklabels_sma_ewma_apo_macd_Bollinger_Momentum_statsmodels_adfuller_ARIMA
  4. 微信怎样加入精准粉丝
  5. 程序员哪有朝九晚六的?朝九晚六只是形式
  6. 学习Linux的常见故障(待更新)
  7. 网易云信完成聊天的案例
  8. Estimate in progress using
  9. springboot线程中获取bean
  10. Ubuntu 网页浏览器 谷歌浏览器 下载使用