An Analysis of Mainstream Go Logging Libraries:Learning How to Integrate Log Rotation and Segmentation Functionality from a Design Perspective

 

In existing logging libraries, including the slog logging library introduced in go 1.21.0, typically support log file rotation and segmentation. However, these functionalities are not built-in directly but require manual configuration to enable.

This article was first published in the Medium MPP plan. If you are a Medium user, please follow me on Medium. Thank you very much.

This article will explore several popular logging libraries, such as Logrus, zap, and the official Slog. I will analyze the key design elements of these libraries and discuss how they support the configuration of log rotation and segmentation functionalities.

Are you ready? Grab a cup of your favorite coffee or tea, and let’s dive into the exploration.

Analysis of logrus, zap, and slog Designs

When comparing and analyzing the designs of the logrus, zap, and slog logging libraries, a notable commonality is that they all include the crucial attribute io.Writer. This attribute plays a central role in the design of logging frameworks as it determines the destination of log output.

logrus Logging Library

logrus is a feature-rich Go logging library that provides structured logging and log level control.

When using logrus, you can create a Logger instance by calling the logrus.New() function. Through this instance, you can perform various operations such as customizing the log output location and printing logs. Take a look at the following code:

1
2
3
4
5
6
7
8
logger := logrus.New()
logger.Out = os.Stdout // stdout
// or redirects to file
//out, err := os.OpenFile("file.log", os.O_CREATE|os.O_WRONLY, 0666)
//if err != nil {
// panic(err)
//}
//logger.Out = out

The definition of the Logger struct is as follows:

1
2
3
4
5
6
type Logger struct {
    Out       io.Writer
    Hooks     LevelHooks
    Formatter Formatter
    // other fields...
}

The key attribute Out, of type io.Writer, is used to specify the output destination of the logs, whether it’s standard output, a file, or any other custom output medium.

zap Logging Library

zap is a high-performance logging library that provides structured logging, multi-level log control, and flexible configuration options.

Similar to logrus, zap also allows configuring the log output location through settings. However, the implementation approach is slightly different. In zap, log output is achieved through the configuration of zapcore.Core. When creating an instance of zapcore.Core, you need to specify a zapcore.WriteSyncer interface implementation as a parameter, which directly determines the log’s output destination. To create a zapcore.WriteSyncer instance, the zapcore.AddSync() function is commonly used, which takes a parameter of type io.Writer.

Here’s a basic example of creating a log instance using zap:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
writer := zapcore.AddSync(os.Stdout) // use stdout
core := zapcore.NewCore(
    zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
    writer,
    zap.InfoLevel,
)
logger := zap.New(core)
defer logger.Sync() // Flush any buffered log entries

// use logger

The key lies in the zapcore.AddSync() function, which takes a parameter of type io.Writer. This parameter is used to specify the log’s output destination, whether it’s standard output, a file, or any other custom output medium.

slog Logging Library

slog is an official logging library introduced in go 1.21.0, providing structured logging. If you want to learn more about the slog logging library,

Similar to logrus and zap, slog also allows users to set the log output destination by specifying an io.Writer parameter when creating an implementation of slog.Handler.

1
2
textLogger := slog.New(slog.NewTextHandler(os.Stdout, nil))
jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

In both of these functions, the first parameter of slog.NewTextHandler and slog.NewJSONHandler is of type io.Writer.

Summary of the Analysis

In the analysis of the three mainstream logging libraries, logrus, zap, and slog, we can identify a crucial commonality: they all rely on the io.Writer interface when handling log output. These logging libraries use the io.Writer interface as a key parameter type to set the log’s output destination.

Pasted image 20240212220811

Mechanism and Practice of Log Rotation and Segmentation Functionality

Mechanism of Implementation

After analyzing the designs of the logrus, zap, and slog logging libraries, we have discovered their commonalities. Now, let’s delve into the mechanism of log rotation and segmentation functionality.

