Files
miku-discord/bot/static/system.html
koko210Serve 32c2a7b930 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.
2026-01-10 20:46:19 +02:00

408 lines
14 KiB
HTML
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.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🎛️ System Settings - Logging Configuration</title>
<style>
body {
margin: 0;
font-family: monospace;
background-color: #121212;
color: #fff;
}
.container {
padding: 2rem;
max-width: 1600px;
margin: 0 auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 2px solid #333;
}
h1 {
color: #61dafb;
margin: 0;
font-size: 1.8rem;
}
h2 {
color: #61dafb;
font-size: 1.3rem;
margin: 0 0 1rem 0;
}
.header-actions {
display: flex;
gap: 0.5rem;
}
button, select {
padding: 0.4rem 0.8rem;
background: #333;
color: #fff;
border: 1px solid #555;
font-family: monospace;
cursor: pointer;
font-size: 0.9rem;
}
button:hover, select:hover {
background: #444;
border-color: #666;
}
.btn-primary {
background: #61dafb;
color: #000;
border-color: #61dafb;
font-weight: bold;
}
.btn-primary:hover {
background: #4fa8c5;
}
.btn-secondary {
background: #555;
border-color: #666;
}
.btn-danger {
background: #d32f2f;
border-color: #d32f2f;
}
.btn-danger:hover {
background: #b71c1c;
}
.content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.card {
background: #1e1e1e;
border: 1px solid #333;
border-radius: 8px;
padding: 1.5rem;
}
.components-table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
}
.components-table th {
background: #2a2a2a;
color: #61dafb;
padding: 0.8rem;
text-align: left;
font-weight: bold;
border-bottom: 2px solid #444;
}
.components-table td {
padding: 0.8rem;
border-bottom: 1px solid #2a2a2a;
vertical-align: top;
}
.components-table tr:hover {
background: #252525;
}
.component-description {
font-size: 0.8rem;
color: #999;
margin-top: 0.3rem;
}
.toggle {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.toggle input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #555;
transition: 0.3s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: 0.3s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #61dafb;
}
input:checked + .slider:before {
transform: translateX(26px);
}
.level-checkboxes {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.level-checkbox {
display: flex;
align-items: center;
gap: 0.3rem;
}
.level-checkbox input[type="checkbox"] {
width: auto;
margin: 0;
cursor: pointer;
}
.level-checkbox label {
font-size: 0.85rem;
cursor: pointer;
}
.status-indicator {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 0.3rem;
}
.status-active {
background-color: #4CAF50;
}
.status-inactive {
background-color: #555;
}
.api-filters {
background: #2a2a2a;
border: 1px solid #444;
padding: 1rem;
margin-top: 1rem;
border-radius: 8px;
}
.api-filters h3 {
color: #61dafb;
font-size: 1rem;
margin-bottom: 0.8rem;
}
.filter-row {
margin-bottom: 0.8rem;
}
.filter-row label {
display: block;
font-weight: bold;
margin-bottom: 0.3rem;
color: #ccc;
}
.setting-row {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 0.8rem;
}
.setting-row label {
font-weight: bold;
color: #ccc;
}
input[type="text"], input[type="number"] {
width: 100%;
padding: 0.5rem;
background: #333;
color: #fff;
border: 1px solid #555;
font-family: monospace;
}
.log-preview {
background: #000;
color: #0f0;
padding: 1rem;
border-radius: 8px;
font-family: monospace;
font-size: 0.85rem;
max-height: 600px;
overflow-y: auto;
white-space: pre-wrap;
word-wrap: break-word;
border: 1px solid #333;
}
.log-preview-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.log-line {
margin-bottom: 3px;
line-height: 1.4;
}
.notification {
position: fixed;
bottom: 20px;
right: 20px;
background-color: #222;
color: #fff;
padding: 1rem;
border: 1px solid #555;
border-radius: 8px;
opacity: 0.95;
z-index: 1000;
font-size: 0.9rem;
animation: slideIn 0.3s ease-out;
}
.notification-success {
border-color: #4CAF50;
background: #1b4d1b;
}
.notification-error {
border-color: #d32f2f;
background: #4d1b1b;
}
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.loading {
text-align: center;
padding: 2rem;
color: #999;
}
@media (max-width: 1200px) {
.content {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎛️ System Settings - Logging Configuration</h1>
<div class="header-actions">
<button class="btn-secondary" onclick="window.location.href='/'">← Back to Dashboard</button>
<button class="btn-primary" onclick="saveAllSettings()">💾 Save All</button>
<button class="btn-danger" onclick="resetToDefaults()">🔄 Reset to Defaults</button>
</div>
</div>
<div class="content">
<div class="card">
<h2>📊 Logging Components</h2>
<p style="color: #999; margin-bottom: 1rem; font-size: 0.9rem;">
Enable or disable specific log levels for each component. You can toggle any combination of levels.
</p>
<div class="api-filters" style="margin-bottom: 1.5rem;">
<h3>🌍 Global Level Controls</h3>
<p style="color: #999; font-size: 0.85rem; margin-bottom: 0.8rem;">
Quickly enable/disable a log level across all components
</p>
<div class="level-checkboxes">
<div class="level-checkbox">
<input type="checkbox" id="global_DEBUG" checked onchange="updateGlobalLevel('DEBUG', this.checked)">
<label for="global_DEBUG">🔍 DEBUG</label>
</div>
<div class="level-checkbox">
<input type="checkbox" id="global_INFO" checked onchange="updateGlobalLevel('INFO', this.checked)">
<label for="global_INFO"> INFO</label>
</div>
<div class="level-checkbox">
<input type="checkbox" id="global_WARNING" checked onchange="updateGlobalLevel('WARNING', this.checked)">
<label for="global_WARNING">⚠️ WARNING</label>
</div>
<div class="level-checkbox">
<input type="checkbox" id="global_ERROR" checked onchange="updateGlobalLevel('ERROR', this.checked)">
<label for="global_ERROR">❌ ERROR</label>
</div>
<div class="level-checkbox">
<input type="checkbox" id="global_CRITICAL" checked onchange="updateGlobalLevel('CRITICAL', this.checked)">
<label for="global_CRITICAL">🔥 CRITICAL</label>
</div>
<div class="level-checkbox">
<input type="checkbox" id="global_API" checked onchange="updateGlobalLevel('API', this.checked)">
<label for="global_API">🌐 API</label>
</div>
</div>
</div>
<div class="api-filters" style="margin-bottom: 1.5rem;">
<h3>🕐 Timestamp Format</h3>
<p style="color: #999; font-size: 0.85rem; margin-bottom: 0.8rem;">
Control how timestamps appear in logs
</p>
<div class="setting-row">
<label>Format:</label>
<select id="timestampFormat" onchange="updateTimestampFormat(this.value)">
<option value="datetime">Date + Time (2026-01-10 20:30:45)</option>
<option value="time">Time Only (20:30:45)</option>
<option value="date">Date Only (2026-01-10)</option>
<option value="off">No Timestamp</option>
</select>
</div>
</div>
<table class="components-table">
<thead>
<tr>
<th>Component</th>
<th>Enabled</th>
<th>Log Levels</th>
<th>Status</th>
</tr>
</thead>
<tbody id="componentsTable">
<tr><td colspan="4" class="loading">Loading components...</td></tr>
</tbody>
</table>
<div id="apiFilters" class="api-filters" style="display: none;">
<h3>🌐 API Request Filters</h3>
<div class="filter-row">
<label>Exclude Paths (comma-separated):</label>
<input type="text" id="excludePaths" placeholder="/health, /static/*">
</div>
<div class="filter-row">
<label>Exclude Status Codes (comma-separated):</label>
<input type="text" id="excludeStatus" placeholder="200, 304">
</div>
<div class="setting-row">
<label>Log Slow Requests (>1000ms):</label>
<label class="toggle">
<input type="checkbox" id="includeSlowRequests" checked>
<span class="slider"></span>
</label>
</div>
<div class="filter-row">
<label>Slow Request Threshold (ms):</label>
<input type="number" id="slowThreshold" value="1000" min="100" step="100">
</div>
<button class="btn-primary" onclick="saveApiFilters()" style="margin-top: 0.5rem;">Save API Filters</button>
</div>
</div>
<div class="card">
<h2>📜 Live Log Preview</h2>
<div class="log-preview-header">
<div>
<label>Component: </label>
<select id="previewComponent" onchange="loadLogPreview()"><option value="bot">Bot</option></select>
</div>
<button class="btn-secondary" onclick="loadLogPreview()">🔄 Refresh</button>
</div>
<div class="log-preview" id="logPreview">
<div class="loading">Select a component to view logs...</div>
</div>
</div>
</div>
</div>
<script src="/static/system-logic.js"></script>
</body>
</html>