Add dynamic log level control via Web UI

Introduces API endpoints and UI elements to view and change the application's log level from the web interface. Log level changes are applied immediately and persisted in the database. Updates backend logic, logging utilities, and frontend scripts to support this feature.
pull/97/head
Broque Thomas 2 months ago
parent 25a8117733
commit c1561b15ab

@ -415,8 +415,23 @@ class MainWindow(QMainWindow):
event.accept()
def main():
# Check for saved log level preference in database
try:
from database.music_database import MusicDatabase
db = MusicDatabase()
saved_log_level = db.get_preference('log_level')
if saved_log_level:
log_level = saved_log_level
else:
# Fall back to config file
logging_config = config_manager.get_logging_config()
log_level = logging_config.get('level', 'INFO')
except:
# If database isn't available yet, use config file
logging_config = config_manager.get_logging_config()
log_level = logging_config.get('level', 'INFO')
logging_config = config_manager.get_logging_config()
log_level = logging_config.get('level', 'INFO')
log_file = logging_config.get('path', 'logs/newmusic.log')
setup_logging(level=log_level, log_file=log_file)

@ -90,4 +90,28 @@ def setup_logging(level: str = "INFO", log_file: Optional[str] = None) -> loggin
def get_logger(name: str) -> logging.Logger:
return logging.getLogger(f"newmusic.{name}")
def set_log_level(level: str) -> bool:
"""Dynamically change the log level for all loggers without restart"""
try:
log_level = getattr(logging, level.upper(), logging.INFO)
# Get the root "newmusic" logger
root_logger = logging.getLogger("newmusic")
root_logger.setLevel(log_level)
# Update all handlers
for handler in root_logger.handlers:
handler.setLevel(log_level)
root_logger.info(f"Log level changed to: {level.upper()}")
return True
except Exception as e:
print(f"Error setting log level: {e}")
return False
def get_current_log_level() -> str:
"""Get the current log level"""
root_logger = logging.getLogger("newmusic")
return logging.getLevelName(root_logger.level)
main_logger = get_logger("main")

@ -2086,6 +2086,47 @@ def handle_settings():
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/settings/log-level', methods=['GET', 'POST'])
def handle_log_level():
"""Get or set the application log level"""
from utils.logging_config import set_log_level, get_current_log_level
from database.music_database import MusicDatabase
if request.method == 'POST':
try:
data = request.get_json()
level = data.get('level')
if not level or level.upper() not in ['DEBUG', 'INFO', 'WARNING', 'ERROR']:
return jsonify({"success": False, "error": "Invalid log level. Must be DEBUG, INFO, WARNING, or ERROR"}), 400
# Change the log level dynamically
success = set_log_level(level)
if success:
# Save to database preferences
db = MusicDatabase()
db.set_preference('log_level', level.upper())
logger.info(f"Log level changed to {level.upper()} via Web UI")
add_activity_item("🔍", "Log Level Changed", f"Set to {level.upper()}", "Now")
return jsonify({"success": True, "level": level.upper()})
else:
return jsonify({"success": False, "error": "Failed to set log level"}), 500
except Exception as e:
logger.error(f"Error setting log level: {e}")
return jsonify({"success": False, "error": str(e)}), 500
else: # GET request
try:
current_level = get_current_log_level()
return jsonify({"success": True, "level": current_level})
except Exception as e:
logger.error(f"Error getting log level: {e}")
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/api/test-connection', methods=['POST'])
def test_connection_endpoint():
data = request.get_json()

@ -2411,6 +2411,19 @@
<button class="browse-button" onclick="browsePath('transfer')">Browse</button>
</div>
</div>
<div class="form-group">
<label>Log Level:</label>
<select id="log-level-select" class="form-select" onchange="changeLogLevel()">
<option value="DEBUG">DEBUG (Detailed)</option>
<option value="INFO" selected>INFO (Normal)</option>
<option value="WARNING">WARNING (Minimal)</option>
<option value="ERROR">ERROR (Critical Only)</option>
</select>
<div class="setting-help-text">
Controls the level of detail in application logs. DEBUG shows all details, INFO shows general operations, WARNING and ERROR show only issues.
</div>
</div>
</div>
<!-- Discovery Settings -->

@ -1545,12 +1545,48 @@ async function loadSettingsData() {
console.error('Error loading discovery lookback period:', error);
}
// Load current log level
try {
const logLevelResponse = await fetch('/api/settings/log-level');
const logLevelData = await logLevelResponse.json();
if (logLevelData.success && logLevelData.level) {
document.getElementById('log-level-select').value = logLevelData.level;
}
} catch (error) {
console.error('Error loading log level:', error);
}
} catch (error) {
console.error('Error loading settings:', error);
showToast('Failed to load settings', 'error');
}
}
async function changeLogLevel() {
const selector = document.getElementById('log-level-select');
const level = selector.value;
try {
const response = await fetch('/api/settings/log-level', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ level: level })
});
const data = await response.json();
if (data.success) {
showToast(`Log level changed to ${level}`, 'success');
console.log(`Log level changed to: ${level}`);
} else {
showToast(`Failed to change log level: ${data.error}`, 'error');
}
} catch (error) {
console.error('Error changing log level:', error);
showToast('Failed to change log level', 'error');
}
}
function updateMediaServerFields() {
const serverType = document.getElementById('media-server-type').value;
const urlInput = document.getElementById('media-server-url');

Loading…
Cancel
Save