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 goroutines concurrently while ensuring data integrity is crucial to software development. In this unit, you'll learn to implement a logging mechanism that allows for safe, simultaneous logging from multiple goroutines.

What You'll Learn

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

  • Understanding thread safety and why it is critical for logging systems.
  • Leveraging sync.Mutex to synchronize log entries and prevent data corruption.
  • Utilizing the os package for file I/O operations to write logs to a shared file efficiently.

Logger Struct Example

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

package main

import (
    "fmt"
    "os"
    "sync"
)

type Logger struct {
    file  *os.File
    mutex sync.Mutex
}

func NewLogger(filename string) (*Logger, error) {
    file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        return nil, fmt.Errorf("unable to open log file: %w", err)
    }
    return &Logger{file: file}, nil
}

func (l *Logger) Log(message string) error {
    l.mutex.Lock()
    defer l.mutex.Unlock()
    
    if l.file == nil {
        return fmt.Errorf("log file is not open")
    }
    
    _, err := fmt.Fprintln(l.file, message)
    if err != nil {
        return fmt.Errorf("failed to write log: %w", err)
    }
    
    return nil
}

func (l *Logger) Close() error {
    l.mutex.Lock()
    defer l.mutex.Unlock()
    
    if l.file != nil {
        return l.file.Close()
    }
    return nil
}

The code snippet demonstrates a simple yet effective way to ensure that log operations from multiple goroutines do not interfere with one another.

If we skip the mutex and write directly to the file, we risk data corruption due to concurrent writes and log messages becoming intermingled. By using a sync.Mutex to synchronize access to the log file, we ensure that only one goroutine can write to the file at a time.

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