A comprehensive real-time log monitoring and alerting system that detects patterns, anomalies, and specific conditions in application logs with multiple notification channels.
Complete Implementation
1. Core Log Alerting System
package com.logalerting; import java.io.*; import java.nio.file.*; import java.util.*; import java.util.concurrent.*; import java.util.regex.*; import java.time.*; import java.time.format.*; /** * Advanced Log Alerting System * Monitors log files in real-time, detects patterns, and triggers alerts */ public class LogAlertingSystem { private final ScheduledExecutorService scheduler; private final List<LogMonitor> logMonitors; private final AlertManager alertManager; private final Map<String, AlertRule> alertRules; private final Set<String> monitoredFiles; private final long checkIntervalMs; private final int maxBufferSize; private volatile boolean isRunning; private final Pattern logPattern; private final DateTimeFormatter logTimestampFormat; public LogAlertingSystem() { this(1000, 10000, "logs/", "yyyy-MM-dd HH:mm:ss,SSS"); } public LogAlertingSystem(long checkIntervalMs, int maxBufferSize, String logDirectory, String timestampFormat) { this.checkIntervalMs = checkIntervalMs; this.maxBufferSize = maxBufferSize; this.scheduler = Executors.newScheduledThreadPool(3); this.logMonitors = new CopyOnWriteArrayList<>(); this.alertManager = new AlertManager(); this.alertRules = new ConcurrentHashMap<>(); this.monitoredFiles = ConcurrentHashMap.newKeySet(); this.logPattern = Pattern.compile( "^(?<timestamp>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3}) " + "\\[(?<thread>.*?)\\] " + "(?<level>\\w+) " + "(?<logger>.*?) - " + "(?<message>.*)$" ); this.logTimestampFormat = DateTimeFormatter.ofPattern(timestampFormat); initializeDefaultRules(); discoverLogFiles(logDirectory); } /** * Start log monitoring */ public void startMonitoring() { if (isRunning) { System.out.println("Log alerting system is already running"); return; } System.out.println("Starting log alerting system..."); isRunning = true; // Start monitoring each log file for (String logFile : monitoredFiles) { LogMonitor monitor = new LogMonitor(logFile); logMonitors.add(monitor); scheduler.scheduleAtFixedRate(monitor, 0, checkIntervalMs, TimeUnit.MILLISECONDS); } // Start alert manager scheduler.scheduleAtFixedRate(alertManager, 0, checkIntervalMs, TimeUnit.MILLISECONDS); System.out.println("Log alerting system started. Monitoring " + monitoredFiles.size() + " log files"); } /** * Stop log monitoring */ public void stopMonitoring() { if (!isRunning) { return; } System.out.println("Stopping log alerting system..."); isRunning = false; scheduler.shutdown(); try { if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); Thread.currentThread().interrupt(); } generateMonitoringReport(); System.out.println("Log alerting system stopped"); } /** * Discover log files in directory */ private void discoverLogFiles(String logDirectory) { try { Path logDir = Paths.get(logDirectory); if (!Files.exists(logDir)) { System.warn("Log directory does not exist: " + logDirectory); return; } Files.walk(logDir) .filter(path -> Files.isRegularFile(path)) .filter(path -> path.toString().endsWith(".log")) .forEach(path -> monitoredFiles.add(path.toString())); System.out.println("Discovered " + monitoredFiles.size() + " log files"); } catch (IOException e) { System.err.println("Error discovering log files: " + e.getMessage()); } } /** * Initialize default alert rules */ private void initializeDefaultRules() { // Error level alerts addAlertRule(new AlertRule("ERROR_ALERT", "ERROR", "Error detected in application logs", AlertSeverity.HIGH, 1, 60)); // Exception patterns addAlertRule(new AlertRule("EXCEPTION_ALERT", ".*Exception:.*|.*Error:.*", "Exception detected in logs", AlertSeverity.HIGH, 3, 120)); // High frequency error alerts addAlertRule(new AlertRule("FREQUENT_ERRORS", "ERROR", "High frequency of errors detected", AlertSeverity.MEDIUM, 10, 300)); // Performance pattern alerts addAlertRule(new AlertRule("SLOW_OPERATION", ".*execution time: \\d{4,}ms.*", "Slow operation detected", AlertSeverity.MEDIUM, 5, 600)); // Memory warning alerts addAlertRule(new AlertRule("MEMORY_WARNING", ".*Memory.*low.*|.*GC.*overhead.*", "Memory-related warning detected", AlertSeverity.MEDIUM, 2, 300)); // Authentication failure alerts addAlertRule(new AlertRule("AUTH_FAILURE", ".*Authentication failed.*|.*Login failure.*", "Authentication failure detected", AlertSeverity.HIGH, 5, 300)); } } 2. Log Monitoring and Parsing
/** * Individual log file monitor */ private class LogMonitor implements Runnable { private final String logFilePath; private long lastReadPosition; private final RandomAccessFile logFile; private final Map<String, Pattern> compiledPatterns; private final CircularBuffer<LogEntry> recentEntries; public LogMonitor(String logFilePath) throws IOException { this.logFilePath = logFilePath; this.lastReadPosition = getFileSize(logFilePath); this.logFile = new RandomAccessFile(logFilePath, "r"); this.compiledPatterns = new ConcurrentHashMap<>(); this.recentEntries = new CircularBuffer<>(maxBufferSize); // Compile patterns for all rules for (AlertRule rule : alertRules.values()) { compiledPatterns.put(rule.getId(), Pattern.compile(rule.getPattern(), Pattern.CASE_INSENSITIVE)); } } @Override public void run() { try { monitorLogFile(); } catch (Exception e) { System.err.println("Error monitoring log file " + logFilePath + ": " + e.getMessage()); } } private void monitorLogFile() throws IOException { long currentSize = getFileSize(logFilePath); if (currentSize < lastReadPosition) { // Log file was rotated or truncated lastReadPosition = 0; } if (currentSize > lastReadPosition) { logFile.seek(lastReadPosition); String line; while ((line = logFile.readLine()) != null) { processLogLine(line); } lastReadPosition = logFile.getFilePointer(); } } private void processLogLine(String line) { if (line == null || line.trim().isEmpty()) { return; } LogEntry logEntry = parseLogEntry(line); if (logEntry != null) { recentEntries.add(logEntry); checkAlertRules(logEntry); } } private LogEntry parseLogEntry(String line) { try { Matcher matcher = logPattern.matcher(line); if (matcher.matches()) { String timestampStr = matcher.group("timestamp"); String thread = matcher.group("thread"); String level = matcher.group("level"); String logger = matcher.group("logger"); String message = matcher.group("message"); LocalDateTime timestamp = LocalDateTime.parse(timestampStr, logTimestampFormat); return new LogEntry(timestamp, thread, level, logger, message, line); } else { // Fallback for non-standard log formats return new LogEntry(LocalDateTime.now(), "unknown", "INFO", "unknown", line, line); } } catch (Exception e) { System.debug("Failed to parse log line: " + line); return null; } } private void checkAlertRules(LogEntry logEntry) { for (AlertRule rule : alertRules.values()) { Pattern pattern = compiledPatterns.get(rule.getId()); if (pattern != null && pattern.matcher(logEntry.getMessage()).find()) { alertManager.recordMatch(rule, logEntry); } } // Check for custom pattern matches checkCustomPatterns(logEntry); } private void checkCustomPatterns(LogEntry logEntry) { // Check for specific business logic patterns if (logEntry.getMessage().contains("OutOfMemoryError")) { alertManager.triggerImmediateAlert( new Alert("OUT_OF_MEMORY", "OutOfMemoryError detected", AlertSeverity.CRITICAL, logEntry)); } if (logEntry.getLevel().equals("ERROR") && logEntry.getMessage().contains("Database connection")) { alertManager.recordMatch( new AlertRule("DB_CONNECTION_ERROR", "Database connection.*ERROR", "Database connection issues", AlertSeverity.HIGH, 3, 60), logEntry); } } private long getFileSize(String filePath) throws IOException { return Files.size(Paths.get(filePath)); } public void close() { try { logFile.close(); } catch (IOException e) { System.err.println("Error closing log file: " + e.getMessage()); } } } /** * Log entry representation */ public static class LogEntry { private final LocalDateTime timestamp; private final String thread; private final String level; private final String logger; private final String message; private final String rawLine; public LogEntry(LocalDateTime timestamp, String thread, String level, String logger, String message, String rawLine) { this.timestamp = timestamp; this.thread = thread; this.level = level; this.logger = logger; this.message = message; this.rawLine = rawLine; } // Getters public LocalDateTime getTimestamp() { return timestamp; } public String getThread() { return thread; } public String getLevel() { return level; } public String getLogger() { return logger; } public String getMessage() { return message; } public String getRawLine() { return rawLine; } @Override public String toString() { return String.format("[%s] [%s] %s - %s", timestamp.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), level, logger, message); } } 3. Alert Rules and Management
/** * Alert rule definition */ public static class AlertRule { private final String id; private final String pattern; private final String description; private final AlertSeverity severity; private final int threshold; private final long timeWindowSeconds; private final Set<String> tags; public AlertRule(String id, String pattern, String description, AlertSeverity severity, int threshold, long timeWindowSeconds) { this.id = id; this.pattern = pattern; this.description = description; this.severity = severity; this.threshold = threshold; this.timeWindowSeconds = timeWindowSeconds; this.tags = new HashSet<>(); } // Getters public String getId() { return id; } public String getPattern() { return pattern; } public String getDescription() { return description; } public AlertSeverity getSeverity() { return severity; } public int getThreshold() { return threshold; } public long getTimeWindowSeconds() { return timeWindowSeconds; } public Set<String> getTags() { return tags; } public void addTag(String tag) { tags.add(tag); } public boolean isThresholdReached(int matchCount, long timeWindowMs) { return matchCount >= threshold && timeWindowMs <= (timeWindowSeconds * 1000); } } /** * Alert severity levels */ public enum AlertSeverity { LOW, MEDIUM, HIGH, CRITICAL } /** * Alert manager */ private class AlertManager implements Runnable { private final Map<String, List<LogEntry>> ruleMatches; private final Map<String, Alert> activeAlerts; private final List<AlertNotifier> notifiers; private final Map<String, LocalDateTime> lastAlertTime; public AlertManager() { this.ruleMatches = new ConcurrentHashMap<>(); this.activeAlerts = new ConcurrentHashMap<>(); this.notifiers = new CopyOnWriteArrayList<>(); this.lastAlertTime = new ConcurrentHashMap<>(); // Add default notifiers notifiers.add(new ConsoleNotifier()); notifiers.add(new LogFileNotifier()); } @Override public void run() { checkAlertThresholds(); cleanupOldMatches(); checkActiveAlerts(); } public void recordMatch(AlertRule rule, LogEntry logEntry) { String ruleId = rule.getId(); ruleMatches.computeIfAbsent(ruleId, k -> new CopyOnWriteArrayList<>()) .add(logEntry); // Check if threshold is reached checkRuleThreshold(rule); } private void checkRuleThreshold(AlertRule rule) { String ruleId = rule.getId(); List<LogEntry> matches = ruleMatches.get(ruleId); if (matches != null && matches.size() >= rule.getThreshold()) { // Check time window LocalDateTime now = LocalDateTime.now(); LocalDateTime windowStart = now.minusSeconds(rule.getTimeWindowSeconds()); long matchesInWindow = matches.stream() .filter(entry -> entry.getTimestamp().isAfter(windowStart)) .count(); if (matchesInWindow >= rule.getThreshold()) { triggerAlert(rule, matches); } } } private void triggerAlert(AlertRule rule, List<LogEntry> matches) { String alertId = rule.getId() + "_" + System.currentTimeMillis(); Alert alert = new Alert(alertId, rule.getDescription(), rule.getSeverity(), matches); // Check if we should suppress this alert (recent duplicate) if (shouldSuppressAlert(rule.getId())) { System.debug("Suppressing duplicate alert: " + rule.getId()); return; } activeAlerts.put(alertId, alert); lastAlertTime.put(rule.getId(), LocalDateTime.now()); // Notify all registered notifiers for (AlertNotifier notifier : notifiers) { try { notifier.notify(alert); } catch (Exception e) { System.err.println("Error sending alert notification: " + e.getMessage()); } } System.warn("ALERT TRIGGERED: " + alert.getDescription() + " [Severity: " + alert.getSeverity() + "]"); } public void triggerImmediateAlert(Alert alert) { activeAlerts.put(alert.getId(), alert); for (AlertNotifier notifier : notifiers) { try { notifier.notify(alert); } catch (Exception e) { System.err.println("Error sending immediate alert: " + e.getMessage()); } } } private boolean shouldSuppressAlert(String ruleId) { LocalDateTime lastAlert = lastAlertTime.get(ruleId); if (lastAlert == null) { return false; } // Suppress duplicate alerts for 5 minutes return Duration.between(lastAlert, LocalDateTime.now()).toMinutes() < 5; } private void checkAlertThresholds() { for (AlertRule rule : alertRules.values()) { checkRuleThreshold(rule); } } private void cleanupOldMatches() { LocalDateTime cleanupThreshold = LocalDateTime.now().minusHours(1); for (List<LogEntry> matches : ruleMatches.values()) { matches.removeIf(entry -> entry.getTimestamp().isBefore(cleanupThreshold)); } } private void checkActiveAlerts() { // Check if active alerts should be auto-resolved Iterator<Map.Entry<String, Alert>> iterator = activeAlerts.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Alert> entry = iterator.next(); Alert alert = entry.getValue(); if (alert.shouldAutoResolve()) { triggerAlertResolution(alert); iterator.remove(); } } } private void triggerAlertResolution(Alert alert) { Alert resolvedAlert = alert.resolve(); for (AlertNotifier notifier : notifiers) { try { notifier.notifyResolution(resolvedAlert); } catch (Exception e) { System.err.println("Error sending alert resolution: " + e.getMessage()); } } System.info("ALERT RESOLVED: " + resolvedAlert.getDescription()); } public void addNotifier(AlertNotifier notifier) { notifiers.add(notifier); } public List<Alert> getActiveAlerts() { return new ArrayList<>(activeAlerts.values()); } } /** * Alert representation */ public static class Alert { private final String id; private final String description; private final AlertSeverity severity; private final LocalDateTime triggerTime; private LocalDateTime resolveTime; private final List<LogEntry> triggeringEntries; private final Map<String, String> metadata; public Alert(String id, String description, AlertSeverity severity, LogEntry triggeringEntry) { this(id, description, severity, Collections.singletonList(triggeringEntry)); } public Alert(String id, String description, AlertSeverity severity, List<LogEntry> triggeringEntries) { this.id = id; this.description = description; this.severity = severity; this.triggerTime = LocalDateTime.now(); this.triggeringEntries = new ArrayList<>(triggeringEntries); this.metadata = new HashMap<>(); } // Getters public String getId() { return id; } public String getDescription() { return description; } public AlertSeverity getSeverity() { return severity; } public LocalDateTime getTriggerTime() { return triggerTime; } public LocalDateTime getResolveTime() { return resolveTime; } public List<LogEntry> getTriggeringEntries() { return triggeringEntries; } public Map<String, String> getMetadata() { return metadata; } public void addMetadata(String key, String value) { metadata.put(key, value); } public boolean shouldAutoResolve() { // Auto-resolve after 1 hour for non-critical alerts if (severity == AlertSeverity.CRITICAL) { return false; } Duration duration = Duration.between(triggerTime, LocalDateTime.now()); return duration.toHours() >= 1; } public Alert resolve() { this.resolveTime = LocalDateTime.now(); return this; } public boolean isResolved() { return resolveTime != null; } public String getSummary() { return String.format("[%s] %s - Triggered: %s, Entries: %d", severity, description, triggerTime.format(DateTimeFormatter.ISO_LOCAL_TIME), triggeringEntries.size()); } } 4. Notification System
/** * Alert notifier interface */ public interface AlertNotifier { void notify(Alert alert) throws Exception; void notifyResolution(Alert alert) throws Exception; String getName(); } /** * Console notifier */ public static class ConsoleNotifier implements AlertNotifier { @Override public void notify(Alert alert) { System.err.println("🚨 ALERT: " + alert.getSummary()); for (LogEntry entry : alert.getTriggeringEntries()) { System.err.println(" - " + entry.getMessage()); } } @Override public void notifyResolution(Alert alert) { System.out.println("✅ ALERT RESOLVED: " + alert.getDescription()); } @Override public String getName() { return "ConsoleNotifier"; } } /** * Log file notifier */ public static class LogFileNotifier implements AlertNotifier { private final PrintWriter logWriter; public LogFileNotifier() { try { String logFile = "alerts.log"; logWriter = new PrintWriter(new FileWriter(logFile, true)); } catch (IOException e) { throw new RuntimeException("Failed to create alert log file", e); } } @Override public void notify(Alert alert) { logWriter.printf("[%s] ALERT: %s - %s%n", LocalDateTime.now(), alert.getSeverity(), alert.getDescription()); logWriter.flush(); } @Override public void notifyResolution(Alert alert) { logWriter.printf("[%s] ALERT RESOLVED: %s%n", LocalDateTime.now(), alert.getDescription()); logWriter.flush(); } @Override public String getName() { return "LogFileNotifier"; } } /** * Email notifier */ public static class EmailNotifier implements AlertNotifier { private final String smtpHost; private final String fromEmail; private final List<String> toEmails; public EmailNotifier(String smtpHost, String fromEmail, List<String> toEmails) { this.smtpHost = smtpHost; this.fromEmail = fromEmail; this.toEmails = new ArrayList<>(toEmails); } @Override public void notify(Alert alert) throws Exception { // Simplified email sending (in real implementation, use JavaMail) String subject = String.format("ALERT: %s [%s]", alert.getDescription(), alert.getSeverity()); StringBuilder body = new StringBuilder(); body.append("Alert Details:\n") .append("Description: ").append(alert.getDescription()).append("\n") .append("Severity: ").append(alert.getSeverity()).append("\n") .append("Time: ").append(alert.getTriggerTime()).append("\n") .append("Triggering Entries:\n"); for (LogEntry entry : alert.getTriggeringEntries()) { body.append(" - ").append(entry.getMessage()).append("\n"); } // In real implementation, send email using JavaMail API System.info("Would send email alert: " + subject); } @Override public void notifyResolution(Alert alert) throws Exception { String subject = String.format("ALERT RESOLVED: %s", alert.getDescription()); // Send resolution email System.info("Would send resolution email: " + subject); } @Override public String getName() { return "EmailNotifier"; } } /** * Slack notifier */ public static class SlackNotifier implements AlertNotifier { private final String webhookUrl; private final String channel; public SlackNotifier(String webhookUrl, String channel) { this.webhookUrl = webhookUrl; this.channel = channel; } @Override public void notify(Alert alert) throws Exception { String color = getColorForSeverity(alert.getSeverity()); String message = String.format(""" { "channel": "%s", "attachments": [ { "color": "%s", "title": "Alert: %s", "text": "%s", "fields": [ { "title": "Severity", "value": "%s", "short": true }, { "title": "Time", "value": "%s", "short": true } ] } ] } """, channel, color, alert.getDescription(), alert.getTriggeringEntries().get(0).getMessage(), alert.getSeverity(), alert.getTriggerTime()); // In real implementation, send HTTP POST to webhook System.info("Would send Slack alert: " + alert.getDescription()); } @Override public void notifyResolution(Alert alert) throws Exception { String message = String.format(""" { "channel": "%s", "text": "✅ Alert Resolved: %s" } """, channel, alert.getDescription()); // Send resolution message System.info("Would send Slack resolution: " + alert.getDescription()); } private String getColorForSeverity(AlertSeverity severity) { switch (severity) { case LOW: return "#36a64f"; case MEDIUM: return "#ffcc00"; case HIGH: return "#ff9900"; case CRITICAL: return "#ff0000"; default: return "#757575"; } } @Override public String getName() { return "SlackNotifier"; } } 5. Pattern Detection and Analysis
/** * Add custom alert rule */ public void addAlertRule(AlertRule rule) { alertRules.put(rule.getId(), rule); System.info("Added alert rule: " + rule.getId()); } /** * Remove alert rule */ public void removeAlertRule(String ruleId) { alertRules.remove(ruleId); System.info("Removed alert rule: " + ruleId); } /** * Add log file to monitor */ public void addLogFile(String filePath) { if (monitoredFiles.add(filePath) && isRunning) { try { LogMonitor monitor = new LogMonitor(filePath); logMonitors.add(monitor); scheduler.scheduleAtFixedRate(monitor, 0, checkIntervalMs, TimeUnit.MILLISECONDS); System.info("Started monitoring log file: " + filePath); } catch (IOException e) { System.err.println("Failed to monitor log file: " + filePath); } } } /** * Remove log file from monitoring */ public void removeLogFile(String filePath) { if (monitoredFiles.remove(filePath)) { logMonitors.removeIf(monitor -> { if (monitor.logFilePath.equals(filePath)) { monitor.close(); return true; } return false; }); System.info("Stopped monitoring log file: " + filePath); } } /** * Search for patterns in recent logs */ public List<LogEntry> searchLogs(String pattern, Duration timeWindow) { List<LogEntry> results = new ArrayList<>(); Pattern compiledPattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE); LocalDateTime cutoff = LocalDateTime.now().minus(timeWindow); for (LogMonitor monitor : logMonitors) { // This would need access to recentEntries which should be exposed // For now, return empty list - implementation would depend on storage strategy } return results; } /** * Get log statistics */ public LogStatistics getStatistics() { LogStatistics stats = new LogStatistics(); for (LogMonitor monitor : logMonitors) { // Collect statistics from each monitor // Implementation would depend on how we expose monitor data } return stats; } /** * Log statistics container */ public static class LogStatistics { private int totalLogEntries; private int errorCount; private int warningCount; private Map<String, Integer> levelDistribution; private Map<String, Integer> loggerDistribution; public LogStatistics() { this.levelDistribution = new HashMap<>(); this.loggerDistribution = new HashMap<>(); } // Getters and setters public int getTotalLogEntries() { return totalLogEntries; } public void setTotalLogEntries(int totalLogEntries) { this.totalLogEntries = totalLogEntries; } public int getErrorCount() { return errorCount; } public void setErrorCount(int errorCount) { this.errorCount = errorCount; } public int getWarningCount() { return warningCount; } public void setWarningCount(int warningCount) { this.warningCount = warningCount; } public Map<String, Integer> getLevelDistribution() { return levelDistribution; } public Map<String, Integer> getLoggerDistribution() { return loggerDistribution; } public void incrementLevel(String level) { levelDistribution.merge(level, 1, Integer::sum); } public void incrementLogger(String logger) { loggerDistribution.merge(logger, 1, Integer::sum); } } 6. Configuration and Report Generation
/** * Configuration class */ public static class AlertingConfig { private long checkIntervalMs = 1000; private int maxBufferSize = 10000; private String logDirectory = "logs/"; private String timestampFormat = "yyyy-MM-dd HH:mm:ss,SSS"; private List<AlertNotifier> notifiers = new ArrayList<>(); private Map<String, String> notificationSettings = new HashMap<>(); public static AlertingConfig builder() { return new AlertingConfig(); } public AlertingConfig checkIntervalMs(long interval) { this.checkIntervalMs = interval; return this; } public AlertingConfig maxBufferSize(int size) { this.maxBufferSize = size; return this; } public AlertingConfig logDirectory(String directory) { this.logDirectory = directory; return this; } public AlertingConfig timestampFormat(String format) { this.timestampFormat = format; return this; } public AlertingConfig addNotifier(AlertNotifier notifier) { this.notifiers.add(notifier); return this; } public AlertingConfig notificationSetting(String key, String value) { this.notificationSettings.put(key, value); return this; } public LogAlertingSystem build() { LogAlertingSystem system = new LogAlertingSystem( checkIntervalMs, maxBufferSize, logDirectory, timestampFormat); for (AlertNotifier notifier : notifiers) { system.alertManager.addNotifier(notifier); } return system; } } /** * Generate monitoring report */ private void generateMonitoringReport() { try { String report = generateReport(); String fileName = "log-alerting-report-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss")) + ".html"; Files.writeString(Paths.get(fileName), report); System.out.println("Monitoring report generated: " + fileName); } catch (IOException e) { System.err.println("Failed to generate monitoring report: " + e.getMessage()); } } private String generateReport() { StringBuilder html = new StringBuilder(); html.append(""" <!DOCTYPE html> <html> <head> <title>Log Alerting System Report</title> <style> body { font-family: Arial, sans-serif; margin: 20px; } .header { background: #f0f0f0; padding: 20px; border-radius: 5px; } .section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; } .alert { background: #fff3cd; border-left: 4px solid #ffc107; padding: 10px; margin: 5px 0; } .critical { background: #f8d7da; border-left: 4px solid #dc3545; } .high { background: #ffeaa7; border-left: 4px solid #fdcb6e; } .metric { display: inline-block; margin: 0 20px 10px 0; padding: 10px; background: #e9ecef; border-radius: 3px; } table { width: 100%; border-collapse: collapse; margin: 10px 0; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } th { background-color: #f2f2f2; } </style> </head> <body> """); html.append("<div class='header'>") .append("<h1>Log Alerting System Report</h1>") .append("<p><strong>Generated:</strong> ").append(LocalDateTime.now()).append("</p>") .append("</div>"); // System summary html.append("<div class='section'>") .append("<h2>System Summary</h2>") .append("<div class='metric'><strong>Monitored Files:</strong> ").append(monitoredFiles.size()).append("</div>") .append("<div class='metric'><strong>Alert Rules:</strong> ").append(alertRules.size()).append("</div>") .append("<div class='metric'><strong>Active Alerts:</strong> ").append(alertManager.getActiveAlerts().size()).append("</div>") .append("</div>"); // Active alerts List<Alert> activeAlerts = alertManager.getActiveAlerts(); if (!activeAlerts.isEmpty()) { html.append("<div class='section'>") .append("<h2>Active Alerts</h2>"); for (Alert alert : activeAlerts) { String alertClass = "alert " + alert.getSeverity().toString().toLowerCase(); html.append("<div class='").append(alertClass).append("'>") .append("<h3>").append(alert.getDescription()).append("</h3>") .append("<p><strong>Severity:</strong> ").append(alert.getSeverity()).append("</p>") .append("<p><strong>Triggered:</strong> ").append(alert.getTriggerTime()).append("</p>") .append("<p><strong>Entries:</strong> ").append(alert.getTriggeringEntries().size()).append("</p>") .append("</div>"); } html.append("</div>"); } // Alert rules html.append("<div class='section'>") .append("<h2>Alert Rules</h2>") .append("<table>") .append("<tr><th>ID</th><th>Description</th><th>Pattern</th><th>Severity</th><th>Threshold</th><th>Time Window</th></tr>"); for (AlertRule rule : alertRules.values()) { html.append("<tr>") .append("<td>").append(rule.getId()).append("</td>") .append("<td>").append(rule.getDescription()).append("</td>") .append("<td>").append(rule.getPattern()).append("</td>") .append("<td>").append(rule.getSeverity()).append("</td>") .append("<td>").append(rule.getThreshold()).append("</td>") .append("<td>").append(rule.getTimeWindowSeconds()).append("s</td>") .append("</tr>"); } html.append("</table></div>"); html.append("</body></html>"); return html.toString(); } 7. Utility Classes
/** * Circular buffer for storing recent log entries */ private static class CircularBuffer<T> { private final T[] buffer; private int head; private int tail; private int size; private final int capacity; @SuppressWarnings("unchecked") public CircularBuffer(int capacity) { this.capacity = capacity; this.buffer = (T[]) new Object[capacity]; this.head = 0; this.tail = 0; this.size = 0; } public synchronized void add(T item) { buffer[head] = item; head = (head + 1) % capacity; if (size < capacity) { size++; } else { tail = (tail + 1) % capacity; } } public synchronized List<T> getRecentItems(int count) { count = Math.min(count, size); List<T> items = new ArrayList<>(count); for (int i = 0; i < count; i++) { int index = (head - 1 - i + capacity) % capacity; items.add(buffer[index]); } Collections.reverse(items); return items; } public synchronized int size() { return size; } } /** * Add notifier to alert manager */ public void addNotifier(AlertNotifier notifier) { alertManager.addNotifier(notifier); } /** * Get active alerts */ public List<Alert> getActiveAlerts() { return alertManager.getActiveAlerts(); } /** * Manual alert trigger for testing */ public void triggerTestAlert() { LogEntry testEntry = new LogEntry( LocalDateTime.now(), "test-thread", "ERROR", "TestLogger", "This is a test error message for alert verification", "Raw test log line" ); alertManager.triggerImmediateAlert( new Alert("TEST_ALERT", "Test alert triggered manually", AlertSeverity.MEDIUM, testEntry)); } } // Utility class for system output class System { public static void info(String message) { java.lang.System.out.println("[INFO] " + message); } public static void warn(String message) { java.lang.System.out.println("[WARN] " + message); } public static void error(String message) { java.lang.System.err.println("[ERROR] " + message); } public static void debug(String message) { java.lang.System.out.println("[DEBUG] " + message); } } 8. Usage Examples
/** * Demo and usage examples */ public class LogAlertingDemo { public static void main(String[] args) throws Exception { // Create alerting system with configuration LogAlertingSystem system = LogAlertingSystem.AlertingConfig.builder() .checkIntervalMs(2000) .maxBufferSize(5000) .logDirectory("application-logs/") .addNotifier(new LogAlertingSystem.ConsoleNotifier()) .addNotifier(new LogAlertingSystem.SlackNotifier( "https://hooks.slack.com/services/...", "#alerts")) .build(); // Add custom alert rules system.addAlertRule(new LogAlertingSystem.AlertRule( "CUSTOM_ERROR", ".*Payment.*failed.*|.*Transaction.*error.*", "Payment processing error", LogAlertingSystem.AlertSeverity.HIGH, 3, 300)); // Start monitoring system.startMonitoring(); // Add another log file dynamically system.addLogFile("another-app.log"); // Let it run for a while Thread.sleep(60000); // Check active alerts List<LogAlertingSystem.Alert> activeAlerts = system.getActiveAlerts(); System.out.println("Active alerts: " + activeAlerts.size()); // Stop monitoring system.stopMonitoring(); } } Features
- Real-time Log Monitoring: Tails log files and processes new entries
- Pattern-based Alerting: Regex patterns for flexible alert conditions
- Multiple Notification Channels: Console, Email, Slack, Log file
- Threshold-based Alerts: Configurable thresholds and time windows
- Alert Suppression: Prevents alert spam for duplicate conditions
- Auto-resolution: Automatic alert resolution after configured periods
- Comprehensive Reporting: HTML reports with system status
- Dynamic Configuration: Add/remove rules and log files at runtime
- Performance Optimized: Efficient circular buffer for log storage
Usage Examples
Basic Setup
LogAlertingSystem system = new LogAlertingSystem(); system.startMonitoring();
Advanced Configuration
LogAlertingSystem system = LogAlertingSystem.AlertingConfig.builder() .checkIntervalMs(1000) .logDirectory("/var/log/myapp/") .addNotifier(new SlackNotifier(webhookUrl, "#alerts")) .build(); Custom Alert Rules
AlertRule rule = new AlertRule( "SLOW_QUERY", ".*Query execution time: \\d{4,}ms.*", "Slow database queries detected", AlertSeverity.MEDIUM, 5, 600); system.addAlertRule(rule); This log alerting system provides comprehensive monitoring and alerting capabilities for Java applications, helping operations teams quickly identify and respond to issues through real-time pattern detection and multi-channel notifications.