鏡像庫 https://gitee.com/yctxkj/spdlog.git
spdlog是基於C++11實現的一款純頭文件的日誌管理庫(git地址:https://github.com/gabime/spdlog,API說明:https://spdlog.docsforge.com/v1.x/1.quickstart/):
- 配置特別簡單,僅包含頭文件即可;
- 寫日誌方式簡單明瞭;
- 可實現自動按日期創建日誌文件/定時創建日誌文件;
- 可自定義日誌格式;
- 可以輸出當前輸出日誌所在的文件及函數;
- 可自定義文檔大小;
- 可將不同級別的信息輸出到不同日誌文件;
- 多平臺等。
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(https://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);
}