异步日志服务器

该日志服务器是基于双缓冲区来接收并且写入日志,采用了单例模式,线程,信号量,文件流机制,并且能根据日志文件大小重写创建文件并且写入(采用c++17)

#include <iostream>
#include <fstream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>
#include <ctime>
#include <stdexcept>
#include <string>
#include <filesystem>

class AsyncLogger {
public:
    // 获取单例实例
    static AsyncLogger& getInstance() {
        static AsyncLogger instance;
        return instance;
    }

    // 删除拷贝构造和赋值运算符
    AsyncLogger(const AsyncLogger&) = delete;
    AsyncLogger& operator=(const AsyncLogger&) = delete;

    // 日志接口
    void info(const std::string& message) {
        log("INFO", message);
    }

    void error(const std::string& message) {
        log("ERROR", message);
    }

    void debug(const std::string& message) {
        log("DEBUG", message);
    }

private:
    AsyncLogger() : exit_flag(false), max_file_size(10 * 1024 * 1024) { // 10MB
        openLogFile();
        // 启动日志线程
        log_thread = std::thread(&AsyncLogger::processLogs, this);
    }

    ~AsyncLogger() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            exit_flag = true;
        }
        cv.notify_all();
        if (log_thread.joinable())
            log_thread.join();
        if (log_file.is_open())
            log_file.close();
    }

    // 打开日志文件
    void openLogFile() {
        if (log_file.is_open()) {
            log_file.close();
        }
        log_file.open("application.log", std::ios::out | std::ios::app);
        if (!log_file.is_open()) {
            throw std::runtime_error("无法打开日志文件");
        }
    }

    // 检查日志文件大小
    void checkLogFileSize() {
        if (log_file.tellp() >= max_file_size) {
            log_file.close();
            // 重命名当前日志文件
            std::string new_file_name = "application_" + getTimestamp() + ".log";
            std::filesystem::rename("application.log", new_file_name);
            openLogFile(); // 重新打开新的日志文件
        }
    }

    // 日志记录函数
    void log(const std::string& level, const std::string& message) {
        std::string timestamp = getTimestamp();
        std::unique_lock<std::mutex> lock(queue_mutex);
        active_buffer.emplace("[" + timestamp + "] [" + level + "] " + message + "\n");
        cv.notify_one();
    }

    // 处理日志的线程函数
    void processLogs() {
        while (true) {
            std::queue<std::string> local_queue;
            {
                std::unique_lock<std::mutex> lock(queue_mutex);
                // 等待有日志或退出信号
                cv.wait(lock, [this] { return exit_flag || !active_buffer.empty(); });

                if (exit_flag && active_buffer.empty())
                    break;

                // 交换缓冲区
                std::swap(active_buffer, local_queue);
            }

            // 将日志写入文件
            while (!local_queue.empty()) {
                checkLogFileSize(); // 检查文件大小
                log_file << local_queue.front();
                local_queue.pop();
            }
            // 确保日志被写入磁盘
            log_file.flush();
        }
    }

    // 获取当前时间戳
    std::string getTimestamp() {
        auto now = std::chrono::system_clock::now();
        std::time_t now_time = std::chrono::system_clock::to_time_t(now);
        char buf[20];
        std::strftime(buf, sizeof(buf), "%Y%m%d_%H%M%S", std::localtime(&now_time)); // 格式化为适合文件名的形式
        return std::string(buf);
    }

    std::ofstream log_file;
    std::queue<std::string> active_buffer;
    std::mutex queue_mutex;
    std::condition_variable cv;
    std::thread log_thread;
    bool exit_flag;
    const std::streamoff max_file_size; // 最大文件大小
};