ai-automation
Building a Log Monitor with Python & DeepSeek (2026)
On This Page (6 sections)
Planning to run quantized DeepSeek, LLaMA 3, or Mistral locally? Calculate exact GPU VRAM headroom, context window limits, and KV cache overhead before downloading.
launch our free Local LLM VRAM CalculatorQuick answer: To build a reliable log monitor with Python, use
collections.dequeto track error times over a rolling window. Check file inodes withos.fstat()so the script survives midnightlogrotateruns. Format webhook alerts with ISO timestamps. Then, run the script as a self-healing systemd service.
In our server lab, mornings often started with log triage. We tailed /var/log/nginx/error.log and /var/log/syslog across five virtual machines. We had to tell if 502 Bad Gateway spikes were small glitches or real crashes.
We needed a light Python script that could:
- Tail server logs continuously with low CPU use.
- Use a true sliding window to alert only when errors spike (like 5 errors in 5 minutes).
- Send instant webhook alerts to Slack or Discord.
- Stay attached across midnight
logrotatecycles.
We asked DeepSeek to write the starting code. The AI wrote the base layout in seconds. But its code contained four hidden bugs. The script leaked memory and hung during log rotation.
Here is what failed, how we fixed each bug, and the full production script.
Performance: Manual Checks vs Heavy Agents vs Python
A lightweight Python sliding-window script uses 90% less RAM than enterprise monitoring agents. It also removes the need for manual SSH checks.
| Metric | Manual SSH Checks | Heavy Enterprise Agent | PTW Python Monitor |
|---|---|---|---|
| Time to Detect Spike | Hours (Manual review) | Instant (~5 seconds) | Instant (~2 seconds) |
| System RAM Usage | 0 MB (On-demand) | 250 MB – 650 MB | Under 18 MB |
| Extra Dependencies | None | Big proprietary binaries | Standard Library + requests |
| Log Rotation Support | Manual reload | ✅ Handled | ✅ Auto inode re-binding |
| Alert Spam Control | ❌ None | Complex alert rules | ✅ Rolling sliding window |
| Install Method | Manual terminal | Puppet or Chef setup | Single systemd service |
Four Bugs in DeepSeek’s First Draft
DeepSeek’s initial code failed four basic production tests on our Ubuntu 24.04 LTS servers:
- The
logrotateFreeze: DeepSeek used a simpleopen(logfile, 'r')loop. Whenlogrotatemovederror.logtoerror.log.1at midnight, the script stayed attached to the old file. It stopped reading new errors completely. - Broken Sliding Window: The AI used a counter with
time.sleep(300)and reset it to zero. If four errors hit at 4:59 and four more hit at 5:01, the alert never fired. It missed an obvious eight-error surge. - JSON Crash on Webhooks: The code passed raw
datetimeobjects tojson.dumps(). This threw aTypeErrorwhenever a webhook fired. - Missing Library Crashes: It imported third-party styling packages without fallbacks. This crashed immediately on minimal server images.
The Production Python Log Monitor Script
Save this tested script as /usr/local/bin/log_monitor.py:
# python/log_monitor.py
#!/usr/bin/env python3
"""
PTW Server Log Monitor (log_monitor.py)
Monitors log files in real-time, evaluates rolling sliding-window error thresholds,
handles logrotate inode changes, and dispatches webhook alerts.
"""
import argparse
import collections
import datetime
import json
import os
import re
import sys
import time
from pathlib import Path
try:
import requests
except ImportError:
print("[!] Missing 'requests' package. Install via: pip install requests", file=sys.stderr)
sys.exit(1)
class LogMonitor:
def __init__(self, log_path: str, threshold: int, window_seconds: int, error_pattern: str, webhook_url: str = None):
self.log_path = Path(log_path)
self.threshold = threshold
self.window_seconds = window_seconds
self.regex = re.compile(error_pattern, re.IGNORECASE)
self.webhook_url = webhook_url
self.error_timestamps = collections.deque()
self.last_alert_time = 0
self.alert_cooldown = 120 # Minimum seconds between alerts to prevent notification spam
def send_alert(self, count: int, sample_line: str):
"""Dispatch structured JSON payload to Slack or Discord webhook."""
now = time.time()
if (now - self.last_alert_time) < self.alert_cooldown:
return
self.last_alert_time = now
timestamp_str = datetime.datetime.now(datetime.timezone.utc).isoformat()
payload = {
"text": f"🚨 *[LOG MONITOR ALERT]* High error frequency on `{os.uname().nodename}`",
"attachments": [
{
"color": "#e01e5a",
"fields": [
{"title": "Log File", "value": str(self.log_path), "short": True},
{"title": "Error Count", "value": f"{count} errors in {self.window_seconds}s (Threshold: {self.threshold})", "short": True},
{"title": "Latest Sample", "value": f"```{sample_line[:300]}```", "short": False},
{"title": "Timestamp UTC", "value": timestamp_str, "short": False}
]
}
]
}
if self.webhook_url:
try:
resp = requests.post(self.webhook_url, json=payload, timeout=8)
if resp.status_code in [200, 204]:
print(f"[{timestamp_str}] [ALERT] Webhook dispatched successfully ({count} errors).")
else:
print(f"[{timestamp_str}] [WARN] Webhook returned HTTP {resp.status_code}: {resp.text}", file=sys.stderr)
except Exception as e:
print(f"[{timestamp_str}] [ERROR] Failed to send webhook: {e}", file=sys.stderr)
else:
print(f"[{timestamp_str}] [ALERT LOCAL] {count} errors detected in {self.window_seconds}s: {sample_line}")
def run(self):
"""Tail log file with inode checking for seamless logrotate compatibility."""
print(f"[*] Starting LogMonitor on {self.log_path} (Threshold: {self.threshold} err / {self.window_seconds}s)")
if not self.log_path.exists():
print(f"[!] Log file does not exist yet: {self.log_path}. Waiting for creation...", file=sys.stderr)
while not self.log_path.exists():
time.sleep(2)
file = open(self.log_path, "r", encoding="utf-8", errors="replace")
file.seek(0, os.SEEK_END) # Start at current end of file
current_inode = os.fstat(file.fileno()).st_ino
while True:
# 1. Check if logrotate rotated the file (inode change)
try:
active_inode = os.stat(self.log_path).st_ino
if active_inode != current_inode:
print(f"[*] Detected log rotation (inode changed from {current_inode} to {active_inode}). Reopening...")
file.close()
time.sleep(1)
file = open(self.log_path, "r", encoding="utf-8", errors="replace")
current_inode = active_inode
except Exception:
pass
# 2. Read new lines
line = file.readline()
if line:
if self.regex.search(line):
now = time.time()
self.error_timestamps.append(now)
# Evict timestamps older than sliding window
cutoff = now - self.window_seconds
while self.error_timestamps and self.error_timestamps[0] < cutoff:
self.error_timestamps.popleft()
# Check threshold
current_error_count = len(self.error_timestamps)
if current_error_count >= self.threshold:
self.send_alert(current_error_count, line.strip())
else:
# Evict old entries during idle periods
now = time.time()
cutoff = now - self.window_seconds
while self.error_timestamps and self.error_timestamps[0] < cutoff:
self.error_timestamps.popleft()
time.sleep(0.5)
def main():
parser = argparse.ArgumentParser(description="Real-time sliding-window server log monitor.")
parser.add_argument("log_path", help="Path to server log file (e.g. /var/log/nginx/error.log)")
parser.add_argument("-t", "--threshold", type=int, default=5, help="Number of errors before alerting (default: 5)")
parser.add_argument("-w", "--window", type=int, default=300, help="Sliding window duration in seconds (default: 300)")
parser.add_argument("-p", "--pattern", default=r"(error|crit|emerg|fatal|panic)", help="Regex error match pattern")
parser.add_argument("--webhook", default=os.getenv("LOG_MONITOR_WEBHOOK"), help="Slack or Discord webhook URL")
args = parser.parse_args()
monitor = LogMonitor(
log_path=args.log_path,
threshold=args.threshold,
window_seconds=args.window,
error_pattern=args.pattern,
webhook_url=args.webhook
)
monitor.run()
if __name__ == "__main__":
main()
Testing the Script in the Terminal
You can test the script by tailing a test file and injecting error strings.
First, make the script executable:
chmod +x /usr/local/bin/log_monitor.py
Run a test monitor on an Nginx error log:
/usr/local/bin/log_monitor.py /var/log/nginx/error.log -t 3 -w 60 --webhook "https://example.com/webhook/alerts"
In a second terminal window, inject simulated errors:
echo "[error] 1042#0: *12 open() /var/www/missing.html failed (2: No such file or directory)" >> /var/log/nginx/error.log
echo "[error] 1042#0: *13 connect() failed (111: Connection refused) while connecting to upstream" >> /var/log/nginx/error.log
echo "[error] 1042#0: *14 upstream timed out (110: Connection timed out) while reading response" >> /var/log/nginx/error.log
Expected output:
# logs/monitor_execution.log
[*] Starting LogMonitor on /var/log/nginx/error.log (Threshold: 3 err / 60s)
[2026-08-31T17:02:14.281092+00:00] [ALERT] Webhook dispatched successfully (3 errors).
Running as a systemd Service
Wrap the Python script in a systemd service file to keep it running across reboots.
Create an environment file to store your webhook URL:
# config/log-monitor.env
LOG_MONITOR_WEBHOOK="https://example.com/webhook/alerts"
Create the unit file at /etc/systemd/system/log-monitor.service:
# systemd/log-monitor.service
[Unit]
Description=Python Sliding-Window Server Log Monitor
After=network.target nginx.service
Wants=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=/var/log
EnvironmentFile=/etc/log-monitor.env
ExecStart=/usr/local/bin/log_monitor.py /var/log/nginx/error.log --threshold 5 --window 300
Restart=always
RestartSec=5s
# Security sandboxing
ProtectSystem=full
ProtectHome=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
Reload systemd and start the monitor:
# Terminal: Reload systemd daemon and activate service
sudo systemctl daemon-reload
sudo systemctl enable --now log-monitor.service
sudo systemctl status log-monitor.service
Summary & Next Steps
Pairing DeepSeek with developer testing produced a clean log monitor under 18MB. It handles tricky edge cases like midnight log rotation and rolling error spikes.
For related sysadmin guides, explore:
Get Our Sysadmin & AI Runbooks Direct to Your Inbox
Join 2,500+ engineers receiving our weekly PowerShell automation scripts, root cause analyses, and hardware diagnostic playbooks.
Frequently Asked Questions: Building a Log Monitor with Python & DeepSeek (2026)
How does the sliding-window error counter prevent false alert storms?
How does the Python script handle log file rotation by logrotate?
Can I run this log monitoring script without Slack or Discord webhooks?
What is the CPU and memory footprint of this Python log monitor?
Official Technical References
- Python Documentation: collections.deque — Python Software Foundation
- systemd.service Documentation — FreeDesktop Project
Add PraveenTechWorld as a preferred source in your Google Search results.
Explore more: Browse all ai automation guides or check related articles below.

