rss-news/pages/log_viewer.py
2025-08-18 10:33:27 +02:00

297 lines
No EOL
10 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# pages/log_viewer.py
import streamlit as st
import os
from utils.css_loader import load_css, apply_dark_theme
from datetime import datetime
# === CSS & Theme laden ===
load_css()
apply_dark_theme()
st.set_page_config(page_title="🧾 Log Viewer", layout="wide")
# Header
st.markdown("""
<div class="main-header">
<h1>🧾 Log Viewer</h1>
<p>Überwache Systemaktivitäten und Debug-Informationen</p>
</div>
""", unsafe_allow_html=True)
LOG_FILE = "logs/rss_tool.log"
MAX_LINES = 500
# === Log-Datei Kontrollen ===
st.markdown('<div class="filter-section">', unsafe_allow_html=True)
st.subheader("📁 Log-Datei Optionen")
col1, col2, col3, col4 = st.columns(4)
with col1:
lines_to_show = st.selectbox(
"Anzahl Zeilen",
[50, 100, 200, 500, 1000],
index=3, # Default: 500
key="lines_select"
)
with col2:
if st.button("🔄 Neu laden", use_container_width=True):
st.rerun()
with col3:
log_level_filter = st.selectbox(
"Log Level Filter",
["Alle", "INFO", "WARNING", "ERROR", "DEBUG"],
key="level_filter"
)
with col4:
search_term = st.text_input(
"Suche in Logs",
placeholder="Suchbegriff...",
key="log_search"
)
st.markdown('</div>', unsafe_allow_html=True)
# === Log-Datei Status ===
if not os.path.exists(LOG_FILE):
st.markdown("""
<div class="wp-status">
<strong>⚠️ Keine Log-Datei gefunden</strong><br>
<div class="text-secondary">
Die Log-Datei wurde noch nicht erstellt oder befindet sich an einem anderen Ort.<br>
Erwarteter Pfad: <code>{}</code>
</div>
</div>
""".format(LOG_FILE), unsafe_allow_html=True)
else:
# Datei-Informationen
file_size = os.path.getsize(LOG_FILE)
file_mtime = datetime.fromtimestamp(os.path.getmtime(LOG_FILE))
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("""
<div class="stats-card">
<div class="stats-number">{:.1f} KB</div>
<div>Dateigröße</div>
</div>
""".format(file_size / 1024), unsafe_allow_html=True)
with col2:
st.markdown("""
<div class="stats-card">
<div class="stats-number" style="font-size: 1.5rem;">{}</div>
<div>Letzte Änderung</div>
</div>
""".format(file_mtime.strftime("%H:%M:%S")), unsafe_allow_html=True)
with col3:
# Zeilen zählen
try:
with open(LOG_FILE, "r", encoding="utf-8") as f:
total_lines = sum(1 for _ in f)
except:
total_lines = 0
st.markdown("""
<div class="stats-card">
<div class="stats-number">{}</div>
<div>Zeilen Gesamt</div>
</div>
""".format(total_lines), unsafe_allow_html=True)
# === Log-Inhalt anzeigen ===
try:
with open(LOG_FILE, "r", encoding="utf-8") as f:
lines = f.readlines()
# Filter anwenden
filtered_lines = []
for line in lines:
# Log Level Filter
if log_level_filter != "Alle":
if f" - {log_level_filter} - " not in line:
continue
# Suchfilter
if search_term and search_term.lower() not in line.lower():
continue
filtered_lines.append(line)
# Anzahl begrenzen
display_lines = filtered_lines[-lines_to_show:] if len(filtered_lines) > lines_to_show else filtered_lines
# Header für Log-Anzeige
st.subheader(f"📋 Log-Einträge ({len(display_lines)} von {len(filtered_lines)} gefilterten Zeilen)")
if display_lines:
# Log-Inhalt mit Syntax-Highlighting
log_content = "".join(display_lines)
# Farbkodierung für verschiedene Log-Level
colored_content = log_content
colored_content = colored_content.replace(" - ERROR - ", " - 🔴 ERROR - ")
colored_content = colored_content.replace(" - WARNING - ", " - 🟡 WARNING - ")
colored_content = colored_content.replace(" - INFO - ", " - 🔵 INFO - ")
colored_content = colored_content.replace(" - DEBUG - ", " - ⚪ DEBUG - ")
# Log in Card anzeigen
st.markdown("""
<div class="article-card">
<h3 class="article-title">📄 Log-Ausgabe</h3>
<div class="article-meta">
Letzte {count} Einträge | Filter: {level} | Suche: "{search}"
</div>
</div>
""".format(
count=len(display_lines),
level=log_level_filter,
search=search_term or "Keine"
), unsafe_allow_html=True)
# Code-Block mit Logs
st.code(colored_content, language="text")
# Download-Button
st.download_button(
label="💾 Log-Datei herunterladen",
data=log_content,
file_name=f"rss_tool_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt",
mime="text/plain",
use_container_width=True
)
else:
st.markdown("""
<div class="wp-status">
<strong>🔍 Keine Log-Einträge gefunden</strong><br>
<div class="text-secondary">
Mit den aktuellen Filtern wurden keine Log-Einträge gefunden.<br>
Versuche andere Filter-Einstellungen.
</div>
</div>
""", unsafe_allow_html=True)
except Exception as e:
st.markdown(f"""
<div class="wp-status">
<strong>❌ Fehler beim Lesen der Log-Datei</strong><br>
<div class="text-secondary">
{str(e)}
</div>
</div>
""", unsafe_allow_html=True)
# === Log-Level Erklärung ===
with st.expander(" Log-Level Erklärung", expanded=False):
st.markdown("""
<div class="article-card">
<h3 class="article-title">📖 Log-Level Bedeutung</h3>
<div class="article-summary">
<strong>🔵 INFO:</strong> Normale Programmaktivitäten (Feed-Updates, Artikel verarbeitet)<br>
<strong>🟡 WARNING:</strong> Potentielle Probleme (Duplikate, fehlende Daten)<br>
<strong>🔴 ERROR:</strong> Fehler die das Programm beeinträchtigen<br>
<strong>⚪ DEBUG:</strong> Detaillierte Entwickler-Informationen
</div>
</div>
""", unsafe_allow_html=True)
# === Log-Datei verwalten ===
st.markdown("---")
st.subheader("🛠️ Log-Datei Verwaltung")
col1, col2, col3 = st.columns(3)
with col1:
if st.button("🗑️ Log-Datei leeren", use_container_width=True):
if st.button("⚠️ Wirklich leeren?", key="confirm_clear"):
try:
with open(LOG_FILE, "w", encoding="utf-8") as f:
f.write("")
st.success("Log-Datei wurde geleert!")
st.rerun()
except Exception as e:
st.error(f"Fehler beim Leeren der Log-Datei: {e}")
with col2:
if st.button("📦 Log archivieren", use_container_width=True):
try:
archive_name = f"rss_tool_log_archive_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
with open(LOG_FILE, "r", encoding="utf-8") as f:
log_data = f.read()
st.download_button(
label=f"💾 {archive_name}",
data=log_data,
file_name=archive_name,
mime="text/plain",
key="archive_download"
)
except Exception as e:
st.error(f"Fehler beim Archivieren: {e}")
with col3:
if st.button("📊 Log-Statistiken", use_container_width=True):
if os.path.exists(LOG_FILE):
try:
with open(LOG_FILE, "r", encoding="utf-8") as f:
all_lines = f.readlines()
# Statistiken berechnen
total_lines = len(all_lines)
info_count = sum(1 for line in all_lines if " - INFO - " in line)
warning_count = sum(1 for line in all_lines if " - WARNING - " in line)
error_count = sum(1 for line in all_lines if " - ERROR - " in line)
debug_count = sum(1 for line in all_lines if " - DEBUG - " in line)
st.subheader("📈 Log-Statistiken")
col1, col2, col3, col4 = st.columns(4)
with col1:
st.markdown("""
<div class="stats-card">
<div class="stats-number">{}</div>
<div>🔵 INFO</div>
</div>
""".format(info_count), unsafe_allow_html=True)
with col2:
st.markdown("""
<div class="stats-card">
<div class="stats-number">{}</div>
<div>🟡 WARNING</div>
</div>
""".format(warning_count), unsafe_allow_html=True)
with col3:
st.markdown("""
<div class="stats-card">
<div class="stats-number">{}</div>
<div>🔴 ERROR</div>
</div>
""".format(error_count), unsafe_allow_html=True)
with col4:
st.markdown("""
<div class="stats-card">
<div class="stats-number">{}</div>
<div>⚪ DEBUG</div>
</div>
""".format(debug_count), unsafe_allow_html=True)
except Exception as e:
st.error(f"Fehler beim Berechnen der Statistiken: {e}")
# === Auto-Refresh Option ===
if st.checkbox("🔄 Auto-Refresh (30s)", key="auto_refresh"):
import time
time.sleep(30)
st.rerun()