Implement a Custom Go Logger
In software development, logging is a crucial aspect of debugging, monitoring, and understanding application behavior. While Go's standard library offers basic logging capabilities, building a custom logger allows for greater control over output formatting, log levels, and destinations. This challenge asks you to create a flexible and configurable logger for your Go applications.
Problem Description
Your task is to implement a custom logger in Go that can:
- Support Multiple Log Levels: The logger should support distinct log levels such as
DEBUG,INFO,WARN,ERROR, andFATAL. Each level should represent a different severity of a log message. - Configurable Output Destination: The logger should be able to write log messages to standard output (
os.Stdout), standard error (os.Stderr), or a specified file. - Customizable Output Format: Allow users to define the format of each log entry. This format should include the timestamp, log level, and the actual message.
- Filter by Log Level: Users should be able to set a minimum log level, and only messages with that level or higher severity should be logged. For example, if the minimum level is
INFO,DEBUGmessages should be ignored. - Handle
FATALMessages: When aFATALlevel message is logged, the application should terminate after logging the message.
Examples
Example 1:
package main
import (
"os"
"your_module/logger" // Assuming your logger is in a package named logger
)
func main() {
// Configure logger to write to stdout with INFO level and default format
log := logger.NewLogger(logger.InfoLevel, os.Stdout, logger.DefaultFormat)
log.Debug("This is a debug message") // Should not be printed
log.Info("Application started successfully")
log.Warn("Potential issue detected")
log.Error("A critical error occurred")
}
2023-10-27 10:00:00 INFO Application started successfully
2023-10-27 10:00:01 WARN Potential issue detected
2023-10-27 10:00:02 ERROR A critical error occurred
Explanation: The DEBUG message is not printed because the minimum log level is set to INFO. The other messages are printed with the default format, including timestamp, level, and message.
Example 2:
package main
import (
"os"
"your_module/logger"
)
func main() {
// Configure logger to write to stderr with WARN level and custom format
customFormat := "[%s] %s: %s\n"
log := logger.NewLogger(logger.WarnLevel, os.Stderr, customFormat)
log.Info("This is an info message") // Should not be printed
log.Warn("Database connection lost")
log.Error("User authentication failed")
}
[WARN] 2023-10-27 10:05:00: Database connection lost
[ERROR] 2023-10-27 10:05:01: User authentication failed
Explanation: The INFO message is ignored. The WARN and ERROR messages are printed to os.Stderr using the custom format [LEVEL] TIMESTAMP: MESSAGE\n.
Example 3:
package main
import (
"os"
"your_module/logger"
)
func main() {
// Configure logger to write to a file with DEBUG level
logFile, _ := os.Create("app.log")
defer logFile.Close()
log := logger.NewLogger(logger.DebugLevel, logFile, logger.DefaultFormat)
log.Debug("Starting up the service...")
log.Fatal("Configuration file not found") // Application will terminate after this
}
Explanation: The DEBUG message will be written to app.log. The FATAL message will also be written to app.log, and then the program will exit. The content of app.log will be:
2023-10-27 10:10:00 DEBUG Starting up the service...
2023-10-27 10:10:01 FATAL Configuration file not found
Constraints
- Log Levels: Must support at least
DEBUG,INFO,WARN,ERROR,FATAL. - Output Destinations: Support
os.Stdout,os.Stderr, and*os.File. - Format String: The format string can contain placeholders for timestamp, level, and message. A common and recommended format string is
"%s %s %s\n"which representsTIMESTAMP LEVEL MESSAGE\n. - Concurrency: The logger should be safe for concurrent use (multiple goroutines logging simultaneously).
- Performance: Logging operations should be reasonably performant and not introduce significant overhead in typical application scenarios.
Notes
- Consider using Go's
time.Now()for timestamps. - The
fmt.Sprintffunction is useful for formatting log messages. - For concurrent safety, think about using mutexes.
- The
FATALlog level should cause the program to exit immediately usingos.Exit(1)after logging. - You'll need to define constants for the log levels and potentially a format string.
- Encapsulate your logger logic within a struct to hold its configuration and state.
- The
DefaultFormatconstant can be provided as a convenience.