cleanup for logfiles, added log for info and error, better logviewer in admin panel

This commit is contained in:
oberon 2026-02-12 22:12:35 +01:00
parent ebad9607a2
commit 633a30180c
2 changed files with 114 additions and 38 deletions

129
app.py
View File

@ -2,10 +2,39 @@
import os
import json
import logging
import shutil
from logging.handlers import TimedRotatingFileHandler
from datetime import datetime
from datetime import datetime, timedelta
from flask import Flask, render_template, redirect, url_for, send_from_directory, request, jsonify
# ── Cleanup Log-Files ────────────────────────────────────────────────────────────────
def cleanup_old_log_dirs(max_age_days=90):
"""Löscht Monatsordner in logs/, die älter als max_age_days sind"""
if not os.path.exists(LOG_DIR):
return
cutoff_date = datetime.now() - timedelta(days=max_age_days)
cutoff_str = cutoff_date.strftime('%Y-%m')
for subdir in os.listdir(LOG_DIR):
subdir_path = os.path.join(LOG_DIR, subdir)
if os.path.isdir(subdir_path):
try:
# subdir ist z. B. "2025-11"
subdir_date = datetime.strptime(subdir, '%Y-%m')
if subdir_date < cutoff_date:
logger.info(f"Lösche alten Log-Ordner: {subdir_path}")
shutil.rmtree(subdir_path)
except ValueError:
# Ungültiges Datumsformat → überspringen
pass
except Exception as e:
logger.error(f"Fehler beim Löschen von {subdir_path}: {e}")
# Beim Start aufrufen
cleanup_old_log_dirs(90)
logger.info("Alte Log-Ordner bereinigt")
# ── Bluetooth ────────────────────────────────────────────────────────────────
from mkconnect.mouldking.MouldKing import MouldKing
from mkconnect.tracer.TracerConsole import TracerConsole
@ -22,36 +51,45 @@ os.makedirs(app.config['CONFIG_DIR'], exist_ok=True)
LOG_DIR = 'logs'
os.makedirs(LOG_DIR, exist_ok=True)
# Täglich neuer Ordner (z. B. logs/2026-02)
# Täglicher Unterordner
today = datetime.now().strftime('%Y-%m')
current_log_subdir = os.path.join(LOG_DIR, today)
os.makedirs(current_log_subdir, exist_ok=True)
# Log-Datei: z. B. logs/2026-02/12.log
log_filename = os.path.join(current_log_subdir, datetime.now().strftime('%d.log'))
# Basis-Dateiname (ohne Endung)
base_filename = os.path.join(current_log_subdir, datetime.now().strftime('%d'))
# Logging konfigurieren
# Handler für INFO+
info_handler = logging.FileHandler(f"{base_filename}-info.log")
info_handler.setLevel(logging.INFO)
info_handler.setFormatter(logging.Formatter(
'%(asctime)s | %(levelname)-8s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
))
# Handler für WARNING+ (inkl. ERROR, CRITICAL)
error_handler = logging.FileHandler(f"{base_filename}-error.log")
error_handler.setLevel(logging.WARNING)
error_handler.setFormatter(logging.Formatter(
'%(asctime)s | %(levelname)-8s | %(message)s\n%(pathname)s:%(lineno)d\n',
datefmt='%Y-%m-%d %H:%M:%S'
))
# Logger konfigurieren
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.setLevel(logging.DEBUG) # DEBUG, damit alles durchkommt
# Handler: schreibt in die tagesaktuelle Datei
handler = logging.FileHandler(log_filename)
handler.setLevel(logging.INFO)
logger.handlers.clear() # Alte Handler entfernen
logger.addHandler(info_handler)
logger.addHandler(error_handler)
# Format
formatter = logging.Formatter('%(asctime)s | %(levelname)-8s | %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
handler.setFormatter(formatter)
# Optional: Auch in Konsole
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(info_handler.formatter)
logger.addHandler(console)
# Alten Handler entfernen (falls schon gesetzt)
logger.handlers.clear()
logger.addHandler(handler)
# Optional: Auch in Konsole loggen (für Entwicklung)
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
logger.info("Logging neu initialisiert Tages-Log: " + log_filename)
logger.info("Logging getrennt initialisiert: info.log + error.log")
# Bluetooth-Komponenten (einmalig initialisieren)
@ -252,15 +290,38 @@ def admin_delete_config(filename):
@app.route('/admin/logs')
def admin_logs():
log_path = 'app.log'
if os.path.exists(log_path):
with open(log_path, 'r', encoding='utf-8') as f:
logs = f.read()
else:
logs = "Keine Logs vorhanden."
@app.route('/admin/logs/<date>')
def admin_logs(date=None):
if date is None:
date = datetime.now().strftime('%d')
return render_template('admin_logs.html', logs=logs)
today = datetime.now().strftime('%Y-%m')
log_dir = os.path.join(LOG_DIR, today)
if not os.path.exists(log_dir):
return render_template('admin_logs.html', logs="Keine Logs für diesen Tag.", date=date, dates=[])
# Verfügbare Tage (alle .log-Dateien im Monatsordner)
dates = sorted([
f.split('-')[0] for f in os.listdir(log_dir)
if f.endswith('-info.log') or f.endswith('-error.log')
], reverse=True)
# Logs des gewählten Tages laden
info_path = os.path.join(log_dir, f"{date}-info.log")
error_path = os.path.join(log_dir, f"{date}-error.log")
logs = []
if os.path.exists(info_path):
with open(info_path, 'r', encoding='utf-8') as f:
logs.append("=== INFO-LOG ===\n" + f.read())
if os.path.exists(error_path):
with open(error_path, 'r', encoding='utf-8') as f:
logs.append("=== ERROR-LOG ===\n" + f.read())
log_content = "\n\n".join(logs) if logs else "Keine Logs für diesen Tag."
return render_template('admin_logs.html', logs=log_content, date=date, dates=dates)
@app.route('/configs/<path:filename>')
@ -452,3 +513,11 @@ def api_reconnect():
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
def daily_cleanup():
while True:
cleanup_old_log_dirs(90)
time.sleep(86400) # 24 Stunden
cleanup_thread = threading.Thread(target=daily_cleanup, daemon=True)
cleanup_thread.start()

View File

@ -1,17 +1,24 @@
{% extends "base.html" %}
{% block title %}Logs MK Control{% endblock %}
{% block title %}Logs {{ date }}. {{ today[:4] }}-{{ today[5:] }}{% endblock %}
{% block content %}
<div class="container my-5">
<h1>Log-Datei (app.log)</h1>
<h1>Logs {{ date }}. {{ today[:4] }}-{{ today[5:] }}</h1>
<pre class="bg-dark text-light p-4 rounded" style="max-height: 70vh; overflow-y: auto; font-size: 0.9rem;">
<div class="mb-4">
<label for="date-select" class="form-label">Tag auswählen:</label>
<select id="date-select" class="form-select w-auto d-inline-block" onchange="window.location.href='/admin/logs/' + this.value">
{% for d in dates %}
<option value="{{ d }}" {% if d == date %}selected{% endif %}>{{ d }}.</option>
{% endfor %}
</select>
</div>
<pre class="bg-dark text-light p-4 rounded" style="max-height: 70vh; overflow-y: auto; font-size: 0.95rem; white-space: pre-wrap;">
{{ logs | safe }}
</pre>
<div class="mt-4">
<a href="{{ url_for('admin') }}" class="btn btn-secondary">Zurück zum Admin</a>
</div>
<a href="{{ url_for('admin') }}" class="btn btn-secondary mt-3">Zurück zum Admin</a>
</div>
{% endblock %}