3636 DBHandler ,
3737 LibraryActionModel ,
3838)
39+ from ttmp32gme .log_handler import MemoryLogHandler
3940from ttmp32gme .print_handler import create_pdf , create_print_layout , format_print_button
4041from ttmp32gme .tttool_handler import copy_gme , delete_gme_tiptoi , make_gme
4142
4546)
4647logger = logging .getLogger (__name__ )
4748
49+ # Add memory handler to capture logs for frontend
50+ memory_handler = MemoryLogHandler (max_records = 1000 )
51+ memory_handler .setFormatter (
52+ logging .Formatter ("%(asctime)s - %(name)s - %(levelname)s - %(message)s" )
53+ )
54+ logging .getLogger ().addHandler (memory_handler )
55+
4856# Create Flask app
4957# Configure paths for both development and PyInstaller
5058if getattr (sys , "frozen" , False ):
@@ -166,6 +174,13 @@ def save_config(config_params: Dict[str, Any]) -> tuple[Dict[str, Any], str]:
166174
167175 shutil .rmtree (new_path , ignore_errors = True )
168176
177+ # Handle log level changes
178+ if "log_level" in config_params :
179+ level_str = config_params ["log_level" ]
180+ level = getattr (logging , level_str , logging .WARNING )
181+ logging .getLogger ().setLevel (level )
182+ logger .info (f"Log level changed to { level_str } " )
183+
169184 # Validate DPI and pixel size
170185 if "tt_dpi" in config_params and "tt_pixel-size" in config_params :
171186 dpi = int (config_params ["tt_dpi" ])
@@ -560,6 +575,7 @@ def config_post():
560575 "audio_format" : new_config ["audio_format" ],
561576 "pen_language" : new_config ["pen_language" ],
562577 "library_path" : new_config ["library_path" ],
578+ "log_level" : new_config .get ("log_level" , "WARNING" ),
563579 },
564580 }
565581 )
@@ -577,6 +593,7 @@ def config_post():
577593 "audio_format" : config ["audio_format" ],
578594 "pen_language" : config ["pen_language" ],
579595 "library_path" : config ["library_path" ],
596+ "log_level" : config .get ("log_level" , "WARNING" ),
580597 },
581598 }
582599 )
@@ -600,6 +617,40 @@ def help_page():
600617 )
601618
602619
620+ @app .route ("/logs" )
621+ def get_logs ():
622+ """Get recent log entries."""
623+ num_lines = request .args .get ("lines" , default = 100 , type = int )
624+ logs = memory_handler .get_logs (num_lines )
625+ return jsonify ({"success" : True , "logs" : logs })
626+
627+
628+ @app .route ("/logs/level" , methods = ["POST" ])
629+ def set_log_level ():
630+ """Set the log level dynamically."""
631+ if not request .json :
632+ return jsonify ({"success" : False , "error" : "Invalid request" }), 400
633+
634+ level_str = request .json .get ("level" , "WARNING" )
635+
636+ # Validate log level
637+ valid_levels = ["DEBUG" , "INFO" , "WARNING" , "ERROR" , "CRITICAL" ]
638+ if level_str not in valid_levels :
639+ return jsonify ({"success" : False , "error" : "Invalid log level" }), 400
640+
641+ # Set the log level
642+ level = getattr (logging , level_str )
643+ logging .getLogger ().setLevel (level )
644+ logger .info (f"Log level changed to { level_str } " )
645+
646+ # Save to config
647+ db = get_db ()
648+ db .insert_or_replace_config ("log_level" , level_str )
649+ config ["log_level" ] = level_str
650+
651+ return jsonify ({"success" : True , "level" : level_str })
652+
653+
603654@app .route ("/images/<path:filename>" )
604655def serve_dynamic_image (filename : str ):
605656 """Serve dynamically generated images (OID codes, covers, etc.)."""
@@ -747,6 +798,13 @@ def main():
747798 logger .info ("Update successful." )
748799 config = fetch_config ()
749800
801+ # Apply log level from config if not overridden by command line
802+ if args .verbose == 0 : # Only apply config if -v/-vv not used
803+ log_level_str = config .get ("log_level" , "WARNING" )
804+ log_level = getattr (logging , log_level_str , logging .WARNING )
805+ logging .getLogger ().setLevel (log_level )
806+ logger .info (f"Log level set to { log_level_str } from config" )
807+
750808 # Override config with command-line args
751809 if args .port :
752810 config ["port" ] = args .port
0 commit comments