C++日誌記錄庫spdlog

鏡像庫 https://gitee.com/yctxkj/spdlog.git

spdlog是基於C++11實現的一款純頭文件的日誌管理庫(git地址:github.com/gabime/spdlo,API說明:spdlog.docsforge.com/v1):

  • 配置特別簡單,僅包含頭文件即可;
  • 寫日誌方式簡單明瞭;
  • 可實現自動按日期創建日誌文件/定時創建日誌文件;
  • 可自定義日誌格式;
  • 可以輸出當前輸出日誌所在的文件及函數;
  • 可自定義文檔大小;
  • 可將不同級別的信息輸出到不同日誌文件;
  • 多平臺等。

spdlog中各對象都分爲多線程與單線程版本:

  • *_st:單線程版本,不用加鎖,效率更高。
  • *_mt:多線程版本,用於多線程程序是線程安全的。

日誌記錄槽sink

spdlog定義了幾種sinks用於不同場景(也可自定義)下的日誌輸出,sink中主要包含:

  • set_pattern(const std::string&):設置日誌輸出的內容格式。
  • set_level(level_enum): 設置日誌輸出的最低等級。
  • log(log_msg):由logger自動調用,外部不會主動調用。

日誌記錄器logger

一個logger對象中存儲有多個sink,當調用logger的日誌輸出函數時,logger會調用自身存儲的所有sink對象的log(log_msg) 函數進行輸出。logger中主要包括:

  • set_pattern(const std::string&):設置logger包含的所有sink的日誌輸出內容格式。
  • set_level(level_enum):設置logger日誌輸出最低等級,如果logger包含的sink沒有設置日誌等級的話,則會爲其設置日誌等級。
  • log(level_enum level,log_msg content):按照level等級進行輸出content,logger其中日誌輸出最低等級小於或等於level的sink會進行執行輸出操作。
  • trace(content,arg1,arg2…):按照trace等級進行輸出,輸出內容由content與後面的參數格式化而成。同類的函數還包括:debug/info/warn…。

輸出格式pattern

通過set_pattern可設定日誌格式,如set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l](%@): %v");

輸出標記flag:

flag meaning example
%v 日誌內容 “my log test content”
%t 線程ID “123”
%P 進程ID “234”
%n 記錄器Logger名 “basicLogger”
%l 日誌級別 “debug”, “info”, etc
%L 日誌級別簡稱 “D”, “I”, etc
%a 星期幾(簡稱) “Thu”
%A 星期幾 “Thursday”
%b 月份簡稱 “Aug”
%B 月份 “August”
%c 日期時間 “Thu Aug 23 15:35:46 2014”
%C 年(兩位) “14”
%Y “2014”
%D %x 日期簡寫 “08/23/14”
%m 月份(數字) “11”
%d 日(數組) “29”
%H 小時(24制) “23”
%I 小時(12制) “11”
%M 分鐘 “59”
%S “58”
%e 毫秒 “678”
%f 微秒 “056789”
%F 納秒 “256789123”
%p AM/PM “AM”
%r 時間(12制) “02:55:02 pm”
%R 時分(24制) “23:55”
%T %X 時間(24制) “23:55:59”
%z 時區(偏移) “+02:00”
%E epoch(秒) “1528834770”
%% 百分號 “%”
%+ 默認格式 “[2014-10-31 23:46:59.678] [mylogger] [info] Some message”
%^ start color range (can be used only once) “[mylogger] [info(green)] Some message”
%$ end color range (for example %^[+++]%$ %v) (can be used only once) [+++] Some message
%@ 文件名與行數 my_file.cpp:123
%s 文件名 my_file.cpp
%g 文件名(含路徑) /some/dir/my_file.cpp
%# 行數 123
%! 函數名 my_func
%o 相對上一條記錄的時間間隔(毫秒) 456
%i 相對上一條記錄的時間間隔(微秒) 456
%u 相對上一條記錄的時間間隔(納秒) 11456
%O 相對上一條記錄的時間間隔(秒) 4

日誌輸出中要攜帶文件名、行數或函數名時,必須使用SPDLOG_LOGGER_*宏,且要激活對應的級別(哪些級別以上的日誌會被記錄):

// 記錄INFO及以上級別日誌
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#include "spdlog/spdlog.h"

SPDLOG_LOGGER_INFO(myLogger, "Support for floats {:03.2f}", 1.23456);
SPDLOG_LOGGER_WARN(myLogger, "Easy padding in numbers like {:08d}", 12);

對齊方式

每個flag都可攜帶對齊方式(最多支持64字符),

align meaning example result
%<width><flag> 右對齊 %8l " info"
%-<width><flag> 左對齊 %-8l "info "
%=<width><flag> 居中 %=8l " info "

截斷

通過!可設定對應輸出的最大長度:

align meaning example result
%<width>!<flag> 右對齊且截斷 %3!l “inf”
%-<width>!<flag> 左對齊且截斷 %-2!l “in”
%=<width>!<flag> 居中且截斷 %=1!l “i”

字符串格式化fmt

spdlog中字符串格式化使用fmt(github.com/fmtlib/fmt)庫。

格式化方式:{ [arg_id] [: (format_spec | chrono_format_spec)] }

  • arg_id:參數標識;
    • 忽略(爲空時),依次對應每一個參數;
    • 索引(數字,從0開始),引用第幾個索引;
    • 名稱,命名參數;
  • format_spec:參數格式化方式(類型、對齊、填充等);

Format Specification

格式化符說明:

format_spec ::=  [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
fill        ::=  <a character other than '{' or '}'>
align       ::=  "<" | ">" | "^" // 左、右、居中對齊
sign        ::=  "+" | "-" | " "
width       ::=  integer | {[arg_id]} // 寬度:數字或指定的參數
precision   ::=  integer | {[arg_id]} // 精度:數字或指定的參數
type        ::=  "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" 
                 | "g" | "G" | "o" | "p" | "s" | "x" | "X"

#不同的轉換下有不同的意義:

  • 整數時,表示前面添加進制前綴,如0x, 0b等;
  • 浮點數時:總是有小數點(即使沒有小數部分);

L只對數字有效,根據本地設置來輸出:如,

auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
// s == "1,234,567,890"

格式化類型:

格式化類型:

type meaning
s 字符串
c 字符
b/B 二進制
d 數字(十進制)
o 八進制
x/X 十六進制
a/A 十六進制浮點數(p表示指數)
e/E 科學計數
f/F 浮點數(包括NAN,INF),固定小數位數輸出
g/G 浮點數輸出
p 指針

示例:

fmt::format("{:*^30}", "centered");  // use '*' as a fill char
// Result: "***********centered***********"

fmt::format("{:#04x}", 0);
// Result: "0x00"

fmt::print(
  "┌{0:─^{2}}┐\n"
  "│{1: ^{2}}│\n"
  "└{0:─^{2}}┘\n", "", "Hello, world!", 20);
┌────────────────────┐
│   Hello, world!    │
└────────────────────┘

spdlog使用

spdlog默認日誌輸出級別是INFO。

默認情況下,日誌是同步模式的,可通過以下方法開啓異步模式:

size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size);

在異步模式下,日誌先存入隊列(隊列佔用的內存 = 設置的隊列大小 * slot的大小, 64位系統下slot大小爲104字節。),再由工作者線程從隊列中取出並輸出。當隊列滿時,會根據設定策略處理:

  • 阻塞新來的日誌,直到隊列中有剩餘空間(默認處理方式);
  • 丟棄新來的日誌,需要如下設定策略:
spdlog::set_async_mode(q_size, spdlog::async_overflow_policy::discard_log_msg);

異常處理

當輸出日誌時發生異常時,spdlog會向std::err 打印一條語句,爲了避免輸出的異常語句刷屏,打印頻率被限制在每分鐘一條。可通過set_error_handler來設定異常處理函數:

    //can be set globaly or per logger(logger->set_error_handler(..))
    spdlog::set_error_handler([](const std::string& msg)
    {
        std::cerr << "my err handler: " << msg << std::endl;
    });

logger

默認情況下,spdlog的默認logger爲輸出到stdout:

#    ifdef _WIN32
    auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
#    else
    auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
#    endif

在使用完logger後,要關閉掉以釋放(否則無再建立同名logger)

spdlog::drop_all();	// 關閉所有logger
spd::drop("basic_logger"); // 關閉指定logger

基礎用法

spdlog中使用{}(裏面可指定格式)作爲格式化符

以下方式把日誌輸出到默認logger上:

//#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#include "spdlog/spdlog.h"

int main() 
{
    spdlog::info("{:<30}", "left aligned");    
    spdlog::warn("Easy padding in numbers like {:08d}", 12);
    spdlog::error("Some error message with arg: {}", 1);    
    spdlog::critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
    
    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");
}

stdout日誌

以彩色方式輸出到標準輸出設備上:

#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)");
}

文件日誌

基本文件

最簡單的日誌文件:

#include "spdlog/sinks/basic_file_sink.h"
void basic_logfile_example()
{
    try 
    {
        auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
    }
    catch (const spdlog::spdlog_ex &ex)
    {
        std::cout << "Log init failed: " << ex.what() << std::endl;
    }
}

循環文件

日誌文件超過指定大小後,自動生成一個新的;並且只保留最多指定數量的日誌文件:

#include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{
    // Create a file rotating logger with 5mb size max and 3 rotated files
    auto max_size = 1024*1024 * 5;
    auto max_files = 3;
    auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
}

每日文件

每天指定時間生成一個新的日誌文件:

#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);
}

示例

設定默認日誌記錄文件並在不同地方獲取使用:

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO

#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"


void writeLog(int n) {
    for (int i = 0; i < n; ++i) {
        // 獲取logger後輸出日誌
        auto myLogger = spdlog::get("baseLogger");
        myLogger->info("{}: Hello, {}!", i + 1, "World");
        myLogger->info("Welcome to spdlog!");
        myLogger->error("Some error message with arg: {}", 1);

        // 帶文件名與行號的日誌輸出
        SPDLOG_LOGGER_INFO(myLogger, "Support for floats {:03.2f}", 1.23456);
        SPDLOG_LOGGER_WARN(myLogger, "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::error("Some error message with arg: {}", 1);
        spdlog::warn("Easy padding in numbers like {:08d}", 12);
        spdlog::info("Support for floats {:03.2f}", 1.23456);
    }
}

void testSPDLog() {
    // 設定日誌最大100k,且最多保留10個
    auto myLogger = spdlog::rotating_logger_mt("baseLogger", "logs/basic.log", 1024 * 100, 10);
    spdlog::set_default_logger(myLogger);
    myLogger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l](%@): %v"); // 非通過宏輸出的日誌%@輸出爲空
    myLogger->set_level(spdlog::level::info);

    myLogger->info("Hello, {}!", "World");

    writeLog(10);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章