本项目是一个C++的日志库,名为NanoLog。它提供了高性能的日志记录功能,支持多线程环境下的异步日志记录。
该库的主要特点包括:
-
多线程日志记录:NanoLog支持多线程环境下的异步日志记录,通过使用环形缓冲区和队列缓冲区来实现日志的异步写入。
-
日志级别控制:支持日志级别控制,可以根据日志级别来过滤日志输出。
-
日志文件滚动:支持日志文件的滚动,可以根据文件大小来触发日志文件的切换。
-
MIT许可证:该库使用MIT许可证,允许自由使用、修改和分发。
-
高性能:NanoLog旨在提供高性能的日志记录功能,通过使用环形缓冲区和异步写入来减少对性能的影响。
在代码中,有一些关键的类和函数:
-
NanoLogLine
:表示一条日志记录,它包含了日志的级别、时间戳、线程ID等信息,以及日志内容的编码和解码函数。 -
BufferBase
:表示日志缓冲区的基类,定义了日志记录和日志读取的接口。 -
RingBuffer
和QueueBuffer
:分别表示环形缓冲区和队列缓冲区,用于异步写入日志。 -
FileWriter
:用于将日志写入文件,并支持日志文件的滚动。 -
NanoLogger
:表示日志记录器,负责将日志写入缓冲区,并在后台线程中异步写入日志文件。 -
initialize
:用于初始化日志记录器,可以选择非保证日志记录和保证日志记录两种模式。 -
set_log_level
和is_logged
:用于设置日志级别和判断是否需要记录某个级别的日志。
总的来说,NanoLog是一个高性能的C++日志库,适用于需要在多线程环境下进行异步日志记录的应用场景。
- Low Latency C++11 Logging Library.
- It's fast. Very fast. See Latency benchmark
- NanoLog only uses standard headers so it should work with any C++11 compliant compiler.
- Supports typical logger features namely multiple log levels, log file rolling and asynchronous writing to file.
- Zero copying of string literals.
- Lazy conversion of integers and doubles to ascii.
- No heap memory allocation for log lines representable in less than ~256 bytes.
- Minimalistic header includes. Avoids common pattern of header only library. Helps in compilation times of projects.
- Nanolog supports Guaranteed logging i.e. log messages are never dropped even at extreme logging rates.
- Nanolog also supports Non Guaranteed logging. Uses a ring buffer to hold log lines. In case of extreme logging rate when the ring gets full (i.e. the consumer thread cannot pop items fast enough), the previous log line in the slot will be dropped. Does not block producer even if the ring buffer is full.
#include "NanoLog.hpp"
int main()
{
// Ensure initialize is called once prior to logging.
// This will create log files like /tmp/nanolog1.txt, /tmp/nanolog2.txt etc.
// Log will roll to the next file after every 1MB.
// This will initialize the guaranteed logger.
nanolog::initialize(nanolog::GuaranteedLogger(), "/tmp/", "nanolog", 1);
// Or if you want to use the non guaranteed logger -
// ring_buffer_size_mb - LogLines are pushed into a mpsc ring buffer whose size
// is determined by this parameter. Since each LogLine is 256 bytes,
// ring_buffer_size = ring_buffer_size_mb * 1024 * 1024 / 256
// In this example ring_buffer_size_mb = 3.
// nanolog::initialize(nanolog::NonGuaranteedLogger(3), "/tmp/", "nanolog", 1);
for (int i = 0; i < 5; ++i)
{
LOG_INFO << "Sample NanoLog: " << i;
}
// Change log level at run-time.
nanolog::set_log_level(nanolog::LogLevel::CRIT);
LOG_WARN << "This log line will not be logged since we are at loglevel = CRIT";
return 0;
}
- A google search for fast logger C++ gives the first result spdlog
- There's an interesting article on worst case latency by the author of g3log
- So let's benchmark NanoLog vs spdlog vs g3log vs reckless.
- Take a look at nano_vs_spdlog_vs_g3log_vs_reckless.cpp
- Benchmark was compiled with g++ 4.8.4 running Linux Mint 17 on Intel(R) Core(TM) i7-2630QM CPU @ 2.00GHz
Logger | 50th | 75th | 90th | 99th | 99.9th | Worst | Average |
---|---|---|---|---|---|---|---|
nanolog_guaranteed | 0 | 1 | 1 | 4 | 8 | 68 | 0.347930 |
spdlog | 3 | 3 | 3 | 5 | 11 | 129 | 2.588590 |
g3log | 5 | 6 | 6 | 10 | 19 | 186 | 5.206230 |
reckless | 0 | 0 | 1 | 1 | 175 | 1861 | 1.829760 |
Logger | 50th | 75th | 90th | 99th | 99.9th | Worst | Average |
---|---|---|---|---|---|---|---|
nanolog_guaranteed | 0 | 1 | 1 | 2 | 5 | 55 | 0.457240 |
nanolog_guaranteed | 0 | 1 | 1 | 2 | 5 | 81 | 0.459090 |
spdlog | 2 | 3 | 3 | 3 | 5 | 25 | 2.449580 |
spdlog | 2 | 3 | 3 | 3 | 6 | 21 | 2.457150 |
g3log | 4 | 5 | 6 | 12 | 18 | 64 | 4.574850 |
g3log | 4 | 5 | 6 | 12 | 20 | 84 | 4.586590 |
reckless | 0 | 1 | 1 | 11 | 417 | 1592 | 4.412750 |
reckless | 0 | 1 | 1 | 12 | 417 | 2138 | 4.427810 |
Logger | 50th | 75th | 90th | 99th | 99.9th | Worst | Average |
---|---|---|---|---|---|---|---|
nanolog_guaranteed | 0 | 1 | 1 | 3 | 6 | 91 | 0.450700 |
nanolog_guaranteed | 0 | 1 | 2 | 3 | 7 | 90 | 0.676050 |
nanolog_guaranteed | 0 | 1 | 2 | 3 | 7 | 262 | 0.680430 |
spdlog | 2 | 2 | 2 | 4 | 6 | 6729 | 1.803570 |
spdlog | 3 | 3 | 3 | 5 | 8 | 25 | 2.679420 |
spdlog | 3 | 3 | 3 | 5 | 10 | 50 | 2.685230 |
g3log | 4 | 4 | 6 | 17 | 27 | 53 | 4.385530 |
g3log | 4 | 4 | 6 | 16 | 26 | 55 | 4.435680 |
g3log | 6 | 7 | 8 | 19 | 29 | 1031 | 5.896250 |
reckless | 1 | 1 | 1 | 298 | 1643 | 3070 | 11.208420 |
reckless | 1 | 1 | 1 | 382 | 2266 | 3006 | 12.310360 |
reckless | 1 | 1 | 1 | 167 | 2839 | 3249 | 12.754520 |
Logger | 50th | 75th | 90th | 99th | 99.9th | Worst | Average |
---|---|---|---|---|---|---|---|
nanolog_guaranteed | 0 | 1 | 2 | 3 | 6 | 53 | 0.582140 |
nanolog_guaranteed | 0 | 1 | 2 | 3 | 7 | 70 | 0.608980 |
nanolog_guaranteed | 0 | 1 | 2 | 3 | 7 | 62 | 0.803630 |
nanolog_guaranteed | 0 | 1 | 2 | 3 | 7 | 61 | 0.797270 |
spdlog | 2 | 2 | 2 | 3 | 5 | 40 | 1.767930 |
spdlog | 2 | 2 | 2 | 3 | 6 | 21 | 1.768640 |
spdlog | 3 | 3 | 3 | 4 | 8 | 24 | 2.676170 |
spdlog | 3 | 3 | 3 | 5 | 10 | 31 | 2.698580 |
g3log | 4 | 4 | 5 | 17 | 30 | 7766 | 4.620760 |
g3log | 6 | 7 | 9 | 21 | 35 | 8478 | 6.368940 |
g3log | 6 | 7 | 8 | 22 | 32 | 1327 | 7.023880 |
g3log | 7 | 8 | 9 | 23 | 36 | 8470 | 7.831750 |
reckless | 1 | 1 | 1 | 506 | 3477 | 9224 | 18.959310 |
reckless | 1 | 1 | 1 | 479 | 3636 | 8471 | 19.181160 |
reckless | 1 | 1 | 1 | 530 | 2990 | 11658 | 19.245110 |
reckless | 1 | 1 | 1 | 436 | 3641 | 8626 | 19.342780 |
- Take a look at non_guaranteed_nanolog_benchmark.cpp for the code used to generate the latency numbers.
- Benchmark was compiled with g++ 4.8.4 running Linux Mint 17 on Intel(R) Core(TM) i7-2630QM CPU @ 2.00GHz
Thread count: 1
Average NanoLog Latency = 131 nanoseconds
Thread count: 2
Average NanoLog Latency = 182 nanoseconds
Average NanoLog Latency = 272 nanoseconds
Thread count: 3
Average NanoLog Latency = 216 nanoseconds
Average NanoLog Latency = 209 nanoseconds
Average NanoLog Latency = 315 nanoseconds
Thread count: 4
Average NanoLog Latency = 229 nanoseconds
Average NanoLog Latency = 221 nanoseconds
Average NanoLog Latency = 233 nanoseconds
Average NanoLog Latency = 332 nanoseconds
Thread count: 5
Average NanoLog Latency = 247 nanoseconds
Average NanoLog Latency = 240 nanoseconds
Average NanoLog Latency = 320 nanoseconds
Average NanoLog Latency = 345 nanoseconds
Average NanoLog Latency = 383 nanoseconds
- g3log has support for crash handling. I do not see the point in re-inventing the wheel. Have a look at that what's done there and if it works for you, give Kjell credit and use his crash handling code.
- NanoLog uses standard library chrono timestamps. Your platform / os may have non-standard but faster timestamps. Use them!