F项目日志输出设计
郝伟 2021/03/11

1 简介

本设计参考 log4j
主要功能

2 使用方法

flogger库的使用示例代码如下所示:

#include "flogger.h"

using namespace flogger;

void test(){
    all('a', 123, "this is all function.");
    info("Terminal", "192.168.1.254", 66534, "OK");
    info("Terminal", "192.168.1.254", 66534, "OK", 'c');
    error("out of index", 1234);
    warn("this class is time taken.");
    fatal("the current thread is forced to shutdown.");
}

int main(int argc, char **argv) {
    DEBUG_LEVEL=ALL;
    SEP='_';
    info("hello");
    info("Network: Done.", "192.168.1.0:8080", 29343);
    all(123);
    test();
    return 0;
}

输出结果为:

C:\Users\hwaus\CLionProjects\hello\cmake-build-debug\hello.exe
[Mar 11 2021 09:48:19]C:\Users\hwaus\CLionProjects\hello\main.cpp@main:17.2:hello
[Mar 11 2021 09:48:19]C:\Users\hwaus\CLionProjects\hello\main.cpp@main:18.2:Network: Done._192.168.1.0:8080_29343
[Mar 11 2021 09:48:19]C:\Users\hwaus\CLionProjects\hello\main.cpp@main:19.0:123
[Mar 11 2021 09:48:19]C:\Users\hwaus\CLionProjects\hello\main.cpp@test:6.0:a_123_this is all function.
[Mar 11 2021 09:48:19]C:\Users\hwaus\CLionProjects\hello\main.cpp@test:7.2:Terminal_192.168.1.254_66534_OK
[Mar 11 2021 09:48:19]C:\Users\hwaus\CLionProjects\hello\main.cpp@test:8.2:Terminal_192.168.1.254_66534_OK_c
[Mar 11 2021 09:48:19]C:\Users\hwaus\CLionProjects\hello\main.cpp@test:9.4:out of index_1234
[Mar 11 2021 09:48:19]C:\Users\hwaus\CLionProjects\hello\main.cpp@test:10.3:this class is time taken.
[Mar 11 2021 09:48:19]C:\Users\hwaus\CLionProjects\hello\main.cpp@test:11.5:the current thread is forced to shutdown.

3 日志使用原则

  1. 严格控制日志输出数量
    由于日志输出是占用性能的,且性能性别是毫秒级,所以输出的数量尽量要少,否则过多的日志输出会严重影响系统性能。
  2. 有效处理无效日志
    一些已经解决问题的DEBUG输出,可以将其注释之或将其分类由DEBUG改为ALL。

4 实现代码

/***************************************************************
 * 【使用说明】
 * 进行日志的输出打印控制,提供了以下七个等级的日志类型(参考log4j)
 * ALL=0:任何人都可以随意输出的信息,优先级最低;
 * DEBUG=1:以测试为目的的打印输出消息;
 * INFO=2:用于输出生产环境中一些重要的信息,这个信息与调试无关;
 * WARN=3:某些可能出问题的情况,在运行环境中会给出提示;
 * ERROR=4:程序出错,但不足矣导致程序退出时的异常信息;
 * FATAL=5:重大错误而导致程序退出的异常信息;
 * OFF=6: 不显示任何信息
 * 提供了7个同名但小写的最多6个不定参数类型的函数
 * 【注意事项】
 * 1. 严格控制日志输出数量
 * 由于日志输出是占用性能的,且性能性别是毫秒级,所以输出的数量尽量要少,否则过多的日志输出会严重影响系统性能。
 * 2. 有效处理无效日志
 * 一些已经解决问题的DEBUG输出,可以将其注释之或将其分类由DEBUG改为ALL。
 * 【发布作者:郝伟】
 * 【更新记录】
 * 2021/03/11 第一次发布
****************************************************************/

#ifndef FSY_FLOGGER_H
#define FSY_FLOGGER_H

#include <iostream>
#include <stdio.h>

