99import re
1010import sys
1111from enum import StrEnum
12+ from logging .handlers import RotatingFileHandler
1213from typing import TYPE_CHECKING , Final , override
1314
14- from PySide6 .QtCore import QtMsgType
15+ from PySide6 .QtCore import QtMsgType , qInstallMessageHandler
1516
1617if TYPE_CHECKING :
1718 from collections .abc import Callable
19+ from pathlib import Path
1820
1921 from PySide6 .QtCore import QMessageLogContext
2022
2123MPV_LEVEL : Final [int ] = 25
2224logging .addLevelName (MPV_LEVEL , "MPV" )
2325
26+ FILE_LOG_MAX_BYTES : Final [int ] = 1_000_000
27+ FILE_LOG_BACKUP_COUNT : Final [int ] = 5
28+
29+ _URL_SCHEME_PATTERN : Final = re .compile (r"^\w+:" )
30+
2431
2532class AnsiColor (StrEnum ):
2633 RESET = "\033 [0m"
@@ -49,9 +56,9 @@ def use_color() -> bool:
4956
5057
5158class MpvqcFormatter (logging .Formatter ):
52- def __init__ (self ) -> None :
59+ def __init__ (self , * , colored : bool ) -> None :
5360 super ().__init__ ()
54- self ._use_color = use_color ()
61+ self ._use_color = colored
5562
5663 @override
5764 def format (self , record : logging .LogRecord ) -> str :
@@ -84,6 +91,12 @@ def format(self, record: logging.LogRecord) -> str:
8491
8592
8693def setup_mpvqc_logging () -> None :
94+ _setup_console_logging ()
95+ _setup_file_logging ()
96+ qInstallMessageHandler (_qt_log_handler ())
97+
98+
99+ def _setup_console_logging () -> None :
87100 is_debug = os .getenv ("MPVQC_DEBUG" )
88101
89102 root_logger = logging .getLogger ()
@@ -93,7 +106,7 @@ def setup_mpvqc_logging() -> None:
93106 root_logger .removeHandler (handler )
94107
95108 console_handler = logging .StreamHandler (sys .stdout )
96- console_handler .setFormatter (MpvqcFormatter ())
109+ console_handler .setFormatter (MpvqcFormatter (colored = use_color () ))
97110 root_logger .addHandler (console_handler )
98111
99112 if is_debug :
@@ -102,15 +115,29 @@ def setup_mpvqc_logging() -> None:
102115 logging .getLogger (name ).setLevel (logging .WARNING )
103116
104117
105- _URL_SCHEME_PATTERN : Final = re .compile (r"^\w+:" )
118+ def _setup_file_logging () -> None :
119+ try :
120+ from mpvqc .services .application_paths import ApplicationPathsService
106121
122+ paths = ApplicationPathsService ()
123+ paths .dir_logs .mkdir (parents = True , exist_ok = True )
124+ attach_file_logging (paths .file_log )
125+ except Exception :
126+ logging .getLogger (__name__ ).exception ("Could not set up file logging" )
107127
108- def logger_name_from (path : str ) -> str :
109- path = _URL_SCHEME_PATTERN .sub ("" , path ).lstrip ("/" ).removeprefix ("qt/qml/" )
110- return path .replace ("/" , "." ).removesuffix (".qml" )
128+
129+ def attach_file_logging (log_file : Path ) -> None :
130+ handler = RotatingFileHandler (
131+ log_file ,
132+ maxBytes = FILE_LOG_MAX_BYTES ,
133+ backupCount = FILE_LOG_BACKUP_COUNT ,
134+ encoding = "utf-8" ,
135+ )
136+ handler .setFormatter (MpvqcFormatter (colored = False ))
137+ logging .getLogger ().addHandler (handler )
111138
112139
113- def qt_log_handler () -> Callable [[QtMsgType , QMessageLogContext , str ], None ]:
140+ def _qt_log_handler () -> Callable [[QtMsgType , QMessageLogContext , str ], None ]:
114141 levels : Final [dict [QtMsgType , int ]] = {
115142 QtMsgType .QtDebugMsg : logging .DEBUG ,
116143 QtMsgType .QtInfoMsg : logging .INFO ,
@@ -144,3 +171,8 @@ def handler(message_type: QtMsgType, context: QMessageLogContext, message: str)
144171 qml_logger .handle (record )
145172
146173 return handler
174+
175+
176+ def logger_name_from (path : str ) -> str :
177+ path = _URL_SCHEME_PATTERN .sub ("" , path ).lstrip ("/" ).removeprefix ("qt/qml/" )
178+ return path .replace ("/" , "." ).removesuffix (".qml" )
0 commit comments