基于spdlog封装了一套接口,可实现控制台log输出、文件log输出,或控制台+文件同时输出,根据自己需求自由切换。亲测OK,分享一下。

头文件定义基类HrgLogger和三个子类ConsoleLogger、FileLogger和MultiLogger:

#ifndef __HRG_LOG__
#define __HRG_LOG__#include <iostream>
#include <string>
#include <sstream>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/logger.h"#include "spdlog/sinks/stdout_sinks.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/common.h"enum LoggerMode
{LAB_MODE = 0,  //实验室模式,打印比较细致的信息TRIAL_MODE = 1,   //试用模式,打印调试信息USER_MODE = 2,    //用户模式,打印较重要的信息
};#define LOG_MODE USER_MODEusing namespace spdlog;#define LOG_LEVEL_TRACE spdlog::level::trace
#define LOG_LEVEL_DEBUG spdlog::level::debug
#define LOG_LEVEL_INFO spdlog::level::info
#define LOG_LEVEL_WARN spdlog::level::warn
#define LOG_LEVEL_ERROR spdlog::level::err
#define LOG_LEVEL_CRITICAL spdlog::level::critical
#define LOG_LEVEL_OFF spdlog::level::offusing print_level = spdlog::level::level_enum;    //变量重命名
using format_string = string_view_t;      // 变量重命名 //wstring_view_t;   //fmt::basic_string_view<char>;class HrgLogger
{
public:HrgLogger(){logger_created = false;logger_droped = false;}~HrgLogger();std::shared_ptr<spdlog::logger> hrg_logger;bool logger_created;         //标识日志创建状态bool logger_droped;          //标识日志关闭状态time_t logger_create_time;   //日志创建时间/* Generate log file name automatically according to real time, usually used when many real-time log files should be output */virtual void generate_file_name_automaticaly();/* Set a specified log file name, usually used when only one log file is needed.input param @filename is the specified filename.*/void set_specified_file_name(std::string filename);/* The abstract api for create logger, would be realized by the derived classes */virtual void create_logger() = 0;   //创建logger的纯虚函数,只能在子函数中实现/* 设置日志的打印级别 */void set_print_level(print_level lvl){hrg_logger->set_level(lvl);}/* 重新封装logger的trace, debug, warn, error和critical打印接口 */template<typename... Args>inline void print_trace(format_string fmt, const Args &... args){hrg_logger->trace(fmt, args...);}template<typename... Args>inline void print_debug(format_string fmt, const Args &... args){hrg_logger->debug(fmt, args...);}template<typename... Args>inline void print_info(format_string fmt, const Args &... args){hrg_logger->info(fmt, args...);}template<typename... Args>inline void print_warn(format_string fmt, const Args &... args){hrg_logger->warn(fmt, args...);}template<typename... Args>inline void print_error(format_string fmt, const Args &... args){hrg_logger->error(fmt, args...);}template<typename... Args>inline void print_critical(format_string fmt, const Args &... args){hrg_logger->critical(fmt, args...);}/* 销毁logger */void destroy_logger(){spdlog::drop_all();logger_droped = true;}protected:std::string log_full_name;  //The full name contains log_path and log_file_namestd::string log_path;  // The log path which is specified in class constructor};/* 控制台logger子类 */
class ConsoleLogger : public HrgLogger
{
public:ConsoleLogger(){std::cout << "ConsoleLogger constructor." << std::endl;}void generate_file_name_automaticaly(){}  //对于控制台子类来说,无需生成file文件,因此该方法的函数体置空virtual void create_logger();};/* 文件logger子类 */
class FileLogger : public HrgLogger
{
public:FileLogger(std::string str){log_path = str;std::cout << "FileLogger Constructor." << std::endl;}virtual void create_logger();
};/* 控制台+文件复合logger子类 */
class MultiLogger : public HrgLogger
{
public:MultiLogger(std::string str)       {log_path = str;std::cout << "MultiLogger with parameters constructor." << std::endl;}virtual void create_logger();
};/* Logger选择类,通过传入的logger模式生成相应的logger */
class LoggerSelector
{
public:LoggerSelector(){}LoggerSelector(std::string str){path = str;}~LoggerSelector(){}HrgLogger* select_logger(std::string out_type);HrgLogger* select_logger(int mode);private:std::string path;
};#endif

基类方法的实现:

HrgLogger::~HrgLogger()
{destroy_logger();
}void HrgLogger::generate_file_name_automaticaly()
{struct tm *cur_time;time_t local_time;time(&local_time);cur_time = localtime(&local_time);/* 通过时间命名日志文件 */char filename[200];strftime(filename, 100, "%Y-%m-%d_%H-%M-%S.log", cur_time);/* log file name, does not contain file path */std::string log_file_name = std::string(filename);/* 判断日志路径是否存在,若不存在,则创建 *//* Check if the input log path exists, if not, create the path */if(access(log_path.c_str(), F_OK) != 0){mkdir(log_path.c_str(), S_IRWXU); }/* 日志文件全路径 */log_full_name = log_path + log_file_name;/* 记录日志创建时间,在通过时间长度控制日志文件大小时,该成员变量会被使用 */logger_create_time = local_time;}/* 除了使用时间命名日志文件,用户还可以自定义日志文件名称 */
void HrgLogger::set_specified_file_name(std::string filename)
{/* Check if the input log path exists, if not, create the path */if(access(log_path.c_str(), F_OK) != 0){mkdir(log_path.c_str(), S_IRWXU); }/* Make full log path */log_full_name = log_path + filename;
}

ConsoleLogger类方法实现:

/* 创建控制台logger */
void ConsoleLogger::create_logger()
{try{   hrg_logger = spdlog::stdout_color_mt("console");//hrg_logger->set_pattern("%+");   hrg_logger->set_pattern("[%Y-%m-%d %T][thread %t][%l]%v");}catch(const spdlog::spdlog_ex &ex){std::cout << "Create console logger failed: " << ex.what() << std::endl;exit(EXIT_FAILURE);}if(!hrg_logger){std::cout << "Create console logger failed." << std::endl;}else{std::cout << "Create console logger seccessfully." << std::endl;logger_created = true;}}

FileLogger类方法实现:

/* 创建文件logger */
void FileLogger::create_logger()
{try{hrg_logger = spdlog::basic_logger_mt("basic_logger", log_full_name.c_str());    spdlog::set_pattern("[%Y-%m-%d %T][%l]%v"); hrg_logger->set_level(spdlog::level::trace);}catch(const spdlog::spdlog_ex &ex){std::cout << "Create file logger failed: " << ex.what() << std::endl;exit(EXIT_FAILURE);}if(!hrg_logger){std::cout << " Create file logger failed." << std::endl;}else{std::cout << "Create file logger successfully." << std::endl;logger_created = true;}
}

MultiLogger类方法实现:

/* 创建复合logger */
void MultiLogger::create_logger()
{try{/* 通过multi-sink的方式创建复合logger,实现方式为:先分别创建文件sink和控制台sink,并将两者放入sink 向量中,组成一个复合logger *//* file sink */std::cout << "MultiLogger: log_full_name = " << log_full_name << std::endl;auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_full_name.c_str(), true);file_sink->set_level(spdlog::level::trace);file_sink->set_pattern("[%Y-%m-%d %T][%l]%v");std::cout << "MultiLogger: create file sink OK." << std::endl;/* 控制台sink */auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();console_sink->set_level(spdlog::level::trace);console_sink->set_pattern("%+");std::cout << "MultiLogger: create console sink OK." << std::endl;/* Sink组合 */std::vector<spdlog::sink_ptr> sinks;sinks.push_back(console_sink);sinks.push_back(file_sink);hrg_logger = std::make_shared<spdlog::logger>("multi-sink", begin(sinks), end(sinks));std::cout << "MultiLogger: create multi sink OK." << std::endl;}catch(const spdlog::spdlog_ex &ex){std::cout << "Create multi-logger failed: " << ex.what() << std::endl;exit(EXIT_FAILURE);}if(!hrg_logger){std::cout << " Create multi-logger failed." << std::endl;}else{std::cout << "Create multi-logger successfully." << std::endl;logger_created = true;}}

通过简单工厂模式实现logger的选择,提供两种方式,一种是通过字符串指定logger类,一种是通过int型mode指定目前使用的模式,可适用于实验室阶段、试用阶段和正式的客户使用阶段,每个阶段分别设置不同的日志level。其中,实验室阶段使用控制台输出,打印的信息较细致,设置输出level为trace;试用阶段使用控制台+文件输出,打印debug以上级别的信息,便于在客户现场定位问题;正式客户使用阶段只需通过文件输出日志,可保留比较重要的打印信息,设置打印级别为info以上即可。

/* 通过指定输出名称选择logger */
HrgLogger* LoggerSelector::select_logger(std::string out_type)
{HrgLogger *p_logger = NULL;if(out_type == "console"){p_logger = new ConsoleLogger();p_logger->create_logger();}else if(out_type == "file"){p_logger = new FileLogger(path);std::cout << "For file logger, path = " << path << std::endl;p_logger->generate_file_name_automaticaly();p_logger->create_logger();}else if(out_type == "both"){p_logger = new MultiLogger(path);p_logger->generate_file_name_automaticaly();p_logger->create_logger();}else {std::cout << "Unsupported logger type!" << std::endl;return NULL;}return p_logger;
}/* 通过日志模式选择logger */
HrgLogger* LoggerSelector::select_logger(int mode)
{HrgLogger *p_logger = NULL;switch(mode){case LAB_MODE:{p_logger = new ConsoleLogger();p_logger->create_logger();p_logger->set_print_level(LOG_LEVEL_TRACE);break;          }case TRIAL_MODE:{p_logger = new MultiLogger(path);p_logger->generate_file_name_automaticaly();p_logger->create_logger();p_logger->set_print_level(LOG_LEVEL_DEBUG);break;}case USER_MODE:{p_logger = new FileLogger(path);p_logger->generate_file_name_automaticaly();p_logger->create_logger();p_logger->set_print_level(LOG_LEVEL_INFO);break;}default:{break;}}return p_logger;
}

测试程序,只测试了通过字符串形式创建logger,但在实际工程使用中,较多使用mode形式,用户可根据自己喜好及习惯选择实现方式。

#include <iostream>
#include <string>
#include <unistd.h>
#include "hrg_log.h"void multi_sink_example()
{auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();console_sink->set_level(spdlog::level::warn);console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("log/multisink.txt", true);file_sink->set_level(spdlog::level::trace);#if 0spdlog::logger logger("multi_sink", {console_sink, file_sink});logger.set_level(spdlog::level::debug);logger.warn("this should appear in both console and file");logger.info("this message should not appear in the console, only in the file");
#endifstd::vector<spdlog::sink_ptr> sinks;sinks.push_back(console_sink);sinks.push_back(file_sink);std::shared_ptr<spdlog::logger> p_logger;p_logger = std::make_shared<spdlog::logger>("multi_sink", begin(sinks), end(sinks));p_logger->warn("this should appear in both console and file");p_logger->info("this message should not appear in the console, only in the file");
}#if 0
int main()
{std::string file_path = "./log/";/* 测试控制台日志 */HrgLogger *my_console_logger = new ConsoleLogger();if(!my_console_logger){std::cout << "Create console logger failed!" << std::endl;exit(1);}my_console_logger->create_logger();my_console_logger->set_print_level(LOG_LEVEL_TRACE);my_console_logger->print_trace("This is a trace message for console logger, number {}.", 1);my_console_logger->print_debug("This is a debug message for console logger, number {}.", 2);my_console_logger->print_info("This is a info message for console logger, number {}.", 3);my_console_logger->print_warn("This is a warn message for console logger, number {}.", 4);my_console_logger->print_error("This is a error message for console logger, number {}.", 5);delete my_console_logger;/* 测试文件日志 */HrgLogger *my_file_logger = new FileLogger(file_path);if(!my_file_logger){std::cout << "Create file logger failed!" << std::endl;exit(1);}my_file_logger->set_specified_file_name("myfilelog.txt");  //测试指定日志名称my_file_logger->create_logger();my_file_logger->set_print_level(LOG_LEVEL_INFO);my_file_logger->print_trace("This is a(n) {} message for file logger.", "trace");my_file_logger->print_debug("This is a(n) {} message for file logger.", "debug");my_file_logger->print_info("This is a(n) {} message for file logger.", "info");my_file_logger->print_warn("This is a(n) {} message for file logger.", "warn");my_file_logger->print_error("This is a(n) {} message for file logger.", "error");delete my_file_logger;sleep(3);/* 测试复合日志 */HrgLogger *my_multi_logger = new MultiLogger(file_path);if(!my_multi_logger){std::cout << "Create multi logger failed!" << std::endl;exit(1);}my_multi_logger->generate_file_name_automaticaly();  //测试自动生成日志名称my_multi_logger->create_logger();my_multi_logger->set_print_level(LOG_LEVEL_DEBUG);my_multi_logger->print_trace("This is a trace message for multi logger.");my_multi_logger->print_debug("This is a debug message for multi logger.");my_multi_logger->print_info("This is a info message for multi logger.");my_multi_logger->print_warn("This is a warn message for multi logger.");my_multi_logger->print_error("This is a error message for multi logger.");delete my_multi_logger;}
#endifint main()
{std::string file_path = "./log/";LoggerSelector *p_selector = new LoggerSelector(file_path);/* 测试控制台日志 */HrgLogger *my_console_logger = p_selector->select_logger("console");if(!my_console_logger){std::cout << "Create console logger failed!" << std::endl;exit(1);}//my_console_logger->create_logger();my_console_logger->set_print_level(LOG_LEVEL_TRACE);my_console_logger->print_trace("This is a trace message for console logger, number {}.", 1);my_console_logger->print_debug("This is a debug message for console logger, number {}.", 2);my_console_logger->print_info("This is an info message for console logger, number {}.", 3);my_console_logger->print_warn("This is a warn message for console logger, number {}.", 4);my_console_logger->print_error("This is an error message for console logger, number {}.", 5);delete my_console_logger;/* 测试文件日志 */HrgLogger *my_file_logger = p_selector->select_logger("file");if(!my_file_logger){std::cout << "Create file logger failed!" << std::endl;exit(1);}my_file_logger->set_print_level(LOG_LEVEL_INFO);my_file_logger->print_trace("This is a(n) {} message for file logger.", "trace");my_file_logger->print_debug("This is a(n) {} message for file logger.", "debug");my_file_logger->print_info("This is a(n) {} message for file logger.", "info");my_file_logger->print_warn("This is a(n) {} message for file logger.", "warn");my_file_logger->print_error("This is a(n) {} message for file logger.", "error");delete my_file_logger;/* 延迟3秒,以免复合日志中的文件名称与文件日志的文件名称冲突 */sleep(3);/* 测试复合日志 */HrgLogger *my_multi_logger = p_selector->select_logger("both");if(!my_multi_logger){std::cout << "Create multi logger failed!" << std::endl;exit(1);}my_multi_logger->generate_file_name_automaticaly();  //测试自动生成日志名称my_multi_logger->set_print_level(LOG_LEVEL_DEBUG);my_multi_logger->print_trace("This is a trace message for multi logger.");my_multi_logger->print_debug("This is a debug message for multi logger.");my_multi_logger->print_info("This is an info message for multi logger.");my_multi_logger->print_warn("This is a warn message for multi logger.");my_multi_logger->print_error("This is an error message for multi logger.");delete my_multi_logger;delete p_selector;return 0;}

编译执行,输出分为控制台输出和日志文件输出。

控制台输出:

基于spdlog实现日志控制台输出、文件输出或控制台+文件同时输出相关推荐

  1. 动手造轮子:实现一个简单的基于 Console 的日志输出

    动手造轮子:实现一个简单的基于 Console 的日志输出 Intro 之前结合了微软的 Logging 框架和 Serilog 写了一个简单的日志框架,但是之前的用法都是基于 log4net.ser ...

  2. linux将屏幕输出到文件,Linux命令执行的屏幕输出内容重定向到日志文件

    摘要: 作者:Syn良子 出处:http://www.cnblogs.com/cssdongl 转载请注明出处 快速mark一下这个命令细节,免得以后使用又忘记了 大家都知道可以用echo来输出内容到 ...

  3. log4j日志输出到web项目指定文件夹

    尝试将log4j的文件日志输出到web工程制定目录,遇到了很多问题,最终搞定,下面是记录. 其原理在于log4j的配置文件支持服务器的vm的环境变量,如${oss.log4j.path},在log4j ...

  4. IDEA文件UTF-8格式控制台输出中文乱码

    方法一:修改文件编码格式 修改文件编码格式,可以直接在最底部修改或者设置里边  File>Settings>Editor > File Encodings 修改完虽然解决了控制台乱码 ...

  5. 如何理解Linux shell中的“2>1”(将文件描述2(标准错误输出)的内容重定向到文件描述符1(标准输出))(尼玛>符号竟然不支持搜索,害我搜搜不到,只能搜)

    文章目录 前言 有何妙用 如何理解 总结 前言 有时候我们常看到类似这样的脚本调用: ./test.sh > log.txt 2>&1 这里的2>&1是什么意思?该如 ...

  6. 基于Rsyslog的日志文件采集办法

    近几年笔者在生产环境中,很多日志处理场景中都适用了Rsyslog,在基于UDP的分布式日志汇聚.日志文件采集方面都有出色的发挥,"The rocket-fast system for log ...

  7. yamlcpp遍历_OpenCV文件输入和输出使用XML和YAML文件

    目标 您会找到以下问题的答案:如何使用YAML或XML文件打印和读取文本和OpenCV文本条目? OpenCV数据结构如何做同样的操作? 如何为您的数据结构做这个? 源代码 您可以从这里下载,也可以在 ...

  8. linux c文件操作,Linux C 文件的输入/输出操作

    10.1 文件I/O操作概述 在Linux系统中,文件I/O操作可以分为两类,一类是基于文件描述符的I/O操作,另一类是基于数据流的I/O操作. 10.1.1 文件描述符简介 在文件操作一章中,也经常 ...

  9. [UE4]导入 PSD 文件失败的解决办法:输出为 PNG,将 PNG 再保存为 PSD 导入

    导入 PSD 文件失败的解决办法:输出为 PNG,将 PNG 再保存为 PSD 导入 图1 报错弹窗 LogFactory: FactoryCreateFile: Texture with Reimp ...

最新文章

  1. 纵深防御仍对付得了当今的网络威胁吗?
  2. ---WebCam网络摄像头6 编译WebCam
  3. android 4.4.2截屏方法,android4.4.2 使用 uiautoviewer 截屏报错
  4. 【转】 ADO.NET最佳实践
  5. 2022年中国开源软件产业研究报告
  6. 编程语言对比 with
  7. 编译libpng出错:pnglibconf.c fatal error: zlib.h 没有那个文件或目录
  8. 用python实现千图成像工具,快给你的男/女神做一个吧~
  9. 下载firebug网站
  10. 固态硬盘开盘数据恢复的方法
  11. matlab zernike矩
  12. 【DP】【Burnside】【多项式】烷基计数
  13. (转)同居男女同一天的日记对比
  14. 1.8.8 配置防盗链
  15. 谈谈运营经验:颠覆式创新
  16. 投资入门第 3 步:技术分析法(常用技术分析)
  17. 取模运算性质_数学与编程——求余、取模运算及其性质 | 学步园
  18. Time Management 时间管理
  19. 24. 项目管理成熟度模型CMM
  20. 数值分析-杜利特尔分解法C语言代码

热门文章

  1. 编程工具:检测永真永假分支
  2. C++动态内存分配---两级allocator设计与实现(二)
  3. ADSP-21569/ADSP-21593的开发入门(中)
  4. 华为名师揭秘编程界“网红”Python
  5. 概率统计--牛客网刷题(二)
  6. 迈百瑞冲刺创业板上市:关联收入占比较高,房健民为加拿大籍
  7. 规则引擎和流程引擎我该怎么理解
  8. python自学:15款学习游戏网站,边学边玩
  9. 打造大数据团队 从组建到价值创造全流程指南
  10. 建立Oracle全文索引