本文目录导读:

基础Shell脚本方案
实时监控tail + grep
#!/bin/bash
# 监控日志文件
LOG_FILE="/var/log/application.log"
KEYWORDS=("ERROR" "FATAL" "Exception")
ALERT_EMAIL="admin@example.com"
# 使用tail -F持续监控
tail -F $LOG_FILE | while read line; do
for keyword in "${KEYWORDS[@]}"; do
if echo "$line" | grep -iq "$keyword"; then
# 发送告警
echo "$(date): 发现关键字: $keyword" | mail -s "日志告警" $ALERT_EMAIL
echo "告警: $line"
fi
done
done
定时检查模式
#!/bin/bash
LOG_FILE="/var/log/application.log"
ALERT_EMAIL="admin@example.com"
CHECK_INTERVAL=60 # 检查间隔(秒)
while true; do
# 检查最近1分钟的错误日志
RECENT_LOGS=$(tail -n 100 $LOG_FILE | grep "ERROR")
if [ -n "$RECENT_LOGS" ]; then
# 发送告警邮件
echo "$RECENT_LOGS" | mail -s "$(hostname) 日志告警" $ALERT_EMAIL
# 或写入告警文件
echo "$(date): 发现错误日志" >> /var/log/alert_history.log
fi
sleep $CHECK_INTERVAL
done
Python方案(推荐)
基础监控脚本
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import re
import argparse
import logging
from datetime import datetime
import smtplib
from email.mime.text import MIMEText
import requests
import subprocess
import os
class LogMonitor:
def __init__(self, log_file, keywords, alert_method='email'):
self.log_file = log_file
self.keywords = keywords
self.alert_method = alert_method
self.last_position = 0
self.setup_logging()
def setup_logging(self):
"""设置日志记录"""
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
self.logger = logging.getLogger(__name__)
def follow(self):
"""实时跟踪日志文件"""
try:
with open(self.log_file, 'r') as f:
# 移动到文件末尾
f.seek(0, 2)
while True:
line = f.readline()
if line:
self.process_line(line.strip())
else:
time.sleep(0.1)
except FileNotFoundError:
self.logger.error(f"日志文件不存在: {self.log_file}")
except Exception as e:
self.logger.error(f"监控错误: {e}")
def process_line(self, line):
"""处理每行日志"""
for keyword in self.keywords:
if re.search(keyword, line, re.IGNORECASE):
self.trigger_alert(f"发现关键字: {keyword}\n日志内容: {line}")
def trigger_alert(self, message):
"""触发告警"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
alert_msg = f"[{timestamp}] {message}"
self.logger.warning(alert_msg)
if self.alert_method == 'email':
self.send_email_alert(alert_msg)
elif self.alert_method == 'webhook':
self.send_webhook_alert(alert_msg)
elif self.alert_method == 'command':
self.run_alert_command(alert_msg)
def send_email_alert(self, message):
"""发送邮件告警"""
msg = MIMEText(message)
msg['Subject'] = f'日志告警 - {self.log_file}'
msg['From'] = 'monitor@example.com'
msg['To'] = 'admin@example.com'
try:
with smtplib.SMTP('localhost') as server:
server.send_message(msg)
except Exception as e:
self.logger.error(f"发送邮件失败: {e}")
def send_webhook_alert(self, message):
"""发送Webhook告警"""
webhook_url = "https://hooks.slack.com/services/xxx/xxx/xxx"
payload = {
"text": message,
"username": "LogMonitor",
"icon_emoji": ":warning:"
}
try:
response = requests.post(webhook_url, json=payload)
response.raise_for_status()
except Exception as e:
self.logger.error(f"发送Webhook失败: {e}")
def run_alert_command(self, message):
"""执行命令式告警"""
command = f'echo "{message}" | wall' # 发送到所有终端
try:
subprocess.run(command, shell=True)
except Exception as e:
self.logger.error(f"执行命令失败: {e}")
def check_rate_limit(self, keyword, interval=300):
"""检查频率限制"""
# 实现在指定时间间隔内不重复告警
pass
def main():
parser = argparse.ArgumentParser(description='日志监控告警工具')
parser.add_argument('--file', '-f', required=True, help='要监控的日志文件')
parser.add_argument('--keywords', '-k', required=True, help='关键字列表(逗号分隔)')
parser.add_argument('--method', '-m', default='email',
choices=['email', 'webhook', 'command', 'stdout'],
help='告警方式')
args = parser.parse_args()
keywords = [k.strip() for k in args.keywords.split(',')]
monitor = LogMonitor(args.file, keywords, args.method)
monitor.follow()
if __name__ == "__main__":
main()
高级监控脚本
带状态跟踪和阈值告警
#!/usr/bin/env python3
import time
import hashlib
import json
from collections import defaultdict
from datetime import datetime, timedelta
class AdvancedLogMonitor:
def __init__(self):
self.state_file = "/tmp/monitor_state.json"
self.error_counts = defaultdict(int)
self.last_alert_time = {}
self.load_state()
def load_state(self):
"""加载监控状态"""
try:
with open(self.state_file, 'r') as f:
data = json.load(f)
self.last_alert_time = data.get('last_alert_time', {})
except:
pass
def save_state(self):
"""保存监控状态"""
with open(self.state_file, 'w') as f:
json.dump({
'last_alert_time': self.last_alert_time
}, f)
def check_threshold(self, error_type, count, threshold=5, window=300):
"""
检查是否达到告警阈值
:param error_type: 错误类型
:param count: 当前计数
:param threshold: 阈值
:param window: 时间窗口(秒)
"""
now = time.time()
# 检查是否在冷静期
if error_type in self.last_alert_time:
last_time = self.last_alert_time[error_type]
if now - last_time < 300: # 5分钟冷静期
return False
# 检查阈值
if count >= threshold:
self.last_alert_time[error_type] = now
self.save_state()
return True
return False
def analyze_log_pattern(self, log_line):
"""
分析日志模式
:param log_line: 日志行
:return: 错误类型或None
"""
patterns = {
'database': ['ORA-', 'MySQL', 'connection refused', 'timeout'],
'application': ['NullPointer', 'ArrayIndexOutOfBounds', 'StackOverflow'],
'system': ['OutOfMemory', 'Disk full', 'CPU overload'],
'security': ['Unauthorized', 'Access denied', 'Invalid token']
}
for error_type, keywords in patterns.items():
for keyword in keywords:
if keyword.lower() in log_line.lower():
return error_type
return None
def aggregate_errors(self, log_file, time_window=60):
"""
聚合错误统计
:param log_file: 日志文件
:param time_window: 时间窗口(秒)
"""
error_pattern = re.compile(r'(ERROR|FATAL|CRITICAL):\s*(.*)')
cut_off = datetime.now() - timedelta(seconds=time_window)
try:
with open(log_file, 'r') as f:
for line in f:
# 解析时间戳
timestamp_match = re.match(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line)
if timestamp_match:
log_time = datetime.strptime(timestamp_match.group(1),
"%Y-%m-%d %H:%M:%S")
if log_time > cut_off:
error_match = error_pattern.search(line)
if error_match:
error_type = self.analyze_log_pattern(line)
if error_type:
self.error_counts[error_type] += 1
except FileNotFoundError:
print(f"日志文件不存在: {log_file}")
def monitor_loop(self, log_file, check_interval=60):
"""
主监控循环
:param log_file: 日志文件
:param check_interval: 检查间隔(秒)
"""
print(f"开始监控日志文件: {log_file}")
print(f"检查间隔: {check_interval}秒")
while True:
self.error_counts.clear()
self.aggregate_errors(log_file)
for error_type, count in self.error_counts.items():
if self.check_threshold(error_type, count):
alert_msg = f"[告警] {error_type}错误频繁: {count}次/分钟"
print(alert_msg)
# 发送告警
self.send_alert(alert_msg)
time.sleep(check_interval)
def send_alert(self, message):
"""发送告警"""
# 多种告警方式
self.send_wechat_alert(message)
self.send_email_alert(message)
self.log_alert(message)
def send_wechat_alert(self, message):
"""发送微信告警"""
# 实现企业微信机器人告警
pass
def send_email_alert(self, message):
"""发送邮件告警"""
# 实现邮件告警
pass
def log_alert(self, message):
"""记录告警日志"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open("/var/log/monitor_alerts.log", "a") as f:
f.write(f"[{timestamp}] {message}\n")
# 监控多个日志文件
def monitor_multiple_logs(log_files):
"""监控多个日志文件"""
import threading
threads = []
for log_file in log_files:
monitor = AdvancedLogMonitor()
thread = threading.Thread(target=monitor.monitor_loop, args=(log_file,))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
if __name__ == "__main__":
monitor = AdvancedLogMonitor()
monitor.monitor_loop("/var/log/application.log")
使用systemd服务管理
创建systemd服务文件
# /etc/systemd/system/log-monitor.service [Unit] Description=Log Monitor Service After=network.target [Service] Type=simple User=root ExecStart=/usr/local/bin/log_monitor.py --file /var/log/application.log --keywords ERROR,FATAL --method email Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
启动服务
# 重载systemd配置 sudo systemctl daemon-reload # 启动服务 sudo systemctl start log-monitor # 设置开机自启 sudo systemctl enable log-monitor # 查看状态 sudo systemctl status log-monitor
实用示例
简单监控脚本(生产可用)
#!/bin/bash
# 生产环境日志监控脚本
LOG_DIR="/var/log/applications"
ALERT_EMAIL="ops@company.com"
SLACK_WEBHOOK="https://hooks.slack.com/services/xxx"
# 监控API错误
monitor_api_errors() {
for log_file in $LOG_DIR/*/api.log; do
# 查找5分钟内的错误
errors=$(tail -n 1000 "$log_file" | grep "HTTP 5[0-9][0-9]" | tail -20)
if [ -n "$errors" ]; then
# 构建告警消息
message="API错误告警 - $(hostname)\n服务: $(basename $(dirname $log_file))\n时间: $(date)\n错误:\n$errors"
# 发送告警
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$message\"}" \
$SLACK_WEBHOOK
fi
done
}
# 监控磁盘使用
monitor_disk_usage() {
threshold=90
df -h | grep -v Filesystem | awk '{print $5, $6}' | while read output; do
usage=$(echo $output | awk '{print $1}' | sed 's/%//')
partition=$(echo $output | awk '{print $2}')
if [ $usage -ge $threshold ]; then
# 记录到系统日志
logger "磁盘使用告警: $partition 使用率 $usage%"
# 发送告警
mail -s "磁盘告警: $partition 使用率 $usage%" $ALERT_EMAIL
fi
done
}
# 监控进程状态
monitor_process() {
processes=("nginx" "mysql" "redis" "java")
for proc in "${processes[@]}"; do
if ! pgrep -x "$proc" > /dev/null; then
message="进程告警: $proc 未运行"
echo "$message" | mail -s "进程告警" $ALERT_EMAIL
# 尝试重启
systemctl restart "$proc" 2>/dev/null || service "$proc" restart 2>/dev/null
fi
done
}
# 主循环
while true; do
monitor_api_errors
monitor_disk_usage
monitor_process
sleep 60
done
使用建议
- 选择合适的告警级别:ERROR/FATAL直接告警,WARNING可以汇总
- 设置告警去重:相同的错误不要重复告警
- 实施冷静期:同一错误至少间隔5分钟再告警
- 分级告警:重要服务直接电话通知,普通错误邮件通知
- 记录告警历史:便于事后分析和优化
- 测试告警通道:定期验证告警是否正常送达
这些脚本可以根据实际需求修改,建议先在测试环境验证后再部署到生产环境。