Go Log Aggregator
Log aggregation is a crucial component of many distributed systems, allowing centralized collection, storage, and analysis of logs from various sources. This challenge asks you to build a simplified log aggregator in Go that receives log messages over a network and stores them in a persistent store (in this case, a file). This is a foundational skill for building robust and observable applications.
Problem Description
You are tasked with creating a Go program that acts as a log aggregator. The aggregator will listen for incoming log messages on a specified TCP port. Each log message will be a simple string. Upon receiving a message, the aggregator should append it to a specified log file. The program should handle multiple concurrent connections and ensure that log messages are appended atomically to prevent data loss.
Key Requirements:
- TCP Listener: The program must listen for incoming TCP connections on a configurable port (default: 8080).
- Concurrent Handling: The program must handle multiple client connections concurrently. Each connection should be handled in a separate goroutine.
- Log Storage: Log messages should be appended to a specified log file (default: "aggregated_logs.txt").
- Atomic Appending: Appending to the log file must be done atomically to prevent data corruption in case of concurrent writes. Use file locking to achieve this.
- Error Handling: The program should gracefully handle errors, such as connection errors, file I/O errors, and invalid input.
- Configuration: The port and log file path should be configurable via command-line arguments.
Expected Behavior:
- The program starts listening on the specified port.
- Clients can connect to the aggregator and send log messages.
- The aggregator receives the messages and appends them to the log file.
- Multiple clients can send messages concurrently without data loss or corruption.
- The program continues to listen for new connections until it is explicitly terminated.
- If an error occurs (e.g., file not found, connection refused), the program should log the error and continue operating (if possible).
Edge Cases to Consider:
- Invalid Port Number: Handle cases where the provided port number is not a valid integer.
- File Permissions: Handle cases where the program does not have write permissions to the specified log file.
- Large Log Files: Consider how the program will handle very large log files. (While not required to implement full large file handling, be aware of potential issues).
- Connection Errors: Handle client disconnections gracefully.
- Empty Log Messages: The aggregator should handle empty log messages without crashing.
Examples
Example 1:
Input:
Client 1 sends: "Error: Database connection failed"
Client 2 sends: "Info: User logged in"
Client 3 sends: "Warning: Disk space low"
Output: The file "aggregated_logs.txt" contains:
Error: Database connection failed
Info: User logged in
Warning: Disk space low
Explanation: The aggregator receives the messages from three clients and appends them to the log file in the order they were received (or potentially interleaved, but all messages must be present).
Example 2:
Input:
Command-line arguments: -port 9000 -logFile custom_logs.txt
Client 1 sends: "Debug: Starting application"
Output: The file "custom_logs.txt" contains:
Debug: Starting application
Explanation: The aggregator listens on port 9000 and appends the log message to the file "custom_logs.txt".
Example 3: (Edge Case)
Input:
Command-line arguments: -port abc -logFile logs.txt
Output: The program prints an error message to the console: "Invalid port number: abc" and defaults to port 8080 and log file "aggregated_logs.txt". The program continues to run. Explanation: The program handles the invalid port number gracefully and continues to operate with default values.
Constraints
- Port Range: The port number must be between 1024 and 65535 (inclusive).
- Input Format: Log messages are simple strings. No complex parsing is required.
- Concurrency: The aggregator must handle at least 10 concurrent connections without significant performance degradation.
- File Size: The log file can grow to a significant size (e.g., 1GB). While full large file optimization isn't required, be mindful of potential performance implications.
- Command-line arguments: The program should accept
-portand-logFilearguments. If not provided, default values should be used.
Notes
- Use the
netpackage for TCP networking. - Use the
ospackage for file I/O. - Use
sync.Mutexto implement atomic file appending. - Consider using
logpackage for logging errors. - Error handling is crucial. Don't panic; handle errors gracefully.
- Focus on correctness and concurrency safety. Performance optimization is secondary.
- The order of log messages from different clients is not strictly guaranteed, but all messages should be persisted.