namespace flogger{

int DEBUG_LEVEL = 0; // 调试级别
char SEP = '~';  // 输出时的分隔符


// 错误等级
#define ALL 0
#define DEBUG 1
#define INFO 2
#define WARN 3
#define ERROR 4
#define FATAL 5
#define OFF 6
#pragma execution_character_set("utf-8")

#define INFO1(arg1) logger(INFO, __FILE__, __FUNCTION__, __LINE__, arg1)
#define INFO2(arg1, arg2) logger(INFO, __FILE__, __FUNCTION__, __LINE__, arg1, arg2)
#define INFO3(arg1, arg2, arg3) logger(INFO, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3)
#define INFO4(arg1, arg2, arg3, arg4) logger(INFO, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4)
#define INFO5(arg1, arg2, arg3, arg4, arg5) logger(INFO, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5)
#define INFO6(arg1, arg2, arg3, arg4, arg5, arg6) logger(INFO, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5, arg6)

#define DEBUG1(arg1) logger(DEBUG, __FILE__, __FUNCTION__, __LINE__, arg1)
#define DEBUG2(arg1, arg2) logger(DEBUG, __FILE__, __FUNCTION__, __LINE__, arg1, arg2)
#define DEBUG3(arg1, arg2, arg3) logger(DEBUG, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3)
#define DEBUG4(arg1, arg2, arg3, arg4) logger(DEBUG, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4)
#define DEBUG5(arg1, arg2, arg3, arg4, arg5) logger(DEBUG, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5)
#define DEBUG6(arg1, arg2, arg3, arg4, arg5, arg6) logger(DEBUG, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5, arg6)

#define ALL1(arg1) logger(ALL, __FILE__, __FUNCTION__, __LINE__, arg1)
#define ALL2(arg1, arg2) logger(ALL, __FILE__, __FUNCTION__, __LINE__, arg1, arg2)
#define ALL3(arg1, arg2, arg3) logger(ALL, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3)
#define ALL4(arg1, arg2, arg3, arg4) logger(ALL, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4)
#define ALL5(arg1, arg2, arg3, arg4, arg5) logger(ALL, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5)
#define ALL6(arg1, arg2, arg3, arg4, arg5, arg6) logger(ALL, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5, arg6)

#define WARN1(arg1) logger(WARN, __FILE__, __FUNCTION__, __LINE__, arg1)
#define WARN2(arg1, arg2) logger(WARN, __FILE__, __FUNCTION__, __LINE__, arg1, arg2)
#define WARN3(arg1, arg2, arg3) logger(WARN, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3)
#define WARN4(arg1, arg2, arg3, arg4) logger(WARN, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4)
#define WARN5(arg1, arg2, arg3, arg4, arg5) logger(WARN, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5)
#define WARN6(arg1, arg2, arg3, arg4, arg5, arg6) logger(WARN, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5, arg6)

#define ERROR1(arg1) logger(ERROR, __FILE__, __FUNCTION__, __LINE__, arg1)
#define ERROR2(arg1, arg2) logger(ERROR, __FILE__, __FUNCTION__, __LINE__, arg1, arg2)
#define ERROR3(arg1, arg2, arg3) logger(ERROR, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3)
#define ERROR4(arg1, arg2, arg3, arg4) logger(ERROR, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4)
#define ERROR5(arg1, arg2, arg3, arg4, arg5) logger(ERROR, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5)
#define ERROR6(arg1, arg2, arg3, arg4, arg5, arg6) logger(ERROR, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5, arg6)

#define FATAL1(arg1) logger(FATAL, __FILE__, __FUNCTION__, __LINE__, arg1)
#define FATAL2(arg1, arg2) logger(FATAL, __FILE__, __FUNCTION__, __LINE__, arg1, arg2)
#define FATAL3(arg1, arg2, arg3) logger(FATAL, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3)
#define FATAL4(arg1, arg2, arg3, arg4) logger(FATAL, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4)
#define FATAL5(arg1, arg2, arg3, arg4, arg5) logger(FATAL, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5)
#define FATAL6(arg1, arg2, arg3, arg4, arg5, arg6) logger(FATAL, __FILE__, __FUNCTION__, __LINE__, arg1, arg2, arg3, arg4, arg5, arg6)

#define GetMacro(_1, _2, _3, _4, _5, _6, NAME, ...) NAME
#define all(...) GetMacro(__VA_ARGS__, ALL6, ALL5, ALL4, ALL3, ALL2, ALL1, ...)(__VA_ARGS__)
#define debug(...) GetMacro(__VA_ARGS__, DEBUG6, DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, ...)(__VA_ARGS__)
#define info(...) GetMacro(__VA_ARGS__, INFO6, INFO5, INFO4, INFO3, INFO2, INFO1, ...)(__VA_ARGS__)
#define warn(...) GetMacro(__VA_ARGS__, WARN6, WARN5, WARN4, WARN3, WARN2, WARN1, ...)(__VA_ARGS__)
#define error(...) GetMacro(__VA_ARGS__, ERROR6, ERROR5, ERROR4, ERROR3, ERROR2, ERROR1, ...)(__VA_ARGS__)
#define fatal(...) GetMacro(__VA_ARGS__, FATAL6, FATAL5, FATAL4, FATAL3, FATAL2, FATAL1, ...)(__VA_ARGS__)


using namespace std;


// 参数为1个时调用此函数
template<typename T>
void show(const T &arg) {
    cout << arg << endl;
}

// 参数个数大于1时使用此函数
template<typename T, typename ... Types>
void show(const T &arg1, const Types &... args) {
    cout << arg1 << SEP;
    show(args...);
}

// 日志记录函数, 格式为:[日期时间]文件路径@函数名称:行号.类型:消息1#消息1#
template<typename T, typename ... Types>
void logger(int log_level, const char* file, const char* func, int lineno, const T &arg1, const Types &... args) {
    if(log_level >= DEBUG_LEVEL) {
        cout << '[' << __DATE__ << ' ' << __TIME__ << ']' << file << '@' << func << ":" << lineno << "." << log_level << ":";
        show(arg1, args...);
    }
}

}
#endif //FSY_FLOGGER_H

