15所 / 待分类 / nanolog入门学习

分享

   

nanolog入门学习

2020-09-26  15所

如何高效的写log, 目前GitHub有很多log的轮子,简单的测试了一下,发现nanolog是最快的(当然这个基本满足需求,所有就没有再找其他的)
nanolog有两个版本.

1. nanolog的基本思想

格式化是一个很耗时的操作,因此nanolog把格式化的内容放到线程里头去做。待写入的日志全部放到一个buffer里头,标志写入每个日志的类型。目前支持int/uint/double/string/char/char*这些类型。

2. 例子

LOG_INFO << 'Logging ' << benchmark << i << 0 << 'K' << -42.42;
  • 1
  • 1

LOG_INFO返回的是一个NanoLogLine的对象,NanoLogLine里支持各个类型形参的opertor<<, 如下:

    NanoLogLine& operator<<(char arg);    NanoLogLine& operator<<(int32_t arg);    NanoLogLine& operator<<(uint32_t arg);    NanoLogLine& operator<<(int64_t arg);    NanoLogLine& operator<<(uint64_t arg);    NanoLogLine& operator<<(double arg);    NanoLogLine& operator<<(std::string const & arg);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.调用过程

3.1 待输出的日志放到NanoLogLine对象里

#define NANO_LOG(LEVEL) nanolog::NanoLog() == nanolog::NanoLogLine(LEVEL, __FILE__, __func__, __LINE__)#define LOG_INFO nanolog::is_logged(nanolog::LogLevel::INFO) && NANO_LOG(nanolog::LogLevel::INFO)#define LOG_WARN nanolog::is_logged(nanolog::LogLevel::WARN) && NANO_LOG(nanolog::LogLevel::WARN)#define LOG_CRIT nanolog::is_logged(nanolog::LogLevel::CRIT) && NANO_LOG(nanolog::LogLevel::CRIT)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

3.2 把NanoLogLine对象作为参数调用NanoLog的operator ==, 把NanoLogLine 的内容add到atomic_nanologger(NanoLogger)里

struct NanoLog{bool operator==(NanoLogLine &);};std::atomic < NanoLogger * > atomic_nanologger;bool NanoLog::operator==(NanoLogLine & logline){atomic_nanologger.load(std::memory_order_acquire)->add(std::move(logline));return true;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3.3 NanoLogger对象在构造的时候,会有开启一个线程( m_thread(&NanoLogger::pop, this), 线程的钩子函数就是pop,pop函数负责把日志写入文件

NanoLogger的线程进行写日志

class NanoLogger{public:NanoLogger(NonGuaranteedLogger ngl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb): m_state(State::INIT), m_buffer_base(new RingBuffer(std::max(1u, ngl.ring_buffer_size_mb) * 1024 * 4)), m_file_writer(log_directory, log_file_name, std::max(1u, log_file_roll_size_mb)), m_thread(&NanoLogger::pop, this){m_state.store(State::READY, std::memory_order_release);}NanoLogger(GuaranteedLogger gl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb): m_state(State::INIT), m_buffer_base(new QueueBuffer()), m_file_writer(log_directory, log_file_name, std::max(1u, log_file_roll_size_mb)), m_thread(&NanoLogger::pop, this){m_state.store(State::READY, std::memory_order_release);}~NanoLogger(){//等待写日志线程结束m_state.store(State::SHUTDOWN);m_thread.join();}void add(NanoLogLine && logline){m_buffer_base->push(std::move(logline));}void pop(){// Wait for constructor to complete and pull all stores done there to this thread / core.while (m_state.load(std::memory_order_acquire) == State::INIT)std::this_thread::sleep_for(std::chrono::microseconds(50));NanoLogLine logline(LogLevel::INFO, nullptr, nullptr, 0);//m_state在构造函数完成时,就是ready状态while (m_state.load() == State::READY){//有log就写,否则就是睡眠50微妙。如果我们不需要实时,可以让其睡眠更久一点if (m_buffer_base->try_pop(logline))m_file_writer.write(logline);elsestd::this_thread::sleep_for(std::chrono::microseconds(50));}// Pop and log all remaining entries// 最后调用析构函数时,m_state为shutdown,最后一次写入logwhile (m_buffer_base->try_pop(logline)){m_file_writer.write(logline);}}private:enum class State{INIT,READY,SHUTDOWN};std::atomic < State > m_state;std::unique_ptr < BufferBase > m_buffer_base;FileWriter m_file_writer;std::thread m_thread;};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

4. log的类型

void initialize(GuaranteedLogger gl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb);void initialize(NonGuaranteedLogger ngl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb);
  • 1
  • 2
  • 1
  • 2

在初始化的时候,需要指定logger的类型,GuaranteedLogger 保证日志不丢失,使用的是一个queue_buffer. NonGuaranteedLogger则不保证日志完整(当写入很快很快的时候),使用的是一个ring_buffer. 。一般来说,日志都不会丢失啦。

5. 其他

nanolog在每一行日志里头,都会加上时间戳、日志级别、线程id、文件名、函数名、行数行号。 例子如下:

[2019-04-15 00:09:15.505866][INFO][140067778569984][non_guaranteed_nanolog_benchmark.cpp:nanolog_benchmark:25] Logging benchmark6580K-42.42
  • 1
  • 1

写那么多信息,一方面需要更多内存,另一个方面频繁调用获取时间戳和线程id的API也会消耗一点时间,因此可以根据需要把不需要的信息去掉,减少消耗。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。如发现有害或侵权内容,请点击这里 或 拨打24小时举报电话:4000070609 与我们联系。

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多
    喜欢该文的人也喜欢 更多

    ×
    ×

    ¥.00

    微信或支付宝扫码支付:

    开通即同意《个图VIP服务协议》

    全部>>