日志

#头文件

#include <co/log.h>

API 在 log 命名空间。

#初始化与关闭

log 内部使用 flag 组件定义了一些配置项,可通过命令行参数或配置文件控制日志行为。另外,日志线程在命令行参数解析后才会启动。因此必须在 main 函数开头调用:

flag::parse(argc, argv);

关闭 API:

void log::close();
  • 刷新日志缓冲区,退出日志线程。
  • 用户一般不需要显示调用此函数。

#打印日志

使用如下函数打印不同级别的日志:

log::debug(...);
log::info(...);
log::warn(...);
log::error(...);
log::fatal(...);
  • 上述函数均线程安全,接受任意数量参数;
  • 参数可以是 co::string::operator<< 支持的任意类型;
  • 日志尾部自动添加换行;
  • fatal 级别日志会终止程序。

示例:

log::info("hello ", false, ' ', 23);

支持的参数类型有:

  • bool,输出 false 或 true;
  • 字符类型:char, signed char, unsigned char;
  • 整数类型;
  • 浮点数类型:double, float;
  • 字符串类型:const char*, co::string, std::string, std::string_view;
  • 指针类型:void*, T*,输出 0x 加十六进制值;
  • STL 容器:std::vector, std::map, co::vector, co::map 等,需包含 co/stl.h。

自定义类型需实现 operator<<(co::string&, const T&),如:

struct Point {
    int x, y;
};

inline co::string& operator<<(co::string& s, const Point& p) {
    s << "Point(" << p.x << ", " << p.y << ")";
    return s;
}

int main(int argc, char** argv) {
    flag::parse(argc, argv);

    Point p{1, 2};
    co::println("point: ", p);
    log::info("point: ", p);

    return 0;
}

#断言

log::check(cond, ...);
log::check_eq(a, b, ...);
log::check_ne(a, b, ...);
log::check_lt(a, b, ...);
log::check_gt(a, b, ...);
log::check_le(a, b, ...);
log::check_ge(a, b, ...);
  • check 失败,会刷新日志缓冲,打印堆栈信息,调用 abort() 终止程序;
  • check 成功,不会输出任何信息。

示例:

log::check(1 + 1 == 2, "1+1 should be 2");
log::check_eq(1 + 1, 2, "1+1 must == 2");
log::check_ne(1 + 1, 3, "1+1 != 3");
log::check_lt(1, 2, "1 < 2");
log::check_gt(2, 1, "2 > 1");
log::check_le(2, 2, "2 <= 2");
log::check_ge(2, 2, "2 >= 2");

#打印堆栈信息

coost 会捕捉程序中产生的异常或异常信号,在程序崩溃退出前打印堆栈信息。

非 Windows 平台需要 libbacktrace。Linux 上 GCC 一般已内置 backtrace 库,macOS 通常需要用户手动安装。

非 Windows 平台构建时用下述方式启用此功能:

# xmake
xmake f --with_backtrace=true   # 使用 backtrace 库
xmake                           # 编译 libco
xmake b stack                   # 编译 test/stack.cc
xmake r stack                   # 执行 stack 测试程序

# cmake
mkdir cmakebuild && cd cmakebuild
cmake .. -DWITH_BACKTRACE=ON
make -j8

#写日志回调

coost 日志默认写本地文件,用户可以设置一个 callback 写日志:

void log::set_write_cb(
    void(*cb)(const void* data, size_t size),
    bool also_log2local=false
);
  • data 中可能包含多条日志,size 可能较大,用 UDP 发送日志需注意。
  • callback 日志线程中执行(仅单个线程写日志)。
  • 若 also_log2local == true,日志也写本地文件。
需要在 flag::parse 前调用,flag::parse 解析命令行参数后会启动日志线程,在日志线程启动前设置 callback 更安全。

示例:

#include <co/log.h>
#include <stdio.h>

static void my_write_cb(const void* data, size_t size) {
    fwrite(data, 1, size, stderr);
}

int main(int argc, char** argv) {
    log::set_write_cb(my_write_cb, false);
    flag::parse(argc, argv);

    log::info("hello ", 23);
    log::warn("warn ", false);
    return 0;
}

#日志配置项

flag 名 类型 默认值 含义
log_dir string "logs" 日志目录
min_log_level uint32 0 输出日志的最小级别,0-4 对应 debug/info/warn/error/fatal
max_log_size uint32 4096 单条日志最大大小
max_log_file_size int64 256 << 20,即 256MB 日志文件最大大小
max_log_file_num uint32 8 日志文件最大数量
max_log_buffer_size uint32 32 << 20,即 32MB 日志缓存最大大小
log_flush_ms uint32 128 刷新日志缓存的时间间隔,单位毫秒
also_log2console bool false 日志也输出到终端
log_daily bool false 日志文件按天轮转

min_log_level 可以过滤掉低级别的日志,级别大于或等于 min_log_level 的日志才输出。

#最小示例

#include <co/log.h>

int main(int argc, char** argv) {
    # parse 是必须的,解析命令行参数后,启动日志线程
    flag::parse(argc, argv);

    log::debug("This is debug.. ", 23);
    log::info("This is info.. ", 23);
    log::warn("This is warn.. ", 23);
    log::error("This is error.. ", 23);

    log::check(1 + 1 == 2, "1+1 should be 2");
    log::check_eq(1 + 1, 2, "1+1 must == 2");
    log::check_ne(1 + 1, 3, "1+1 != 3");
    log::check_lt(1, 2, "1 < 2");
    log::check_gt(2, 1, "2 > 1");
    log::check_le(2, 2, "2 <= 2");
    log::check_ge(2, 2, "2 >= 2");

    // log::fatal("fatal.. ", 23); // 会终止程序

    return 0;
}

运行示例:

./app -min_log_level=1 -also_log2console=true

命令行参数表示只输出 info 及以上级别日志,并输出到终端。