Hone logo
Hone
Problems

Custom Logger Implementation in Go

Logging is a crucial aspect of software development, providing valuable insights into application behavior, debugging information, and performance metrics. This challenge asks you to implement a custom logger in Go, allowing for flexible log levels, formatting, and output destinations. Building a custom logger provides a deeper understanding of logging principles and allows for tailored solutions to specific application needs.

Problem Description

You are tasked with creating a Logger struct in Go that provides basic logging functionality. The logger should support different log levels (Debug, Info, Warning, Error, Fatal) and allow for custom formatting of log messages. The logger should be able to write logs to a specified io.Writer (e.g., standard output, a file, or a network connection).

Key Requirements:

  • Log Levels: Implement five log levels: Debug, Info, Warning, Error, and Fatal.
  • Formatting: Provide a mechanism to format log messages using a template string (similar to fmt.Sprintf).
  • Output Destination: The logger should accept an io.Writer interface, allowing it to write logs to various destinations.
  • Log Entry Structure: Each log entry should include the log level, timestamp, and the formatted message.
  • Fatal Logging: The Fatal log level should print the message and then immediately exit the program.

Expected Behavior:

The Logger struct should have the following methods:

  • NewLogger(output io.Writer, format string): Constructor that initializes a new logger with the specified output writer and format string.
  • Debug(message string): Logs a message with the Debug level.
  • Info(message string): Logs a message with the Info level.
  • Warning(message string): Logs a message with the Warning level.
  • Error(message string): Logs a message with the Error level.
  • Fatal(message string): Logs a message with the Fatal level and exits the program.

Edge Cases to Consider:

  • Handle nil io.Writer gracefully (e.g., by logging to standard output as a default).
  • Handle empty or invalid format strings.
  • Ensure thread safety if the logger is used concurrently. (While not strictly required for this challenge, it's a good consideration).
  • Consider how to handle errors during writing to the output destination.

Examples

Example 1:

Input:
Logger := NewLogger(os.Stdout, "%s - %s - %s\n")
Logger.Info("Application started")
Logger.Debug("Debug message")
Logger.Error("An error occurred")

Output:
[Timestamp] - Info - Application started
[Timestamp] - Debug - Debug message
[Timestamp] - Error - An error occurred

Explanation: The logger writes messages to standard output, formatted with the timestamp, log level, and the provided message.

Example 2:

Input:
file, err := os.Create("app.log")
if err != nil {
    panic(err)
}
defer file.Close()
Logger := NewLogger(file, "[%d] %s: %s\n")
Logger.Warning("Potential issue detected")

Output:
(app.log file contains)
[Timestamp] - Warning - Potential issue detected

Explanation: The logger writes messages to a file named "app.log", formatted with the timestamp, log level, and the provided message.

Example 3: (Edge Case - Fatal)

Input:
Logger := NewLogger(os.Stdout, "Fatal: %s\n")
Logger.Fatal("Critical error - exiting")

Output:
Fatal: Critical error - exiting
(Program exits immediately)

Explanation: The Fatal log level prints the message and terminates the program.

Constraints

  • The format string should support at least placeholders for the timestamp, log level, and message.
  • The timestamp should be in a readable format (e.g., "2023-10-27 10:00:00").
  • The logger should be reasonably efficient; avoid unnecessary allocations or operations.
  • The Fatal method must exit the program.
  • The log levels should be represented as strings (e.g., "Debug", "Info").

Notes

  • You can use the time package to generate timestamps.
  • The fmt package can be helpful for formatting log messages.
  • Consider using a switch statement or a map to handle different log levels.
  • Think about how to make your logger extensible for future features (e.g., adding support for different output formats or log rotation).
  • While thread safety isn't explicitly required, consider the implications if your logger might be used concurrently. A simple mutex could be used to protect the output writer.
Loading editor...
go