py.lib
43:6f8bea109183
Go to Latest
py.lib/log/log_confile.py
. Наведение порядка в коде логирования
2 """ Логирование на консоль
4 Метки в журнале о уровне сообщения:
13 from time import monotonic, ctime
14 from datetime import timedelta, date
15 from traceback import extract_tb, extract_stack
16 from sys import exc_info, stderr, stdout
17 from typing import Optional, TextIO, Any
18 from os.path import join as p_join, abspath
19 from threading import RLock
22 TIME_TO_FLUSH: int = 120 # Время по умолчанию с момента прошлого сброса лога,
23 # после которого, выполняется принудительный сброс буферов
27 def __init__(self, name: Optional[str] = None):
32 self.prefix = f'{name} :: '
34 self.tsAll = monotonic()
38 return monotonic() - self.ts
46 return self.prefix + '%s(%.4f)' % (timedelta(seconds=(ts - self.tsAll)), ts - self.ts)
48 def __call__(self, msg):
49 _buf = f'{self} | {msg}'
54 class NullLog(object):
55 def __init__(self, prefix: str = 'main'):
59 def _write(mark: str, msg: Any):
60 pass # cat > /dev/null
62 def __call__(self, msg):
78 def get_timing(name: Optional[str] = None):
81 def sub_log(self, name: str):
82 return self.__class__(f'{self.prefix}/{name}')
84 def excpt(self, msg, e_class=None, e_obj=None, e_tb=None, stack_skip=0):
86 e_class, e_obj, e_tb = exc_info()
88 tb_data_tb = list(extract_tb(e_tb))[::-1]
89 tb_data_stack = list(extract_stack())[::-1][(2 + stack_skip):]
91 self.err('--- EXCEPTION ---')
92 self.err(f' {e_class.__name__} ({e_obj})')
93 self.err('--- TRACEBACK ---')
94 for _tb_file, _tb_line, _tb_func, _tb_text in tb_data_tb:
95 self.err(f'File: {_tb_file}, line {_tb_line} in {_tb_func}')
96 self.err(f' {_tb_text}')
98 self.err('>>> Exception Handler <<<')
99 for _tb_file, _tb_line, _tb_func, _tb_text in tb_data_stack:
100 self.err(f'File: {_tb_file}, line {_tb_line} in {_tb_func}')
101 self.err(f' {_tb_text}')
103 self.err('--- END EXCEPTION ---')
106 class FileLog(NullLog):
108 def _open_file(file_name: str):
109 return open(file_name, 'a', encoding='utf-8')
111 def __init__(self, prefix: str = 'main',
112 file_name: Optional[str] = None,
113 file_obj: Optional[TextIO] = None,
114 time_to_flush: int = TIME_TO_FLUSH
117 super().__init__(prefix=prefix)
118 self.fd: Optional[TextIO] = None
120 self.flush_time = monotonic()
121 self.time_to_flush = time_to_flush # Время с момента прошлого сброса лога,
122 # после которого, выполняется принудительный сброс буферов
124 if file_name is not None:
125 self.fd = self._open_file(file_name)
131 raise ValueError(f'Не задан файл для записи журналов')
133 def flush(self, time_mark: float = None):
134 if time_mark is None:
135 time_mark = monotonic()
137 self.flush_time = time_mark
146 if self.fd is not None:
151 def flush_event(self):
153 if t - self.flush_time >= self.time_to_flush:
156 def _write(self, mark: str, msg: Any):
158 raise ValueError('Попытка использовать закрытый файл журнала')
161 for l in str(msg).splitlines():
162 self.fd.write(f'{t} | {mark} {self.prefix} | {l}')
166 def sub_log(self, name: str):
168 raise ValueError('Попытка использовать закрытый файл журнала')
170 return self.__class__(f'{self.prefix}/{name}', file_obj=self.fd, time_to_flush=self.time_to_flush)
173 class StderrLog(FileLog):
174 def __init__(self, prefix: str = 'main'):
175 super().__init__(prefix, file_obj=stderr)
177 def flush_event(self):
178 pass # нет необходимости сбрасывать буферы в консоли
181 class StdoutLog(FileLog):
182 def __init__(self, prefix: str = 'main'):
183 super().__init__(prefix, file_obj=stdout)
185 def flush_event(self):
186 pass # нет необходимости сбрасывать буферы в консоли
189 class LogrotateFile(FileLog):
190 def __init__(self, directory: str = '.', prefix: str = 'main', time_to_flush: int = TIME_TO_FLUSH):
192 directory = abspath(directory)
193 file_name = p_join(directory, f'{prefix}-{d}.log')
195 super().__init__(prefix, file_name=file_name, time_to_flush=time_to_flush)
196 self.logrotate_base = p_join(directory, f'{prefix}')
197 self.logrotate_date = d
199 def flush(self, time_mark: float = None, no_rotate: bool = False):
202 if self.logrotate_date != d:
203 self.logrotate_rotate()
206 super().flush(time_mark=time_mark)
208 def logrotate_rotate(self):
210 file_name = f'{self.logrotate_base}-{d}.log'
212 self.fd = self._open_file(file_name)
213 self.logrotate_date = d
214 self.flush(no_rotate=True)
217 class ThreadSafeFileLog(FileLog):
218 def __int__(self, *a, **kwa):
219 super().__init__(*a, **kwa)
220 self.thread_safe_lock = RLock()
222 def _write(self, mark: str, msg: Any):
223 with self.thread_safe_lock:
224 super()._write(mark, msg)
226 def flush(self, time_mark: float = None):
227 with self.thread_safe_lock:
228 super().flush(time_mark)
231 class ThreadSafeLogrotateFile(LogrotateFile):
232 def __init__(self, directory: str = '.', prefix: str = 'main', time_to_flush: int = TIME_TO_FLUSH):
233 super().__init__(directory=directory, prefix=prefix, time_to_flush=time_to_flush)
234 self.thread_safe_lock = RLock()
236 def _write(self, mark: str, msg: Any):
237 with self.thread_safe_lock:
238 super()._write(mark, msg)
240 def flush(self, time_mark: float = None, no_rotate: bool = False):
241 with self.thread_safe_lock:
242 super().flush(time_mark=time_mark, no_rotate=no_rotate)
244 def logrotate_rotate(self):
245 with self.thread_safe_lock:
246 super().logrotate_rotate()