nanolog入门学习

如何高效的写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;

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

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)
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;
	}
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);
				else
					std::this_thread::sleep_for(std::chrono::microseconds(50));
			}

			// Pop and log all remaining entries
			// 最后调用析构函数时,m_state为shutdown,最后一次写入log
			while (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;
	};
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);

在初始化的时候,需要指定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

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值