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.
This commit is contained in:
parent
25a8117733
commit
c1561b15ab
5 changed files with 130 additions and 1 deletions
17
main.py
17
main.py
|
|
@ -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…
Reference in a new issue