Advantages of asynchronously printing logs
Log service is a common background service, which usually requires a lot of log output to record various operations and events. During the running of the service, frequently outputting log information will bring a certain performance overhead, reduce the throughput and response speed of the service, and may even cause the service to crash.
Therefore, printing logs asynchronously can solve this problem. The basic idea of asynchronously printing logs is: put the log information to be printed into a buffer queue first, and let another thread read the log information in the queue and write it into the log file. In this way, the process of log output can be asynchronous, avoiding the impact of frequent file operations on service performance.
Specifically, the advantages of asynchronously printing logs include:
-
1. Reduce interference to the main thread: Printing logs directly in the main thread will reduce the response speed of the service, while printing logs asynchronously can separate the process of log output from the main thread without causing too much interference to the main thread.
-
2. Improve service throughput: Asynchronous printing of logs can separate the process of log output from the main thread, which can avoid the impact of frequent file operations on service performance, thereby improving service throughput.
-
3. Increase the robustness of the program: Asynchronous printing of logs can separate the process of log output from the main thread, so that even if an exception occurs during the process of printing logs, it will not affect the operation of the main thread, thereby increasing the robustness of the program.
In short, asynchronously printing logs can improve the performance and stability of the service, and is a very worthwhile optimization solution.
Code sample
#include <iostream> #include <fstream> #include <string> #include <thread> #include <mutex> #include <condition_variable> #include <queue> class AsyncLogger {<!-- --> public: // Constructor, initialize the log file name and the maximum length of the queue, and open the log file stream 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) {<!-- --> // start worker thread m_worker_thread = std::thread( &AsyncLogger::worker_func, this); } // Destructor, end worker thread ~AsyncLogger() {<!-- --> // Set the m_done flag to notify the worker thread to exit m_done = true; // Notify all waiting threads that the worker thread ends m_cv. notify_all(); // wait for the worker thread to exit m_worker_thread. join(); } // log function, add log information to the queue void log(const std::string & amp; message) {<!-- --> std::unique_lock<std::mutex> lock(m_mutex); // If the queue is full, wait for a space in the queue while (m_log_queue. size() >= m_max_queue_size) {<!-- --> m_cv.wait(lock); } // Add log information to the queue m_log_queue. push(message); // Notify the worker thread that there is new log information m_cv. notify_one(); } private: // The worker thread function continuously takes log information from the queue and writes it to the log file void worker_func() {<!-- --> while (!m_done) {<!-- --> std::unique_lock<std::mutex> lock(m_mutex); // If the queue is empty, wait for new log information while (m_log_queue.empty() & amp; & amp; !m_done) {<!-- --> m_cv.wait(lock); } // Process all log information in the queue while (!m_log_queue.empty()) {<!-- --> const auto & message = m_log_queue. front(); m_log_stream << message << std::endl; m_log_queue. pop(); } // Notify all waiting threads that there is space in the queue m_cv. notify_all(); } } std::queue<std::string> m_log_queue; // log information queue const size_t m_max_queue_size; // maximum length of the queue std::ofstream m_log_stream; // log file stream std::mutex m_mutex; // mutex std::condition_variable m_cv; // condition variable for thread synchronization std::thread m_worker_thread; // worker thread bool m_done = false; // worker thread end flag }; int main() {<!-- --> AsyncLogger logger("test. log"); // Add 10000 log messages to the log queue for (int i = 0; i < 10000; + + i) {<!-- --> logger.log("log message " + std::to_string(i)); } return 0; }
Screenshot of running results
windows programming code
#include <iostream> #include <fstream> #include <string> #include <thread> #include <mutex> #include <condition_variable> #include <queue> const int MAX_LOG_QUEUE_SIZE = 1000; // log message structure struct LogMessage {<!-- --> std::string message; int level; }; // log queue class class LogQueue {<!-- --> public: LogQueue() : m_stop(false) {<!-- -->} // add log message to queue void push(const LogMessage & amp; message) {<!-- --> std::unique_lock<std::mutex> lock(m_mutex); m_queue.push(message); m_cond. notify_one(); } // Stop log queue processing void stop() {<!-- --> std::unique_lock<std::mutex> lock(m_mutex); m_stop = true; m_cond. notify_all(); } // Pop a message from the log queue, wait if the queue is empty bool try_pop(LogMessage & amp; message) {<!-- --> std::unique_lock<std::mutex> lock(m_mutex); while (m_queue.empty() & amp; & amp; !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; }; // log class class Logger {<!-- --> public: Logger(const std::string & amp; logFile) : m_logFile(logFile), m_logLevel(0), m_queueThread( & amp;Logger::processQueue, this) {<!-- -->} // Destructor, stop log queue processing and wait for thread to end ~Logger() {<!-- --> m_queue. stop(); m_queueThread. join(); } // set log level void setLogLevel(int level) {<!-- --> m_logLevel = level; } // add a log message to the queue void log(int level, const std::string & amp; message) {<!-- --> if (level >= m_logLevel) {<!-- --> LogMessage tmp; tmp.level = level; tmp. message = message; m_queue.push(tmp); } } private: // Process the messages in the log queue and write them to the log file 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; // log file name int m_logLevel; // log level LogQueue m_queue; // log message queue bool m_queueStopped; // Whether the log queue is stopped std::thread m_queueThread; // log queue processing thread }; 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; }
Run result
log.txt
[1] This message should be logged with level 1