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

c++日志库实战——spdlog,是不是感觉log4cxx有点笨重,不妨试一试spdlogc++日志库实战——spdlog,是不是感觉log4cxx有点笨重,不妨试一试spdlog背景spdlog是什么spdlog快速入门常见问题打印行号怎么控制台看不到log同时输出控制台和文件文件按天分割完整代码附录CMakeList.txt关于背景在最近新入职同事的推荐下,作者在一个小工具中学习和使用了spdlog,且已发布到线上运行,以下是学习记录。spdlog是什么FastC++…

大家好,又见面了,我是你们的朋友全栈君。

背景

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

更新记录

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

spdlog是什么

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

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

  • 存文件
  • 按照天数切分

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

spdlog快速入门

githubhttps://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.5

CMake suite maintained and supported by Kitware (kitware.com/cmake).

Vcpkg全自动方式(推荐)

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

  1. 安装spdlog包
$ vcpkg search spdlog # 搜索
$ vcpkg install spdlog # 下载,编译,VS2017中会自动发现
  1. 配置CMake,使用Vcpkg

附(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 debug
    spdlog::debug("This message should be displayed..");    
    
    // change log pattern
    spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
    
    // Compile time log levels
    // define SPDLOG_ACTIVE_LEVEL to desired level
    SPDLOG_TRACE("Some trace message with param {}", 42);
    SPDLOG_DEBUG("Some debug message");
    
    // Set the default logger to file logger
    auto 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 1
        auto 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);
#endif
        auto 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 globally
        spdlog::register_logger(nml_logger);

        // 设置日志记录级别
#ifdef _DEBUG
        nml_logger->set_level(spdlog::level::trace);
#else
        nml_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 logger
    auto 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:30am
    auto 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 logger
    auto 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 loggers
    spdlog::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 error

Process 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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/152317.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 小白教程!!!win10如何安装Windows和Linux双系统??

    小白教程!!!win10如何安装Windows和Linux双系统??最近升级了win10装了一块固态硬盘,决定装一个双系统玩玩,正好公司运维大哥没事干,在他的帮助下,加上上网看了看发现关于win10的双系统双硬盘安装教程大都语焉不详,要么就是从别处复制粘贴的,这里发一个我的安装步骤如下:一:去官网下载Ubuntu系统 地址:https://www.ubuntu.com/download/desktop问题来了,去哪里下载一个linux系统呢?很简单,去官…

  • python闭包详解_python闭包的使用场景

    python闭包详解_python闭包的使用场景闭包首先了解一下:如果在一个函数的内部定义了另一个函数,外部的我们叫他外函数,内部的我们叫他内函数。在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用

  • JAVA数组去重方法

    JAVA数组去重方法记录JAVA中数组去重方法1利用Set的特性去重复 Setset=newHasSet(); for(intx=0;x<arr.length;x++){ set.add(arr[x]); }最后set里面的值就是arr数组去重后的所有元素,但是set中数据是无序的,会打乱原本的顺序。2如果想保留原本的顺序就使用有顺序,不重复特点的链表的哈希集合 LinkedHasSet<Object>temp=newLinkedHasSet<>()

  • pytest报错_git代码提交流程

    pytest报错_git代码提交流程前言我们每天写完自动化用例后都会提交到git仓库,随着用例的增多,为了保证仓库代码的干净,当有用例新增的时候,我们希望只运行新增的未提交git仓库的用例。pytest-picked插件可以

  • PS2手柄按键测试

    PS2手柄按键测试arduino的PS2手柄控制测试:#include<PS2X_lib.h>//电机引脚#definePWMA12//A电机转速#defineDIRA134#defineDIRA235//A电机方向#definePWMB8//B电机转速#defineDIRB137#defineDIRB236//B电机方向#def…

  • css分页效果_asp中数字分页样式

    css分页效果_asp中数字分页样式CSS分页实例简单分页如果你的网站有很多个页面,你就需要使用分页来为每个页面做导航。ul.pagination{display:inline-block;padding:0;marg

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号