异步打印日志的优点

日志服务是一个常见的后台服务,它通常需要大量的日志输出来记录各种操作和事件。在服务的运行过程中,频繁地输出日志信息会带来一定的性能开销,降低服务的吞吐能力和响应速度,甚至可能导致服务崩溃。

因此,异步打印日志可以解决这个问题。异步打印日志的基本思路是:将要打印的日志信息先放入一个缓存队列中,由另一个线程去读取队列中的日志信息,并将其写入到日志文件中。这样就可以将日志输出的过程异步化,避免了频繁的文件操作对服务性能的影响。

具体来说,异步打印日志的优点包括:

  • 1.减少对主线程的干扰:在主线程中直接打印日志会降低服务的响应速度,而异步打印日志可以将日志输出的过程与主线程分离,不会对主线程造成太大的干扰。
  • 2.提高服务的吞吐能力:异步打印日志可以将日志输出的过程与主线程分离,这样可以避免频繁的文件操作对服务性能的影响,从而提高服务的吞吐能力。
  • 3.增加程序的健壮性:异步打印日志可以将日志输出的过程与主线程分离,这样即使在打印日志的过程中出现异常,也不会影响主线程的运行,从而增加程序的健壮性。

总之,异步打印日志可以提高服务的性能和稳定性,是一个非常值得采用的优化方案。

代码示例

#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

class AsyncLogger {
public:
    // 构造函数,初始化日志文件名和队列最大长度,并打开日志文件流
    AsyncLogger(const std::string& log_filename, size_t max_queue_size = 10000) :
        m_max_queue_size(max_queue_size),
        m_log_stream(log_filename, std::ios::app)
    {
        // 启动工作线程
        m_worker_thread = std::thread(&AsyncLogger::worker_func, this);
    }

    // 析构函数,结束工作线程
    ~AsyncLogger() {
        // 设置 m_done 标志,通知工作线程退出
        m_done = true;
        // 通知所有等待线程,工作线程结束
        m_cv.notify_all();
        // 等待工作线程退出
        m_worker_thread.join();
    }

    // 日志函数,将日志信息添加到队列中
    void log(const std::string& message) {
        std::unique_lock<std::mutex> lock(m_mutex);
        // 如果队列已满,则等待队列有空位
        while (m_log_queue.size() >= m_max_queue_size) {
            m_cv.wait(lock);
        }
        // 添加日志信息到队列中
        m_log_queue.push(message);
        // 通知工作线程有新的日志信息
        m_cv.notify_one();
    }

private:
    // 工作线程函数,不断从队列中取出日志信息并写入日志文件中
    void worker_func() {
        while (!m_done) {
            std::unique_lock<std::mutex> lock(m_mutex);
            // 如果队列为空,则等待新的日志信息
            while (m_log_queue.empty() && !m_done) {
                m_cv.wait(lock);
            }

            // 处理队列中的所有日志信息
            while (!m_log_queue.empty()) {
                const auto& message = m_log_queue.front();
                m_log_stream << message << std::endl;
                m_log_queue.pop();
            }
            // 通知所有等待线程,队列中有空位了
            m_cv.notify_all();
        }
    }

    std::queue<std::string> m_log_queue; // 日志信息队列
    const size_t m_max_queue_size; // 队列最大长度
    std::ofstream m_log_stream; // 日志文件流
    std::mutex m_mutex; // 互斥锁
    std::condition_variable m_cv; // 条件变量,用于线程同步
    std::thread m_worker_thread; // 工作线程
    bool m_done = false; // 工作线程结束标志
};

int main() {
    AsyncLogger logger("test.log");

    // 向日志队列中添加 10000 条日志信息
    for (int i = 0; i < 10000; ++i) {
        logger.log("log message " + std::to_string(i));
    }
    
    return 0;
}

运行结果截图

微服务日志统一打印_#include

windows编程代码

#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

const int MAX_LOG_QUEUE_SIZE = 1000;

// 日志消息结构体
struct LogMessage {
    std::string message;
    int level;
};

// 日志队列类
class LogQueue {
public:
    LogQueue() : m_stop(false) {}
    
    // 添加日志消息到队列
    void push(const LogMessage& message) {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_queue.push(message);
        m_cond.notify_one();
    }
    
    // 停止日志队列处理
    void stop() {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_stop = true;
        m_cond.notify_all();
    }
    
    // 从日志队列中弹出一条消息,如果队列为空,则等待
    bool try_pop(LogMessage& message) {
        std::unique_lock<std::mutex> lock(m_mutex);
        while (m_queue.empty() && !m_stop) {
            m_cond.wait(lock);
        }
        if (m_queue.empty()) {
            return false;
        }
        message = m_queue.front();
        m_queue.pop();
        return true;
    }
    
private:
    std::queue<LogMessage> m_queue;
    std::mutex m_mutex;
    std::condition_variable m_cond;
    bool m_stop;
};

// 日志类
class Logger {
public:
    Logger(const std::string& logFile) : m_logFile(logFile), m_logLevel(0), m_queueThread(&Logger::processQueue, this) {}
    
    // 析构函数,停止日志队列处理并等待线程结束
    ~Logger() {
        m_queue.stop();
        m_queueThread.join();
    }
    
    // 设置日志级别
    void setLogLevel(int level) {
        m_logLevel = level;
    }
    
    // 添加一条日志消息到队列
    void log(int level, const std::string& message) {
        if (level >= m_logLevel) {
			LogMessage tmp;
			tmp.level = level;
			tmp.message = message;
            m_queue.push(tmp);
        }
    }
    
private:
    // 处理日志队列中的消息,将其写入日志文件
    void processQueue() {
        std::ofstream logStream(m_logFile);
        if (!logStream.is_open()) {
            std::cerr << "Error opening log file " << m_logFile << std::endl;
            return;
        }
        while (true) {
            LogMessage message;
            if (!m_queue.try_pop(message)) {
                if (m_queueStopped) {
                    break;
                } else {
                    continue;
                }
            }
            logStream << "[" << message.level << "] " << message.message << std::endl;
        }
        logStream.close();
    }
    
    std::string m_logFile;  // 日志文件名
    int m_logLevel;  // 日志级别
    LogQueue m_queue;  // 日志消息队列
    bool m_queueStopped;  // 日志队列是否停止
    std::thread m_queueThread;  // 日志队列处理线程
};


int main() {
    Logger logger("log.txt");
    logger.setLogLevel(1);
    
    logger.log(0, "This message should not be logged");
    logger.log(1, "This message should be logged with level 1");
    
    return 0;
}

运行结果

log.txt

[1] This message should be logged with level 1