feat: Implement comprehensive non-hierarchical logging system

- Created new logging infrastructure with per-component filtering
- Added 6 log levels: DEBUG, INFO, API, WARNING, ERROR, CRITICAL
- Implemented non-hierarchical level control (any combination can be enabled)
- Migrated 917 print() statements across 31 files to structured logging
- Created web UI (system.html) for runtime configuration with dark theme
- Added global level controls to enable/disable levels across all components
- Added timestamp format control (off/time/date/datetime options)
- Implemented log rotation (10MB per file, 5 backups)
- Added API endpoints for dynamic log configuration
- Configured HTTP request logging with filtering via api.requests component
- Intercepted APScheduler logs with proper formatting
- Fixed persistence paths to use /app/memory for Docker volume compatibility
- Fixed checkbox display bug in web UI (enabled_levels now properly shown)
- Changed System Settings button to open in same tab instead of new window

Components: bot, api, api.requests, autonomous, persona, vision, llm,
conversation, mood, dm, scheduled, gpu, media, server, commands,
sentiment, core, apscheduler

All settings persist across container restarts via JSON config.
This commit is contained in:
2026-01-10 20:46:19 +02:00
parent ce00f9bd95
commit 32c2a7b930
34 changed files with 2766 additions and 936 deletions

View File