5 参考资料

[1] C++11 - New features - Variadic templates, http://www.cplusplus.com/articles/EhvU7k9E/
[2] What's New in the C++11 Standard Template Library?, https://www.quantstart.com/articles/Whats-New-in-the-C11-Standard-Template-Library/
[3] logger with file name, line and time stamp, https://stackoverflow.com/questions/15134727/logger-with-file-name-line-and-time-stamp
[4] C语言重载宏函数的小技巧, https://blog.csdn.net/lmhuanying1012/article/details/78715351

其他

#if DEBUG_LEVEL==FATAL
    #define fatal(...) GetMacro(__VA_ARGS__, FATAL6, FATAL5, FATAL4, FATAL3, FATAL2, FATAL1, ...)(__VA_ARGS__)
#elif DEBUG_LEVEL==ERROR
    #define error(...) GetMacro(__VA_ARGS__, ERROR6, ERROR5, ERROR4, ERROR3, ERROR2, ERROR1, ...)(__VA_ARGS__)
    #define fatal(...) GetMacro(__VA_ARGS__, FATAL6, FATAL5, FATAL4, FATAL3, FATAL2, FATAL1, ...)(__VA_ARGS__)
#elif DEBUG_LEVEL==WARN
    #define warn(...) GetMacro(__VA_ARGS__, WARN6, WARN5, WARN4, WARN3, WARN2, WARN1, ...)(__VA_ARGS__)
    #define error(...) GetMacro(__VA_ARGS__, ERROR6, ERROR5, ERROR4, ERROR3, ERROR2, ERROR1, ...)(__VA_ARGS__)
    #define fatal(...) GetMacro(__VA_ARGS__, FATAL6, FATAL5, FATAL4, FATAL3, FATAL2, FATAL1, ...)(__VA_ARGS__)
#elif DEBUG_LEVEL==INFO
#define all(...) GetMacro(__VA_ARGS__, ALL6, ALL5, ALL4, ALL3, ALL2, ALL1, ...)(__VA_ARGS__)
    #define info(...) GetMacro(__VA_ARGS__, INFO6, INFO5, INFO4, INFO3, INFO2, INFO1, ...)(__VA_ARGS__)
    #define warn(...) GetMacro(__VA_ARGS__, WARN6, WARN5, WARN4, WARN3, WARN2, WARN1, ...)(__VA_ARGS__)
    #define error(...) GetMacro(__VA_ARGS__, ERROR6, ERROR5, ERROR4, ERROR3, ERROR2, ERROR1, ...)(__VA_ARGS__)
    #define fatal(...) GetMacro(__VA_ARGS__, FATAL6, FATAL5, FATAL4, FATAL3, FATAL2, FATAL1, ...)(__VA_ARGS__)
#elif DEBUG_LEVEL==DEBUG
    #define all(...) GetMacro(__VA_ARGS__, ALL6, ALL5, ALL4, ALL3, ALL2, ALL1, ...)(__VA_ARGS__)
    #define debug(...) GetMacro(__VA_ARGS__, DEBUG6, DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, ...)(__VA_ARGS__)
    #define info(...) GetMacro(__VA_ARGS__, INFO6, INFO5, INFO4, INFO3, INFO2, INFO1, ...)(__VA_ARGS__)
    #define warn(...) GetMacro(__VA_ARGS__, WARN6, WARN5, WARN4, WARN3, WARN2, WARN1, ...)(__VA_ARGS__)
    #define error(...) GetMacro(__VA_ARGS__, ERROR6, ERROR5, ERROR4, ERROR3, ERROR2, ERROR1, ...)(__VA_ARGS__)
    #define fatal(...) GetMacro(__VA_ARGS__, FATAL6, FATAL5, FATAL4, FATAL3, FATAL2, FATAL1, ...)(__VA_ARGS__)
#elif DEBUG_LEVEL==ALL
    #define all(...) GetMacro(__VA_ARGS__, ALL6, ALL5, ALL4, ALL3, ALL2, ALL1, ...)(__VA_ARGS__)
    #define debug(...) GetMacro(__VA_ARGS__, DEBUG6, DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, ...)(__VA_ARGS__)
    #define info(...) GetMacro(__VA_ARGS__, INFO6, INFO5, INFO4, INFO3, INFO2, INFO1, ...)(__VA_ARGS__)
    #define warn(...) GetMacro(__VA_ARGS__, WARN6, WARN5, WARN4, WARN3, WARN2, WARN1, ...)(__VA_ARGS__)
    #define error(...) GetMacro(__VA_ARGS__, ERROR6, ERROR5, ERROR4, ERROR3, ERROR2, ERROR1, ...)(__VA_ARGS__)
    #define fatal(...) GetMacro(__VA_ARGS__, FATAL6, FATAL5, FATAL4, FATAL3, FATAL2, FATAL1, ...)(__VA_ARGS__)
#endif