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

131
app.py
View File

@ -2,10 +2,39 @@
import os import os
import json import json
import logging import logging
import shutil
from logging.handlers import TimedRotatingFileHandler 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 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 ──────────────────────────────────────────────────────────────── # ── Bluetooth ────────────────────────────────────────────────────────────────
from mkconnect.mouldking.MouldKing import MouldKing from mkconnect.mouldking.MouldKing import MouldKing
from mkconnect.tracer.TracerConsole import TracerConsole from mkconnect.tracer.TracerConsole import TracerConsole
@ -22,36 +51,45 @@ os.makedirs(app.config['CONFIG_DIR'], exist_ok=True)
LOG_DIR = 'logs' LOG_DIR = 'logs'
os.makedirs(LOG_DIR, exist_ok=True) 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') today = datetime.now().strftime('%Y-%m')
current_log_subdir = os.path.join(LOG_DIR, today) current_log_subdir = os.path.join(LOG_DIR, today)
os.makedirs(current_log_subdir, exist_ok=True) os.makedirs(current_log_subdir, exist_ok=True)
# Log-Datei: z. B. logs/2026-02/12.log # Basis-Dateiname (ohne Endung)
log_filename = os.path.join(current_log_subdir, datetime.now().strftime('%d.log')) 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 = logging.getLogger(__name__)
logger.setLevel(logging.INFO) logger.setLevel(logging.DEBUG) # DEBUG, damit alles durchkommt
# Handler: schreibt in die tagesaktuelle Datei logger.handlers.clear() # Alte Handler entfernen
handler = logging.FileHandler(log_filename) logger.addHandler(info_handler)
handler.setLevel(logging.INFO) logger.addHandler(error_handler)
# Format # Optional: Auch in Konsole
formatter = logging.Formatter('%(asctime)s | %(levelname)-8s | %(message)s', datefmt='%Y-%m-%d %H:%M:%S') console = logging.StreamHandler()
handler.setFormatter(formatter) console.setLevel(logging.INFO)
console.setFormatter(info_handler.formatter)
logger.addHandler(console)
# Alten Handler entfernen (falls schon gesetzt) logger.info("Logging getrennt initialisiert: info.log + error.log")
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)
# Bluetooth-Komponenten (einmalig initialisieren) # Bluetooth-Komponenten (einmalig initialisieren)
@ -252,15 +290,38 @@ def admin_delete_config(filename):
@app.route('/admin/logs') @app.route('/admin/logs')
def admin_logs(): @app.route('/admin/logs/<date>')
log_path = 'app.log' def admin_logs(date=None):
if os.path.exists(log_path): if date is None:
with open(log_path, 'r', encoding='utf-8') as f: date = datetime.now().strftime('%d')
logs = f.read()
else:
logs = "Keine Logs vorhanden."
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>') @app.route('/configs/<path:filename>')
@ -451,4 +512,12 @@ def api_reconnect():
if __name__ == '__main__': if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True) 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" %} {% extends "base.html" %}
{% block title %}Logs MK Control{% endblock %} {% block title %}Logs {{ date }}. {{ today[:4] }}-{{ today[5:] }}{% endblock %}
{% block content %} {% block content %}
<div class="container my-5"> <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 }} {{ logs | safe }}
</pre> </pre>
<div class="mt-4"> <a href="{{ url_for('admin') }}" class="btn btn-secondary mt-3">Zurück zum Admin</a>
<a href="{{ url_for('admin') }}" class="btn btn-secondary">Zurück zum Admin</a>
</div>
</div> </div>
{% endblock %} {% endblock %}