// Enum representing log levels enum LogLevel { DEBUG, INFO, WARNING, ERROR, FATAL }
// Class representing a log message class LogMessage { private LocalDateTime timestamp; private LogLevel level; private String message; public LogMessage(LogLevel level, String message) { this.timestamp = LocalDateTime.now(); this.level = level; this.message = message; } @Override public String toString() { return "[" + timestamp + "] [" + level + "] " + message; } }
// Interface for log appenders interface LogAppender { void append(LogMessage message); }
// Console Appender class ConsoleAppender implements LogAppender { @Override public void append(LogMessage message) { System.out.println(message.toString()); } }
// File Appender class FileAppender implements LogAppender { private String filePath; public FileAppender(String filePath) { this.filePath = filePath; } @Override public void append(LogMessage message) { try (FileWriter writer = new FileWriter(filePath, true)) { writer.write(message.toString() + "\n"); } catch (IOException e) { System.err.println("Failed to write to log file: " + e.getMessage()); } } }
// Database Appender class DatabaseAppender implements LogAppender { private String connectionString; private String insertQuery = "INSERT INTO logs (timestamp, level, message) VALUES (?, ?, ?)"; public DatabaseAppender(String connectionString) { this.connectionString = connectionString; } @Override public void append(LogMessage message) { try (Connection conn = DriverManager.getConnection(connectionString); PreparedStatement stmt = conn.prepareStatement(insertQuery)) { stmt.setString(1, message.toString()); stmt.executeUpdate(); } catch (Exception e) { System.err.println("Failed to write to database: " + e.getMessage()); } } }
// Logger Configuration class LoggerConfig { private LogLevel logLevel; private LogAppender appender; public LoggerConfig(LogLevel logLevel, LogAppender appender) { this.logLevel = logLevel; this.appender = appender; } public LogLevel getLogLevel() { return logLevel; } public LogAppender getAppender() { return appender; } }
// Logger Singleton class Logger { private static Logger instance; private LoggerConfig config; private final Lock lock = new ReentrantLock(); private Logger() {} public static Logger getInstance() { if (instance == null) { synchronized (Logger.class) { if (instance == null) { instance = new Logger(); } } } return instance; } public void setConfig(LoggerConfig config) { lock.lock(); try { this.config = config; } finally { lock.unlock(); } } private void log(LogLevel level, String message) { lock.lock(); try { if (config != null && level.ordinal() >= config.getLogLevel().ordinal()) { LogMessage logMessage = new LogMessage(level, message); config.getAppender().append(logMessage); } } finally { lock.unlock(); } } public void debug(String message) { log(LogLevel.DEBUG, message); } public void info(String message) { log(LogLevel.INFO, message); } public void warning(String message) { log(LogLevel.WARNING, message); } public void error(String message) { log(LogLevel.ERROR, message); } public void fatal(String message) { log(LogLevel.FATAL, message); } }
// Example Usage public class LoggingExample { public static void main(String[] args) { Logger logger = Logger.getInstance(); // Set Console Appender logger.setConfig(new LoggerConfig(LogLevel.INFO, new ConsoleAppender())); // Log messages logger.debug("This is a DEBUG message"); logger.info("This is an INFO message"); logger.error("This is an ERROR message"); // Change to File Appender logger.setConfig(new LoggerConfig(LogLevel.DEBUG, new FileAppender("logs.txt"))); logger.warning("This is a WARNING message"); logger.fatal("This is a FATAL message"); } }
Input:
Output:
[2024-12-30T14:20:00] [INFO] This is an INFO message
[2024-12-30T14:20:01] [ERROR] This is an ERROR message
[2024-12-30T14:20:02] [WARNING] This is a WARNING message
[2024-12-30T14:20:03] [FATAL] This is a FATAL message