Hone logo
Hone
Problems

Go Interface Satisfaction: The Polymorphic Logger

Go's interfaces are a powerful mechanism for achieving polymorphism and decoupling. This challenge will test your understanding of how to define and implement interfaces, and how concrete types can satisfy them implicitly. You'll build a system where different data sources can be logged through a unified interface.

Problem Description

Your task is to create a flexible logging system in Go. You'll define an EventLogger interface that specifies a Log(message string) method. Then, you will implement this interface with at least two different concrete types, each representing a distinct way of handling log messages (e.g., writing to the console, writing to a file, sending to a remote server). Finally, you'll demonstrate how to use these different logger implementations interchangeably through the EventLogger interface.

Key Requirements:

  • Define EventLogger Interface: Create an interface named EventLogger with a single method: Log(message string).
  • Implement Concrete Loggers:
    • ConsoleLogger: Implement EventLogger to print log messages to standard output (os.Stdout). Each message should be prefixed with [CONSOLE] .
    • FileLogger: Implement EventLogger to write log messages to a file. You'll need to handle file creation and appending. Each message should be prefixed with [FILE] .
    • (Optional) NetworkLogger: Implement EventLogger to simulate sending log messages over a network (e.g., by printing to standard output with a [NETWORK] prefix, indicating a simulated network transmission).
  • Demonstrate Polymorphism: Create a function that accepts an EventLogger interface. This function should be able to receive any concrete type that satisfies the EventLogger interface and call its Log method.
  • Cleanup: Ensure any opened files are properly closed.

Expected Behavior:

When the Log method is called on an EventLogger instance, the underlying concrete implementation should execute its specific logging logic. The calling code should not need to know the specific type of logger it's interacting with.

Edge Cases:

  • File Operations: Handle potential errors during file opening, writing, and closing.
  • Empty Messages: Ensure the system handles empty log messages gracefully.

Examples

Example 1: Console Logging

// Assume a function `processData` that takes an EventLogger
// and a ConsoleLogger is passed in.

// Calling processData with a ConsoleLogger:
// logger.Log("User logged in")

Output:
[CONSOLE] User logged in

Example 2: File Logging

// Assume a function `processData` that takes an EventLogger
// and a FileLogger (configured to write to "app.log") is passed in.

// Calling processData with a FileLogger:
// logger.Log("Data saved successfully")

// Content of "app.log" after execution:
[FILE] Data saved successfully

Example 3: Mixed Logging and Function Usage

// Assume a function `sendNotification` that takes an EventLogger
// and a message.

// Create a ConsoleLogger and a FileLogger
consoleLogger := NewConsoleLogger()
fileLogger := NewFileLogger("system.log")

// Use the sendNotification function with different loggers
sendNotification(consoleLogger, "System startup complete")
sendNotification(fileLogger, "Configuration loaded")

// Helper function (you'll need to implement this)
func sendNotification(logger EventLogger, msg string) {
    logger.Log(msg)
}

// Expected Output (to console):
[CONSOLE] System startup complete

// Expected Content of "system.log":
[FILE] Configuration loaded

Constraints

  • Log messages are strings and will not exceed 1024 characters.
  • File paths for FileLogger will be valid and not exceed 255 characters.
  • Error handling for I/O operations is expected.
  • The solution should be efficient, though no strict performance benchmarks are enforced for this problem.

Notes

  • Remember that in Go, interfaces are satisfied implicitly. You do not need to explicitly declare that a type implements an interface.
  • Consider using defer for resource cleanup (like closing files).
  • Think about how you will manage the file handle for the FileLogger.
  • The NewConsoleLogger and NewFileLogger functions are examples of constructor patterns you might use. You can name them as you see fit.
Loading editor...
go