Hone logo
Hone
Problems

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, and FATAL. 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, DEBUG messages should be ignored.
  • Handle FATAL Messages: When a FATAL level 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 represents TIMESTAMP 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.Sprintf function is useful for formatting log messages.
  • For concurrent safety, think about using mutexes.
  • The FATAL log level should cause the program to exit immediately using os.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 DefaultFormat constant can be provided as a convenience.
Loading editor...
go