To implement log file rotation and segmentation, we typically rely on third-party libraries such as lumberjack. There are other similar libraries available, but we won’t list them all here.

lumberjack is a library specifically designed for log rotation and segmentation. Its role can be likened to a pluggable component. By configuring this component and integrating it into the chosen logging library, we can achieve log file rotation and segmentation functionality.

The code for initializing the lumberjack component is as follows:

1
2
3
4
5
6
7
8
log := &lumberjack.Logger{
    Filename:   "/path/file.log", // location of the log file
    MaxSize:    10,               // Maximum file size (in MB)
    MaxBackups: 3,                // Maximum number of old files to keep
    MaxAge:     28,               // Maximum number of days to retain old files
    Compress:   true,             // whether to compress/archive old files
    LocalTime:  true,             // create timestamps using local time
}

In this example, we create a lumberjack.Logger instance and set the following parameters:

  • Filename: Specifies the storage path of the log file.
  • MaxSize: Triggers log rotation when the log file reaches a certain size in MB.
  • MaxBackups: Specifies the maximum number of old log files to keep.
  • MaxAge: Sets the maximum number of days to retain old log files.
  • Compress: Determines whether to compress/archive old log files (e.g., convert them to .gz).
  • LocalTime: Creates timestamps using local time.

It’s important to note that the Logger struct of lumberjack implements the io.Writer interface. This means that all the core logic related to log file rotation and segmentation is encapsulated in the Write method. This implementation also allows the Logger struct to be integrated into any logging library that supports the io.Writer parameter.

Now that we understand these details, you should have a good understanding of how to implement log rotation and segmentation functionality. Since the Logger struct of lumberjack implements the io.Writer interface, you can pass it to third-party libraries for seamless integration and configuration.

Practice

Implementation with logrus Logging Library

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
log := &lumberjack.Logger{
    Filename:   "/path/file.log", // location of log file
    MaxSize:    10,               // Maximum file size in MB
    MaxBackups: 3,                // Maximum number of old files to keep
    MaxAge:     28,               // Maximum number of days to retain old files
    Compress:   true,             // whether to compress/archive old files
    LocalTime:  true,             // create timestamps using local time
}
logger := logrus.New()
logger.Out = log

Implementation with zap Logging Library

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
log := &lumberjack.Logger{
    Filename:   "/path/file.log", // location of log file
    MaxSize:    10,               // Maximum file size in MB
    MaxBackups: 3,                // Maximum number of old files to keep
    MaxAge:     28,               // Maximum number of days to retain old files
    Compress:   true,             // whether to compress/archive old files
    LocalTime:  true,             // create timestamps using local time
}
writer := zapcore.AddSync(log)
core := zapcore.NewCore(
    zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
    writer,
    zap.InfoLevel,
)
logger := zap.New(core)
defer logger.Sync() // Flush any buffered log entries

Implementation with slog Logging Library

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
log := &lumberjack.Logger{
    Filename:   "/path/file.log", // location of log file
    MaxSize:    10,               // Maximum file size (in MB)
    MaxBackups: 3,                // Maximum number of old files retained
    MaxAge:     28,               // Maximum number of days to keep old files
    Compress:   true,             // Whether to compress/archive old files
    LocalTime:  true,             // Create a timestamp with local time
}
textLogger := slog.New(slog.NewTextHandler(log, nil))
jsonLogger := slog.New(slog.NewJSONHandler(log, nil))

In this article, we have analyzed the design elements of the three popular logging libraries, logrus, zap, and slog. Although they have differences in the details of creating log instances, they all rely on the io.Writer interface parameter to handle log output. By mastering how to configure the io.Writer parameter and combining it with the use of the lumberjack library, we can achieve log file rotation and segmentation functionality.

Even if new logging libraries are introduced in the future, we can quickly integrate log file rotation and segmentation functionality using similar methods.

true
Last updated on Jun 28, 2024 18:21 CST
Built with Hugo
Theme Stack designed by Jimmy