• spdlog日志库的封装使用


    前言

    编码过程中,日志是必要的一个组件,我们选择使用哪个日志库呢?

    参考:15 best C++ Logging libraries in 2022

    C++ 日志库用于在用 C++ 编写的应用程序中记录信息。日志记录可以在不同的粒度级别、不同的日志类型和不同的输出目标上完成。一个好的日志库是任何应用程序的关键组件。它允许您执行各种调试任务,例如跟踪错误、监控性能甚至跟踪用户操作。日志记录是一种跟踪程序正在做什么的方法。在收集统计数据、分析性能问题,甚至只是为了大致了解应用程序内部发生的情况时,日志也很有用。有几个流行的 C++ Logging 开源库可供开发人员使用: spdlog - 快速 C 日志记录库;glog - Google 日志模块的 C 实现;easyloggingpp - 单头 C 日志库。

    随大流,选择使用人数多,商业友好的spdlog


    spdlog的基本使用

    见官方文档:spdlog在这里插入图片描述


    spdlog日志库的封装

    在一个多人合作的项目中,日志库封装的越顺手,程序员在代码中越多的使用日志语句,给后续的分析bug带来便利。

    第一种封装方式,是只封装一个日志库。好处是封装起来较为容易,从而封装的效果也比较好;坏处是不便于替换使用其他日志库。

    第二种封装方式,以相同的接口,同时封装多种日志库,在编译的时候,使用编译选项决定使用哪种日志库。好处是可以替换使用日志库。缺点是增大了封装的难度,在程序员水平一定的时候,封装的效果不大好。

    spdlog直接拿过来不太好用。比如,我想在一个进程的任何地方,都可以使用同一个logger。这是一个比较合理的要求,只用初始化一次,只输出到一个日志文件。我们可以在main中初始化一日志,在进程的其他地方,使用 spdlog::get线程安全的访问记录器。这样做的缺点是,使用日志库的所有程序员都需要知道spdlog库api的使用方式。

    所以,我们需要对日志库进行封装,屏蔽掉细节,以保证日志库被方便安全的被使用

    在封装的时候,我在考虑要不要将其封装成单例模式。在整个进程中使用同一个logger,似乎是可以封装成单例模式,或者全局变量?。但是如果此时,在一个进程中,希望可以有两个或者多个功能的logger,则不应该使用单例模式。

    最后,我选择使用单例模式。因为,如果有在一个进程中使用多种logger,那似乎可以将程序分为多进程。

    因日志库,而不得不拆分程序为多进程,似乎是不应该的。先写成单例模式吧,日志有需要再修改。

    boost1.46曾有过Singleton的类模板,本打算借鉴借鉴,但这个类被移除了。参考下面两个链接,使用static来实现单例。(文中讨论的单例模式的缺点,没看懂,懵~)

    下面是对日志库的封装。这个封装的使用,可参考da1234cao/FreeFile

    #pragma once
    
    // 参考1:https://blog.csdn.net/gongjianbo1992/article/details/113279632
    // 参考2:https://techartlife.com/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/cpp/%E5%B0%81%E8%A3%85spdlog%E5%BA%93/
    // 参考3:https://github.com/SergiusTheBest/plog/blob/master/include/plog/Log.h
    
    #include "exception.hpp"
    #include 
    #include 
    #include 
    #include 
    #include 
    
    namespace Log {
    
    struct log_execption : virtual base_exception {};
    
    class SPDLOG {
    private:
        SPDLOG() = default;
    private:
        std::shared_ptr logger_ptr_;
        void setLogLevel(const std::string& level);
    
    public:
    	static SPDLOG& getInstance() {
    		static SPDLOG instance;
    		return instance;
    	}
        // 初始化一个默认日志文件logger: 日志路径;logger name; 日志等级;单个日志文件最大大小;回滚日志文件个数;日志是否线程安全;
    	void init(std::string log_file_path,std::string logger_name, std::string level, size_t max_file_size, size_t max_files, bool mt_security = false); 
        std::shared_ptr logger() { return logger_ptr_; }
    }; // SPDLOG class
    
    #define LOG_TRACE(...)       SPDLOG::getInstance().logger().get()->trace(__VA_ARGS__)
    #define LOG_DEBUG(...)       SPDLOG::getInstance().logger().get()->debug(__VA_ARGS__)
    #define LOG_INFO(...)        SPDLOG::getInstance().logger().get()->info(__VA_ARGS__)
    #define LOG_WARN(...)        SPDLOG::getInstance().logger().get()->warn(__VA_ARGS__)
    #define LOG_ERROR(...)       SPDLOG::getInstance().logger().get()->error(__VA_ARGS__)
    #define LOG_CRITICAL(...)    SPDLOG::getInstance().logger().get()->critical(__VA_ARGS__)
    } // Log namespace
    
    void Log::SPDLOG::init(std::string log_file_path, std::string logger_name, std::string level, size_t max_file_size, size_t max_files, bool mt_security)
    {
        try {
            if (mt_security) {
                logger_ptr_ = spdlog::rotating_logger_mt(logger_name, log_file_path, max_file_size, max_files);
            }
            else {
                logger_ptr_ = spdlog::rotating_logger_st(logger_name, log_file_path, max_file_size, max_files);
            }
            setLogLevel(level);
            logger_ptr_->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%t] [%s %!:%#] %v"); //设置格式:https://spdlog.docsforge.com/v1.x/3.custom-formatting/
        } 
        catch (const spdlog::spdlog_ex& ex) {
           BOOST_THROW_EXCEPTION(log_execption()
                << err_str("Log initialization failed: " + std::string(ex.what()))
            );
        }
    }
    
    void Log::SPDLOG::setLogLevel(const std::string &level)
    {
        char L = toupper(level[0]);
        if (L == 'T') { // trace
            logger_ptr_->set_level(spdlog::level::trace);
            logger_ptr_->flush_on(spdlog::level::trace);
        }
        else if (L == 'D') { // debug
            logger_ptr_->set_level(spdlog::level::debug);
            logger_ptr_->flush_on(spdlog::level::debug);
        }
        else if (L == 'I') { // info
            logger_ptr_->set_level(spdlog::level::info);
            logger_ptr_->flush_on(spdlog::level::info);
        }
        else if (L == 'W') { // warn
            logger_ptr_->set_level(spdlog::level::warn);
            logger_ptr_->flush_on(spdlog::level::warn);
        }
        else if (L == 'E') { // error
            logger_ptr_->set_level(spdlog::level::err);
            logger_ptr_->flush_on(spdlog::level::err);
        }
        else if (L == 'C') { // critical
            logger_ptr_->set_level(spdlog::level::critical);
            logger_ptr_->flush_on(spdlog::level::critical);
        } else {
            BOOST_THROW_EXCEPTION(log_execption()
                << err_str("level set error " + level)
            );
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
  • 相关阅读:
    基于Unity开发的血条(Health Bar)的预制设计
    python之对接有道翻译API接口实现批量翻译
    Echarts 柱状图的详细配置过程
    mybatisPlus不能赋null值
    HTML+CSS-项目:学成在线
    Llama改进之——RoPE旋转位置编码
    都已经有了 HTTP 协议,怎么还要有 RPC?
    Goland常用快捷键设置
    C++ Reference: Standard C++ Library reference: C Library: cwchar: vfwscanf
    [微前端实战]---033vue2 - 新能源子页面
  • 原文地址:https://blog.csdn.net/sinat_38816924/article/details/126192561