C++20多线程简易日志库

1,日志会输出信息,io操作会损耗性能,一般是单独开线程来做。

2,c++20 format库

3,完整实现 log.h

#ifndef _LOG_H_
#define _LOG_H_
#include <array>
#include <string>
#include <string_view>
#include <list>
#include <fstream>
#include <iostream>
#include <filesystem>
#include <format>
#include <thread>
#include <atomic>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <condition_variable>
#include <chrono>
#ifdef _MSC_VER
#include <sysinfoapi.h>
#endif
using uint = unsigned int;
struct system_time {
	uint sec;
	uint min;
	uint hour;
	uint mday;
	uint mon;
	uint year;
	uint wday;
};

static auto sys_time() {
	system_time ret{};
#ifdef _MSC_VER
	SYSTEMTIME t{};
	GetLocalTime(&t);
	ret.year = t.wYear;
	ret.mon	= t.wMonth;
	ret.mday = t.wDay;
	ret.hour = t.wHour;
	ret.min	= t.wMinute;
	ret.sec	= t.wSecond;
	ret.wday = t.wDayOfWeek;
#else
	auto now = std::chrono::system_clock::now();
	time_t tt = std::chrono::system_clock::to_time_t(now);
	auto ct = localtime(&tt);
	ret.year = ct->tm_year + 1900;
	ret.mon	= ct->tm_mon + 1;
	ret.mday	= ct->tm_mday;
	ret.hour = ct->tm_hour;
	ret.min	= ct->tm_min;
	ret.sec	= ct->tm_sec;
	ret.wday = ct->tm_wday;
#endif
	return ret;
}

template<class T>
struct mt_cv {
	T data;
	std::mutex mtx;
	std::condition_variable cv;
	bool ready{};

	mt_cv() = default;
	mt_cv(mt_cv const& ref) : data(ref.data) {}
	mt_cv(T const& ref) : data(ref) {}
	mt_cv& operator=(mt_cv const& ref) {
		this.data = ref.data;
		return *this;
	}
};

class __log {
	std::array<std::string, 4> level_ = {"DBG", "INFO", "WARN", "ERR"};
	std::array<bool, 4> open_{true, true, true, true};
	mt_cv<std::list<std::string>> msgs_;
	std::atomic<bool> write_file_{};
	std::filesystem::path file_dir_{};
	std::fstream file_;
	uint day_{};
	std::mutex file_mtx_;
	std::atomic<bool> run_{true};
	std::thread th_;

	void write(std::string_view msg) {
		//std::cout << msg << std::endl;
		if (write_file_) {
			std::lock_guard lk(file_mtx_);
			if (!file_.is_open()) {
				auto&& tm = sys_time();
				std::string name{std::format("log{}-{}-{}.txt", tm.year, tm.mon, tm.mday)};
				auto f = file_dir_ / name;
				file_.open(f, std::ios_base::out | std::ios_base::app);
			}
			file_ << msg << "\n"; //or std::endl to flush
		}
	}

public:
	__log() {
		th_  = std::thread([&]() {
			while (run_) {
				std::unique_lock ul(msgs_.mtx);
				while (msgs_.data.empty() && run_) {
					msgs_.cv.wait(ul);
				}
				if (msgs_.data.empty()) {
					ul.unlock();
					continue;
				}
				std::string msg{};
				msg.swap(msgs_.data.front());
				msgs_.data.pop_front();
				ul.unlock();
				write(msg);
			}
			for (const auto& msg : msgs_.data) {
				write(msg);
			}
		});
	}

	~__log() {
		run_ = false;
		{
			std::lock_guard mlg(msgs_.mtx);
			msgs_.cv.notify_one();
		}
		th_.join();
	}

	void write_file(bool b, std::string_view dir = "") {
		write_file_ = b;
		if (b) {
			file_dir_ = dir;
		}
	}

    template <class... Args>
	void log_msg(uint8_t level, std::string_view msgFmt, Args&&... args) {
        auto&& tm = sys_time();
		{
			std::lock_guard lg(file_mtx_);
			if (tm.mday != day_ && write_file_) {
				day_ = tm.mday;
				if (file_.is_open()) file_.close();
			}
		}
		std::string msg{std::format("[{}:{}:{}][{}]", tm.hour, tm.min, tm.sec, level_[level])};
		auto fmtArgs{std::make_format_args(args...)};
		std::string buff{std::vformat(msgFmt, fmtArgs)};
		msg.append(buff);
		std::lock_guard mlg(msgs_.mtx);
		msgs_.data.emplace_back(msg);
		msgs_.cv.notify_one();
    }
};
static __log glog;
#endif

4,测试代码 logtest.cc

#include "log.h"
using namespace std::chrono;
struct RunTime {
    system_clock::time_point start;
	RunTime() {
	    start = system_clock::now();
    }
	~RunTime() {
        std::cout << duration_cast<microseconds>(system_clock::now() - start).count() << "us" << std::endl;
    }
};

int main() {
    RunTime rt;
    int a = 10;
    int b = 20; 
    int c = 30;
    glog.write_file(true, "./");
    for (int i = 0; i < 1000000; ++i) {
        int ret = i % 6;
        if (ret == 0) {
            glog.log_msg(1, "{} {} {}", a, b, c);
        } else if (ret == 1) {
            glog.log_msg(2, "{} {} {}", a, c, b);
        } else if (ret == 2) {
            glog.log_msg(3, "{} {} {}", b, a, c);
        } else if (ret == 3) {
            glog.log_msg(3, "{} {} {}", b, c, a);
        } else if (ret == 4) {
            glog.log_msg(2, "{} {} {}", c, a, b);
        } else if (ret == 5) {
            glog.log_msg(1, "{} {} {}", c, b, a);
        }
    }
    return 0;
}

5,测试结果

g++ -g3 -std=c++2a -O3 logtest.cc -o logtest -lpthread

 

 测试100w个简单消息写入,耗时大概4.5s。如果write函数里用std::endl代替"\n"会刷新缓冲区,耗时大概9.1s。

6,目前尚未在实际项目中使用过,请慎用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值