upload
This commit is contained in:
54
modules/base.py
Normal file
54
modules/base.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
Bazowa klasa dla modułów monitorowania logów
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
import threading
|
||||
|
||||
|
||||
class LogModule:
|
||||
"""Bazowa klasa dla modułów monitorowania"""
|
||||
|
||||
def __init__(self, config, daemon):
|
||||
"""
|
||||
Args:
|
||||
config: ConfigParser object z konfiguracją
|
||||
daemon: Referencja do głównego demona
|
||||
"""
|
||||
self.config = config
|
||||
self.daemon = daemon
|
||||
self.logger = logging.getLogger(self.__class__.__name__)
|
||||
self.running = False
|
||||
self.thread = None
|
||||
|
||||
def start(self):
|
||||
"""Uruchamia moduł w osobnym wątku"""
|
||||
if self.running:
|
||||
self.logger.warning("Module already running")
|
||||
return
|
||||
|
||||
self.running = True
|
||||
self.thread = threading.Thread(target=self._run, daemon=True)
|
||||
self.thread.start()
|
||||
self.logger.info(f"{self.__class__.__name__} started")
|
||||
|
||||
def stop(self):
|
||||
"""Zatrzymuje moduł"""
|
||||
self.running = False
|
||||
if self.thread and self.thread.is_alive():
|
||||
self.thread.join(timeout=5)
|
||||
self.logger.info(f"{self.__class__.__name__} stopped")
|
||||
|
||||
def _run(self):
|
||||
"""Główna pętla modułu - do nadpisania w klasach potomnych"""
|
||||
raise NotImplementedError("Subclasses must implement _run()")
|
||||
|
||||
def process_line(self, line):
|
||||
"""
|
||||
Przetwarza pojedynczą linię logu
|
||||
|
||||
Args:
|
||||
line: Linia tekstu z logu
|
||||
"""
|
||||
raise NotImplementedError("Subclasses must implement process_line()")
|
||||
8
modules/init.py
Normal file
8
modules/init.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
LogMon Modules - Moduły monitorowania różnych aplikacji
|
||||
"""
|
||||
|
||||
from .base import LogModule
|
||||
from .postfix import PostfixModule
|
||||
|
||||
__all__ = ['LogModule', 'PostfixModule']
|
||||
120
modules/postfix.py
Normal file
120
modules/postfix.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
Moduł monitorujący Postfix SMTP server
|
||||
"""
|
||||
|
||||
import re
|
||||
import time
|
||||
from .base import LogModule
|
||||
|
||||
|
||||
class PostfixModule(LogModule):
|
||||
"""Moduł monitorujący Postfix"""
|
||||
|
||||
def __init__(self, config, daemon):
|
||||
super().__init__(config, daemon)
|
||||
|
||||
# Kompiluj wzorce regex dla wydajności
|
||||
self.patterns = self._load_patterns()
|
||||
|
||||
# Regex do wyciągania IP z logów Postfix
|
||||
# Obsługuje zarówno unknown[IP] jak i hostname[IP]
|
||||
self.ip_pattern = re.compile(
|
||||
r'(?:unknown|[\w\-\.]+)\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]'
|
||||
)
|
||||
|
||||
# Ścieżka do pliku logu
|
||||
self.log_file = config.get('module_postfix', 'log_file',
|
||||
fallback='/var/log/mail.log')
|
||||
|
||||
self.logger.info(f"Loaded {len(self.patterns)} patterns for Postfix")
|
||||
|
||||
def _load_patterns(self):
|
||||
"""Ładuje wzorce z konfiguracji"""
|
||||
patterns = []
|
||||
pattern_names = self.config.get('module_postfix', 'patterns',
|
||||
fallback='').split(',')
|
||||
|
||||
for name in pattern_names:
|
||||
name = name.strip()
|
||||
if not name:
|
||||
continue
|
||||
|
||||
section = f'pattern_{name}'
|
||||
if section not in self.config:
|
||||
self.logger.warning(f"Pattern section '{section}' not found in config")
|
||||
continue
|
||||
|
||||
try:
|
||||
regex = self.config.get(section, 'regex')
|
||||
score = self.config.getint(section, 'score', fallback=1)
|
||||
|
||||
patterns.append({
|
||||
'name': name,
|
||||
'regex': re.compile(regex, re.IGNORECASE),
|
||||
'score': score
|
||||
})
|
||||
|
||||
self.logger.debug(f"Loaded pattern '{name}': {regex} (score: {score})")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error loading pattern '{name}': {e}")
|
||||
|
||||
return patterns
|
||||
|
||||
def _run(self):
|
||||
"""Główna pętla - tail -f na pliku logu"""
|
||||
self.logger.info(f"Tailing log file: {self.log_file}")
|
||||
|
||||
try:
|
||||
with open(self.log_file, 'r') as f:
|
||||
# Przejdź na koniec pliku
|
||||
f.seek(0, 2)
|
||||
|
||||
while self.running:
|
||||
line = f.readline()
|
||||
|
||||
if line:
|
||||
self.process_line(line.strip())
|
||||
else:
|
||||
# Brak nowych linii, czekaj chwilę
|
||||
time.sleep(0.1)
|
||||
|
||||
except FileNotFoundError:
|
||||
self.logger.error(f"Log file not found: {self.log_file}")
|
||||
except PermissionError:
|
||||
self.logger.error(f"Permission denied reading: {self.log_file}")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error tailing log: {e}")
|
||||
|
||||
def process_line(self, line):
|
||||
"""
|
||||
Przetwarza linię z logu Postfix
|
||||
|
||||
Przykłady linii:
|
||||
- postfix/smtpd[1234]: warning: unknown[1.2.3.4]: SASL LOGIN authentication failed
|
||||
- postfix/smtpd[1234]: connect from unknown[1.2.3.4]
|
||||
"""
|
||||
# Wyciągnij IP
|
||||
ip_match = self.ip_pattern.search(line)
|
||||
if not ip_match:
|
||||
return
|
||||
|
||||
ip = ip_match.group(1)
|
||||
|
||||
# Pomiń lokalne IP
|
||||
if ip.startswith('127.') or ip.startswith('192.168.') or ip.startswith('10.'):
|
||||
return
|
||||
|
||||
# Sprawdź wzorce
|
||||
for pattern in self.patterns:
|
||||
if pattern['regex'].search(line):
|
||||
self.logger.debug(
|
||||
f"Pattern '{pattern['name']}' matched for IP {ip}"
|
||||
)
|
||||
self.logger.debug(f"Line: {line}")
|
||||
|
||||
# Zgłoś niepowodzenie do demona
|
||||
self.daemon.track_failure(ip, pattern['score'])
|
||||
|
||||
# Tylko pierwszy pasujący wzorzec
|
||||
break
|
||||
Reference in New Issue
Block a user