Alerting on Log Patterns in Java

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.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper