Thread-safe Logging System

Thread-safe Logging System

Welcome to this new unit on creating a thread-safe logging system. Having previously explored various concurrency topics, such as handling multiple mutexes and solving producer-consumer problems, you're now well-equipped to tackle this practical and highly relevant challenge. The ability to manage logs from multiple threads concurrently while ensuring data integrity is crucial in software development. In this unit, you'll learn to implement a logging mechanism that allows for safe, simultaneous logging from multiple threads.

What You'll Learn

In this lesson, you'll dive into building a thread-safe logging system using C++. This includes:

  • Understanding thread safety and why it's critical for logging systems.
  • Leveraging std::mutex to synchronize log entries and prevent data corruption.
  • Utilizing file I/O operations to write logs to a shared file efficiently.

To get started, consider the following example of a simple Logger class:

#ifndef LOGGER_H
#define LOGGER_H

#include <fstream>
#include <mutex>
#include <string>

class Logger {
public:
    Logger(const std::string& filename) : file_(filename, std::ios::app) {}  // Open file in append mode

    void log(const std::string& message) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (file_.is_open()) {  // Check if file is open
            file_ << message << std::endl;
        } else {
            throw std::runtime_error("Unable to open log file.");
        }
    }

private:
    std::ofstream file_;
    std::mutex mutex_;
};

#endif // LOGGER_H

The code snippet demonstrates a simple yet effective way to ensure that log operations from multiple threads do not interfere with each other.

If we skip the std::lock_guard and directly write to the file, we risk data corruption due to concurrent writes and the log messages getting mixed up. By using a std::mutex to synchronize access to the log file, we ensure that only one thread can write to the file at a time.

Let's now explore how we can use this Logger class to create a thread-safe logging system:

int main() {
    try {
        Logger logger("log.txt");

        std::thread t1([&logger]() {
            for (int i = 0; i < 10; ++i) {
                logger.log("Thread 1: Log message " + std::to_string(i));
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
            }
        });

        std::thread t2([&logger]() {
            for (int i = 0; i < 10; ++i) {
                logger.log("Thread 2: Log message " + std::to_string(i));
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
            }
        });

        t1.join();
        t2.join();
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }

    return 0;
}

In this example, two threads (t1 and t2) are created to log messages concurrently using the Logger class. The std::lock_guard ensures that only one thread can write to the log file at a time, preventing race conditions and data corruption.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal