Logging System
Problem Statement
Section titled “Problem Statement”Design a flexible and extensible logging system that allows applications to log messages with different severity levels to various destinations (console, file, database, etc.). The system should support log formatting, filtering by log levels, and easy configuration of multiple log destinations.
Requirements
Section titled “Requirements”Functional Requirements
Section titled “Functional Requirements”- Support multiple log levels: DEBUG, INFO, WARNING, ERROR, FATAL
- Allow logging to multiple destinations (console, file, remote server)
- Support custom log message formatting
- Enable filtering logs based on severity level
- Thread-safe logging operations
- Support both synchronous and asynchronous logging
- Allow runtime configuration of log levels and destinations
Non-Functional Requirements
Section titled “Non-Functional Requirements”- High performance with minimal overhead
- Thread-safe for concurrent logging
- Extensible to add new log destinations
- Memory efficient for high-volume logging
- Easy to integrate and use
Simplified Class Diagram
Section titled “Simplified Class Diagram”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
skinparam classBorderThickness 3skinparam ArrowThickness 1skinparam defaultFontSize 16skinparam classAttributeFontSize 18skinparam classFontSize 16
class Logger { + debug() + info() + warning() + error() + fatal()}
class LoggerConfig { + setLogLevel() + addDestination() + setFormatter()}
class LogFormatter { + format()}
class LogDestination { + write() + flush()}
class ConsoleDestination { + write()}
class FileDestination { + write() + rotate()}
class LogMessage { + getLevel() + getMessage() + getTimestamp()}
Logger *-- LoggerConfigLogger ..> LogMessageLoggerConfig --> LogFormatterLoggerConfig --> LogDestinationLogDestination <|-- ConsoleDestinationLogDestination <|-- FileDestination
@endumlSimplified Overview
Section titled “Simplified Overview”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startumlskinparam componentStyle rectangle
package "Core" { [Logger] as Logger [LogMessage] as Msg}
package "Configuration" { [LoggerConfig] as Config [LoggerFactory] as Factory}
package "Formatting" { interface "ILogFormatter" as Formatter [SimpleFormatter] [JsonFormatter]}
package "Destinations" { interface "ILogDestination" as Dest [ConsoleDestination] [FileDestination] [DatabaseDestination]}
package "Processing" { interface "IMessageProcessor" as Processor [SynchronousProcessor] [AsynchronousProcessor]}
[LoggerDriver] --> Factory : usesFactory --> Logger : createsLogger *-- Config : composed ofLogger *-- Processor : composed ofLogger ..> Msg : createsConfig o-- Formatter : aggregatesConfig o-- Dest : aggregatesFormatter <|.. SimpleFormatterFormatter <|.. JsonFormatterDest <|.. ConsoleDestinationDest <|.. FileDestinationDest <|.. DatabaseDestinationProcessor <|.. SynchronousProcessorProcessor <|.. AsynchronousProcessor
@endumlDetailed Class Diagram
Section titled “Detailed Class Diagram”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
enum LogLevel { DEBUG INFO WARNING ERROR FATAL + getValue(): int + isMoreSevereThan(other: LogLevel): boolean}
class LogMessage { - timestamp: DateTime - level: LogLevel - message: String - threadId: String - className: String - context: Map<String, Object> + LogMessage(level: LogLevel, message: String) + getTimestamp(): DateTime + getLevel(): LogLevel + getMessage(): String + addContext(key: String, value: Object): void}
interface ILogFormatter { + format(message: LogMessage): String}
class SimpleFormatter { - dateFormat: String + SimpleFormatter() + format(message: LogMessage): String}
class JsonFormatter { - includeContext: boolean + JsonFormatter(includeContext: boolean) + format(message: LogMessage): String}
class XmlFormatter { + format(message: LogMessage): String}
interface ILogDestination { + write(formattedMessage: String): void + flush(): void + close(): void}
class ConsoleDestination { - outputStream: PrintStream + ConsoleDestination() + write(formattedMessage: String): void + flush(): void + close(): void}
class FileDestination { - filePath: String - writer: BufferedWriter - maxFileSize: long + FileDestination(filePath: String) + write(formattedMessage: String): void + flush(): void + close(): void - rotateFile(): void}
class DatabaseDestination { - connectionProvider: IConnectionProvider + DatabaseDestination(provider: IConnectionProvider) + write(formattedMessage: String): void + flush(): void + close(): void}
interface IConnectionProvider { + getConnection(): Connection + releaseConnection(conn: Connection): void}
interface ILogFilter { + shouldLog(message: LogMessage): boolean}
class LevelFilter { - minLevel: LogLevel + LevelFilter(minLevel: LogLevel) + shouldLog(message: LogMessage): boolean}
class ThreadFilter { - allowedThreads: Set<String> + shouldLog(message: LogMessage): boolean}
class LoggerConfig { - formatter: ILogFormatter - destinations: List<ILogDestination> - filters: List<ILogFilter> + LoggerConfig() + setFormatter(formatter: ILogFormatter): LoggerConfig + addDestination(destination: ILogDestination): LoggerConfig + addFilter(filter: ILogFilter): LoggerConfig + getFormatter(): ILogFormatter + getDestinations(): List<ILogDestination> + getFilters(): List<ILogFilter>}
interface ILogger { + debug(message: String): void + info(message: String): void + warning(message: String): void + error(message: String): void + fatal(message: String): void + log(level: LogLevel, message: String): void}
class Logger { - config: LoggerConfig - messageProcessor: IMessageProcessor + Logger(config: LoggerConfig, processor: IMessageProcessor) + debug(message: String): void + info(message: String): void + warning(message: String): void + error(message: String): void + fatal(message: String): void + log(level: LogLevel, message: String): void - shouldLog(message: LogMessage): boolean}
interface IMessageProcessor { + process(message: LogMessage, config: LoggerConfig): void}
class SynchronousProcessor { + process(message: LogMessage, config: LoggerConfig): void}
class AsynchronousProcessor { - messageQueue: BlockingQueue<LogMessage> - workerThread: Thread - running: AtomicBoolean + AsynchronousProcessor() + process(message: LogMessage, config: LoggerConfig): void + start(): void + stop(): void - processQueue(): void}
class LoggerFactory { - {static} instance: LoggerFactory - loggers: Map<String, ILogger> - defaultConfig: LoggerConfig - LoggerFactory() + {static} getInstance(): LoggerFactory + createLogger(name: String): ILogger + createLogger(name: String, config: LoggerConfig): ILogger + setDefaultConfig(config: LoggerConfig): void}
class LoggerDriver { + {static} main(args: String[]): void - demonstrateBasicLogging(): void - demonstrateAsyncLogging(): void - demonstrateMultipleDestinations(): void}
LogMessage *-- LogLevel
ILogFormatter <|.. SimpleFormatterILogFormatter <|.. JsonFormatterILogFormatter <|.. XmlFormatter
ILogDestination <|.. ConsoleDestinationILogDestination <|.. FileDestinationILogDestination <|.. DatabaseDestination
DatabaseDestination o-- IConnectionProvider
ILogFilter <|.. LevelFilterILogFilter <|.. ThreadFilter
LoggerConfig o-- ILogFormatterLoggerConfig o-- ILogDestinationLoggerConfig o-- ILogFilter
ILogger <|.. LoggerLogger *-- LoggerConfig : composed ofLogger *-- IMessageProcessor : composed ofLogger ..> LogMessage : creates
IMessageProcessor <|.. SynchronousProcessorIMessageProcessor <|.. AsynchronousProcessor
LoggerFactory ..> ILogger : createsLoggerFactory o-- LoggerConfig
LoggerDriver ..> LoggerFactory : usesLoggerDriver ..> LoggerConfig : configuresLoggerDriver ..> ILogFormatter : configuresLoggerDriver ..> ILogDestination : configures
@endumlKey Design Patterns
Section titled “Key Design Patterns”- Singleton Pattern: Logger class ensures only one instance exists
- Strategy Pattern: LogFormatter and LogDestination use strategy pattern for flexible implementations
- Observer Pattern: Multiple destinations can subscribe to log events
- Factory Pattern: Can be used to create different types of loggers
Design Pattern Diagrams
Section titled “Design Pattern Diagrams”1. Strategy Pattern - Formatters & Destinations
Section titled “1. Strategy Pattern - Formatters & Destinations”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startumltitle Strategy Pattern in Logging System
interface ILogFormatter { + format(LogEntry): String}
note right of ILogFormatter Strategy Pattern allows switching formatting behavior at runtime without changing Logger codeend note
class SimpleFormatter { + format(LogEntry): String}
class JSONFormatter { + format(LogEntry): String}
class XMLFormatter { + format(LogEntry): String}
class Logger { - formatter: ILogFormatter + setFormatter(ILogFormatter): void + log(LogLevel, String): void}
ILogFormatter <|.. SimpleFormatterILogFormatter <|.. JSONFormatterILogFormatter <|.. XMLFormatterLogger o-- ILogFormatter : uses
note bottom of Logger **Code Example:**
// Switch formatters at runtime logger.setFormatter(new JSONFormatter()); logger.log(INFO, "User logged in");
// Output: {"level":"INFO","message":"User logged in"}
logger.setFormatter(new SimpleFormatter()); logger.log(INFO, "User logged out");
// Output: [INFO] User logged outend note
@enduml2. Observer Pattern - Multiple Destinations
Section titled “2. Observer Pattern - Multiple Destinations”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startumltitle Observer Pattern - Log Destinations
class Logger { - destinations: List<ILogDestination> + addDestination(ILogDestination): void + removeDestination(ILogDestination): void + log(LogLevel, String): void - notifyDestinations(LogEntry): void}
note top of Logger Observer Pattern: Logger notifies all registered destinations when a log event occursend note
interface ILogDestination { + write(LogEntry): void}
class ConsoleDestination { + write(LogEntry): void}
class FileDestination { - filePath: String + write(LogEntry): void}
class DatabaseDestination { - connection: Connection + write(LogEntry): void}
Logger o-- "*" ILogDestination : notifies >ILogDestination <|.. ConsoleDestinationILogDestination <|.. FileDestinationILogDestination <|.. DatabaseDestination
note bottom of Logger **Code Example:**
Logger logger = Logger.getInstance(); logger.addDestination(new ConsoleDestination()); logger.addDestination(new FileDestination("app.log")); logger.addDestination(new DatabaseDestination());
// Single log call notifies all 3 destinations logger.log(ERROR, "Database connection failed"); // -> Console: [ERROR] Database connection failed // -> File: Appends to app.log // -> Database: Inserts into logs tableend note
@enduml3. Factory Pattern - Logger Creation
Section titled “3. Factory Pattern - Logger Creation”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
title Factory Pattern - Logger Factory
class LoggerFactory { + {static} createLogger(LoggerConfig): Logger + {static} createConsoleLogger(): Logger + {static} createFileLogger(String): Logger + {static} createProductionLogger(): Logger}
class Logger { - formatter: ILogFormatter - destinations: List<ILogDestination> - filters: List<ILogFilter> + log(LogLevel, String): void}
class LoggerConfig { - minLogLevel: LogLevel - destinations: List<ILogDestination> - formatter: ILogFormatter}
LoggerFactory ..> Logger : createsLoggerFactory ..> LoggerConfig : uses
note right of LoggerFactory **Code Example:**
// Simple console logger Logger devLogger = LoggerFactory.createConsoleLogger();
// File logger with rotation Logger fileLogger = LoggerFactory.createFileLogger("app.log");
// Production logger: File + Database + Email alerts Logger prodLogger = LoggerFactory.createProductionLogger();
// Custom configuration LoggerConfig config = new LoggerConfig(); config.setMinLogLevel(LogLevel.WARN); config.addDestination(new SlackDestination()); Logger customLogger = LoggerFactory.createLogger(config);end note
@endumlCode Snippets
Section titled “Code Snippets”Basic Usage
Section titled “Basic Usage”// Configure loggerLoggerConfig config = new LoggerConfig();config.setMinLogLevel(LogLevel.DEBUG);config.addDestination(new ConsoleDestination());config.addDestination(new FileDestination("app.log"));config.setFormatter(new SimpleFormatter());
// Get logger instanceLogger logger = Logger.getInstance();logger.setConfig(config);
// Use loggerlogger.info("Application started");logger.error("Error occurred: " + errorMessage);logger.debug("Debug information: " + debugData);Custom Log Formatter
Section titled “Custom Log Formatter”public class JsonFormatter implements LogFormatter { @Override public String format(LogMessage message) { return String.format( "{\"timestamp\":\"%s\",\"level\":\"%s\",\"message\":\"%s\",\"thread\":\"%s\"}", message.getTimestamp(), message.getLevel(), message.getMessage(), message.getThreadId() ); }}Extension Points
Section titled “Extension Points”- Add new log destinations (Kafka, Redis, Cloud services)
- Implement custom formatters (XML, custom protocols)
- Add log filtering based on custom criteria
- Implement log rotation and archival strategies
- Add log aggregation and analysis capabilities