@@ -9,6 +9,9 @@ import os
import random
import json
import globals
from utils.logger import get_logger
logger = get_logger('persona')
# ============================================================================
# EVIL MODE PERSISTENCE
@@ -40,16 +43,16 @@ def save_evil_mode_state(saved_role_color=None):
}
with open(EVIL_MODE_STATE_FILE, "w", encoding="utf-8") as f:
json.dump(state, f, indent=2)
print(f"💾 Saved evil mode state: {state}")
logger.debug(f"Saved evil mode state: {state}")
except Exception as e:
print(f"⚠️ Failed to save evil mode state: {e}")
logger.error(f"Failed to save evil mode state: {e}")
def load_evil_mode_state():
"""Load evil mode state from JSON file"""
try:
if not os.path.exists(EVIL_MODE_STATE_FILE):
print(f" No evil mode state file found, using defaults")
logger.info(f"No evil mode state file found, using defaults")
return False, "evil_neutral", None
with open(EVIL_MODE_STATE_FILE, "r", encoding="utf-8") as f:
@@ -58,10 +61,10 @@ def load_evil_mode_state():
evil_mode = state.get("evil_mode_enabled", False)
evil_mood = state.get("evil_mood", "evil_neutral")
saved_role_color = state.get("saved_role_color")
print(f"📂 Loaded evil mode state: evil_mode={evil_mode}, mood={evil_mood}, saved_color={saved_role_color}")
logger.debug(f"Loaded evil mode state: evil_mode={evil_mode}, mood={evil_mood}, saved_color={saved_role_color}")
return evil_mode, evil_mood, saved_role_color
except Exception as e:
print(f"⚠️ Failed to load evil mode state: {e}")
logger.error(f"Failed to load evil mode state: {e}")
return False, "evil_neutral", None
@@ -70,13 +73,13 @@ def restore_evil_mode_on_startup():
evil_mode, evil_mood, saved_role_color = load_evil_mode_state()
if evil_mode:
print("😈 Restoring evil mode from previous session...")
logger.debug("Restoring evil mode from previous session...")
globals.EVIL_MODE = True
globals.EVIL_DM_MOOD = evil_mood
globals.EVIL_DM_MOOD_DESCRIPTION = load_evil_mood_description(evil_mood)
print(f"😈 Evil mode restored: {evil_mood}")
logger.info(f"Evil mode restored: {evil_mood}")
else:
print("🎤 Normal mode active")
logger.info("Normal mode active")
return evil_mode
@@ -90,7 +93,7 @@ def get_evil_miku_lore() -> str:
with open("evil_miku_lore.txt", "r", encoding="utf-8") as f:
return f.read()
except Exception as e:
print(f"⚠️ Failed to load evil_miku_lore.txt: {e}")
logger.error(f"Failed to load evil_miku_lore.txt: {e}")
return "## EVIL MIKU LORE\n[File could not be loaded]"
@@ -100,7 +103,7 @@ def get_evil_miku_prompt() -> str:
with open("evil_miku_prompt.txt", "r", encoding="utf-8") as f:
return f.read()
except Exception as e:
print(f"⚠️ Failed to load evil_miku_prompt.txt: {e}")
logger.error(f"Failed to load evil_miku_prompt.txt: {e}")
return "## EVIL MIKU PROMPT\n[File could not be loaded]"
@@ -110,7 +113,7 @@ def get_evil_miku_lyrics() -> str:
with open("evil_miku_lyrics.txt", "r", encoding="utf-8") as f:
return f.read()
except Exception as e:
print(f"⚠️ Failed to load evil_miku_lyrics.txt: {e}")
logger.error(f"Failed to load evil_miku_lyrics.txt: {e}")
return "## EVIL MIKU LYRICS\n[File could not be loaded]"
@@ -178,7 +181,7 @@ def load_evil_mood_description(mood_name: str) -> str:
with open(path, "r", encoding="utf-8") as f:
return f.read().strip()
except FileNotFoundError:
print(f"⚠️ Evil mood file '{mood_name}' not found. Falling back to evil_neutral.")
logger.warning(f"Evil mood file '{mood_name}' not found. Falling back to evil_neutral.")
try:
with open(os.path.join("moods", "evil", "evil_neutral.txt"), "r", encoding="utf-8") as f:
return f.read().strip()
@@ -338,13 +341,13 @@ async def get_current_role_color(client) -> str:
if role.name.lower() in ["miku color", "miku colour", "miku-color"]:
# Convert discord.Color to hex
hex_color = f"#{role.color.value:06x}"
print(f"🎨 Current role color: {hex_color}")
logger.debug(f"Current role color: {hex_color}")
return hex_color
print("⚠️ No 'Miku Color' role found in any server")
logger.warning("No 'Miku Color' role found in any server")
return None
except Exception as e:
print(f"⚠️ Failed to get current role color: {e}")
logger.warning(f"Failed to get current role color: {e}")
return None
@@ -377,14 +380,14 @@ async def set_role_color(client, hex_color: str):
if color_role:
await color_role.edit(color=discord_color, reason="Evil mode color change")
updated_count += 1
print(f" 🎨 Updated role color in {guild.name}: #{hex_color}")
logger.debug(f"Updated role color in {guild.name}: #{hex_color}")
except Exception as e:
print(f" ⚠️ Failed to update role color in {guild.name}: {e}")
logger.warning(f"Failed to update role color in {guild.name}: {e}")
print(f"🎨 Updated role color in {updated_count} server(s) to #{hex_color}")
logger.info(f"Updated role color in {updated_count} server(s) to #{hex_color}")
return updated_count > 0
except Exception as e:
print(f"⚠️ Failed to set role color: {e}")
logger.error(f"Failed to set role color: {e}")
return False
@@ -398,7 +401,7 @@ async def apply_evil_mode_changes(client, change_username=True, change_pfp=True,
change_nicknames: Whether to change server nicknames (default True, but skip on startup restore)
change_role_color: Whether to change role color (default True, but skip on startup restore)
"""
print("😈 Enabling Evil Mode...")
logger.info("Enabling Evil Mode...")
# Save current role color before changing (if we're actually changing it)
if change_role_color:
@@ -412,9 +415,9 @@ async def apply_evil_mode_changes(client, change_username=True, change_pfp=True,
if change_username:
try:
await client.user.edit(username="Evil Miku")
print("Changed bot username to 'Evil Miku'")
logger.debug("Changed bot username to 'Evil Miku'")
except Exception as e:
print(f"⚠️ Could not change bot username: {e}")
logger.error(f"Could not change bot username: {e}")
# Update nicknames in all servers
if change_nicknames:
@@ -431,7 +434,7 @@ async def apply_evil_mode_changes(client, change_username=True, change_pfp=True,
# Save state to file
save_evil_mode_state()
print("😈 Evil Mode enabled!")
logger.info("Evil Mode enabled!")
async def revert_evil_mode_changes(client, change_username=True, change_pfp=True, change_nicknames=True, change_role_color=True):
@@ -444,16 +447,16 @@ async def revert_evil_mode_changes(client, change_username=True, change_pfp=True
change_nicknames: Whether to change server nicknames (default True, but skip on startup restore)
change_role_color: Whether to restore role color (default True, but skip on startup restore)
"""
print("🎤 Disabling Evil Mode...")
logger.info("Disabling Evil Mode...")
globals.EVIL_MODE = False
# Change bot username back
if change_username:
try:
await client.user.edit(username="Hatsune Miku")
print("Changed bot username back to 'Hatsune Miku'")
logger.debug("Changed bot username back to 'Hatsune Miku'")
except Exception as e:
print(f"⚠️ Could not change bot username: {e}")
logger.error(f"Could not change bot username: {e}")
# Update nicknames in all servers back to normal
if change_nicknames:
@@ -469,16 +472,16 @@ async def revert_evil_mode_changes(client, change_username=True, change_pfp=True
_, _, saved_color = load_evil_mode_state()
if saved_color:
await set_role_color(client, saved_color)
print(f"🎨 Restored role color to {saved_color}")
logger.debug(f"Restored role color to {saved_color}")
else:
print("⚠️ No saved role color found, skipping color restoration")
logger.warning("No saved role color found, skipping color restoration")
except Exception as e:
print(f"⚠️ Failed to restore role color: {e}")
logger.error(f"Failed to restore role color: {e}")
# Save state to file (this will clear saved_role_color since we're back to normal)
save_evil_mode_state(saved_role_color=None)
print("🎤 Evil Mode disabled!")
logger.info("Evil Mode disabled!")
async def update_all_evil_nicknames(client):
@@ -505,9 +508,9 @@ async def update_evil_server_nickname(client, guild_id: int):
me = guild.get_member(client.user.id)
if me:
await me.edit(nick=nickname)
print(f"😈 Changed nickname to '{nickname}' in server {guild.name}")
logger.debug(f"Changed nickname to '{nickname}' in server {guild.name}")
except Exception as e:
print(f"⚠️ Failed to update evil nickname in guild {guild_id}: {e}")
logger.error(f"Failed to update evil nickname in guild {guild_id}: {e}")
async def revert_all_nicknames(client):
@@ -524,7 +527,7 @@ async def set_evil_profile_picture(client):
evil_pfp_path = "memory/profile_pictures/evil_pfp.png"
if not os.path.exists(evil_pfp_path):
print(f"⚠️ Evil profile picture not found at {evil_pfp_path}")
logger.error(f"Evil profile picture not found at {evil_pfp_path}")
return False
try:
@@ -532,10 +535,10 @@ async def set_evil_profile_picture(client):
avatar_bytes = f.read()
await client.user.edit(avatar=avatar_bytes)
print("😈 Set evil profile picture")
logger.debug("Set evil profile picture")
return True
except Exception as e:
print(f"⚠️ Failed to set evil profile picture: {e}")
logger.error(f"Failed to set evil profile picture: {e}")
return False
@@ -554,12 +557,12 @@ async def restore_normal_profile_picture(client):
avatar_bytes = f.read()
await client.user.edit(avatar=avatar_bytes)
print(f"🎤 Restored normal profile picture from {path}")
logger.debug(f"Restored normal profile picture from {path}")
return True
except Exception as e:
print(f"⚠️ Failed to restore from {path}: {e}")
logger.error(f"Failed to restore from {path}: {e}")
print("⚠️ Could not restore normal profile picture - no backup found")
logger.error("Could not restore normal profile picture - no backup found")
return False
@@ -602,4 +605,4 @@ async def rotate_evil_mood():
globals.EVIL_DM_MOOD_DESCRIPTION = load_evil_mood_description(new_mood)
save_evil_mode_state() # Save state when mood rotates
print(f"😈 Evil mood rotated from {old_mood} to {new_mood}")
logger.info(f"Evil mood rotated from {old_mood} to {new_mood}")