mlog-util 0.1.6__py3-none-any.whl → 0.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mlog-util might be problematic. Click here for more details.
- mlog_util/__init__.py +2 -0
- mlog_util/handlers.py +106 -30
- mlog_util/log_manager.py +4 -5
- {mlog_util-0.1.6.dist-info → mlog_util-0.1.7.dist-info}/METADATA +1 -1
- mlog_util-0.1.7.dist-info/RECORD +7 -0
- mlog_util-0.1.6.dist-info/RECORD +0 -7
- {mlog_util-0.1.6.dist-info → mlog_util-0.1.7.dist-info}/WHEEL +0 -0
- {mlog_util-0.1.6.dist-info → mlog_util-0.1.7.dist-info}/top_level.txt +0 -0
mlog_util/__init__.py
CHANGED
mlog_util/handlers.py
CHANGED
|
@@ -5,6 +5,8 @@ import glob
|
|
|
5
5
|
import errno
|
|
6
6
|
import logging
|
|
7
7
|
import portalocker
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import re
|
|
8
10
|
from abc import ABC, abstractmethod
|
|
9
11
|
|
|
10
12
|
|
|
@@ -234,20 +236,34 @@ class MultiProcessSafeSizeRotatingHandler(MultiProcessSafeRotatingHandlerBase):
|
|
|
234
236
|
|
|
235
237
|
def _do_rollover_impl(self):
|
|
236
238
|
# 轮转备份文件
|
|
239
|
+
log_path = Path(self.filename)
|
|
240
|
+
|
|
241
|
+
# 1. 轮转已存在的备份文件 (例如 .3 -> .4, .2 -> .3, .1 -> .2)
|
|
242
|
+
# 倒序处理,避免覆盖
|
|
237
243
|
for i in range(self.backupCount - 1, 0, -1):
|
|
238
|
-
sfn = f
|
|
239
|
-
dfn = f
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
244
|
+
sfn = log_path.with_suffix(f'.log.{i}')
|
|
245
|
+
dfn = log_path.with_suffix(f'.log.{i+1}')
|
|
246
|
+
|
|
247
|
+
if sfn.exists():
|
|
248
|
+
try:
|
|
249
|
+
# 直接尝试重命名,如果目标文件已存在会失败
|
|
250
|
+
os.rename(sfn, dfn)
|
|
251
|
+
except FileExistsError:
|
|
252
|
+
# 如果失败,说明目标文件已存在,先删除再重命名
|
|
253
|
+
# 这比 "先检查再操作" 更能避免竞态条件
|
|
254
|
+
dfn.unlink() # pathlib 的删除方法
|
|
255
|
+
os.rename(sfn, dfn)
|
|
256
|
+
|
|
257
|
+
# 2. 将当前日志文件重命名为第一个备份 .1
|
|
258
|
+
if log_path.exists():
|
|
259
|
+
dfn = log_path.with_suffix('.log.1')
|
|
260
|
+
try:
|
|
261
|
+
os.rename(log_path, dfn)
|
|
262
|
+
except FileExistsError:
|
|
263
|
+
dfn.unlink()
|
|
264
|
+
os.rename(log_path, dfn)
|
|
265
|
+
|
|
266
|
+
# 重新创建空的日志文件 占位
|
|
251
267
|
try:
|
|
252
268
|
with open(self.filename, 'w', encoding='utf-8') as f:
|
|
253
269
|
pass
|
|
@@ -268,7 +284,6 @@ class MultiProcessSafeTimeRotatingHandler(MultiProcessSafeRotatingHandlerBase):
|
|
|
268
284
|
super().__init__(filename, backupCount)
|
|
269
285
|
self.when = when.upper()
|
|
270
286
|
self.interval = max(1, int(interval)) # 至少为 1
|
|
271
|
-
self.last_rollover = int(time.time())
|
|
272
287
|
|
|
273
288
|
# 支持的单位映射
|
|
274
289
|
self.when_to_seconds = {
|
|
@@ -278,29 +293,90 @@ class MultiProcessSafeTimeRotatingHandler(MultiProcessSafeRotatingHandlerBase):
|
|
|
278
293
|
'D': 86400, # 天
|
|
279
294
|
}
|
|
280
295
|
|
|
281
|
-
def _should_rollover(self, record) -> bool:
|
|
282
|
-
now = int(time.time())
|
|
283
|
-
|
|
284
296
|
if self.when not in self.when_to_seconds:
|
|
285
|
-
|
|
297
|
+
raise ValueError(f"Invalid rollover interval specified: {self.when}")
|
|
298
|
+
|
|
299
|
+
# 在初始化时就计算好下一个轮转时间点
|
|
300
|
+
self.rolloverAt = self._compute_next_rollover_time(int(time.time()))
|
|
286
301
|
|
|
287
|
-
|
|
288
|
-
|
|
302
|
+
def _compute_next_rollover_time(self, current_time):
|
|
303
|
+
"""
|
|
304
|
+
计算下一个轮转的时间点(时间戳)。
|
|
305
|
+
这个方法的核心是使用本地时间来计算,确保轮转发生在正确的本地时间。
|
|
306
|
+
"""
|
|
307
|
+
t = time.localtime(current_time)
|
|
308
|
+
|
|
309
|
+
# 根据轮转单位,找到当前周期的起始时间点
|
|
310
|
+
if self.when == 'S':
|
|
311
|
+
current_period_start = time.mktime((t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, t.tm_wday, t.tm_yday, t.tm_isdst))
|
|
312
|
+
elif self.when == 'M':
|
|
313
|
+
current_period_start = time.mktime((t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, 0, t.tm_wday, t.tm_yday, t.tm_isdst))
|
|
314
|
+
elif self.when == 'H':
|
|
315
|
+
current_period_start = time.mktime((t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, 0, 0, t.tm_wday, t.tm_yday, t.tm_isdst))
|
|
316
|
+
else: # 'D' or any other
|
|
317
|
+
current_period_start = time.mktime((t.tm_year, t.tm_mon, t.tm_mday, 0, 0, 0, t.tm_wday, t.tm_yday, t.tm_isdst))
|
|
289
318
|
|
|
290
|
-
#
|
|
291
|
-
|
|
292
|
-
last_cycle = self.last_rollover // total_interval_seconds
|
|
319
|
+
# 下一个轮转时间点 = 当前周期起始 + N个周期
|
|
320
|
+
next_rollover_time = current_period_start + (self.interval * self.when_to_seconds[self.when])
|
|
293
321
|
|
|
294
|
-
|
|
322
|
+
# 如果计算出的时间点已经过了(例如,程序刚好在边界点启动),则再推后一个周期
|
|
323
|
+
if next_rollover_time <= current_time:
|
|
324
|
+
next_rollover_time += self.interval * self.when_to_seconds[self.when]
|
|
325
|
+
|
|
326
|
+
return next_rollover_time
|
|
327
|
+
|
|
328
|
+
def _should_rollover(self, record) -> bool:
|
|
329
|
+
"""
|
|
330
|
+
修正3: 判断逻辑改为与下一个轮转时间点比较
|
|
331
|
+
"""
|
|
332
|
+
# 获取日志记录产生的时间戳
|
|
333
|
+
record_time = int(record.created)
|
|
334
|
+
|
|
335
|
+
# 如果记录的时间已经超过了我们预定的下一个轮转时间点,则触发轮转
|
|
336
|
+
return record_time >= self.rolloverAt
|
|
295
337
|
|
|
296
338
|
def _do_rollover_impl(self):
|
|
297
|
-
#
|
|
339
|
+
# 1. 执行轮转:将当前日志文件重命名为带时间戳的文件
|
|
298
340
|
date_str = time.strftime(self._get_rollover_format())
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
341
|
+
log_path = Path(self.filename)
|
|
342
|
+
dfn = log_path.with_name(f"{log_path.name}.{date_str}")
|
|
343
|
+
|
|
344
|
+
if log_path.exists():
|
|
345
|
+
try:
|
|
346
|
+
log_path.rename(dfn)
|
|
347
|
+
except FileExistsError:
|
|
348
|
+
dfn.unlink()
|
|
349
|
+
log_path.rename(dfn)
|
|
350
|
+
|
|
351
|
+
# 2. 重新创建空的日志文件(使用你指定的方式)
|
|
352
|
+
try:
|
|
353
|
+
with open(self.filename, 'w', encoding='utf-8') as f:
|
|
354
|
+
pass
|
|
355
|
+
except Exception as e:
|
|
356
|
+
print(f"Failed to recreate log file {self.filename}: {e}")
|
|
357
|
+
|
|
358
|
+
# 3. 更新下一个轮转的时间点(核心逻辑)
|
|
359
|
+
current_time = int(time.time())
|
|
360
|
+
self.rolloverAt = self._compute_next_rollover_time(current_time)
|
|
361
|
+
|
|
362
|
+
# --- 清理旧备份的逻辑 ---
|
|
363
|
+
|
|
364
|
+
# 4. 查找所有匹配的备份文件
|
|
365
|
+
backup_pattern = log_path.with_name(f"{log_path.name}.*")
|
|
366
|
+
backup_files = glob.glob(str(backup_pattern))
|
|
367
|
+
|
|
368
|
+
# 5. 如果备份文件数量超过限制,则进行清理
|
|
369
|
+
if len(backup_files) > self.backupCount:
|
|
370
|
+
# 6. 按文件名(即时间戳)排序,找出最旧的文件
|
|
371
|
+
backup_files.sort()
|
|
372
|
+
|
|
373
|
+
# 7. 计算需要删除的文件数量并删除
|
|
374
|
+
files_to_delete = backup_files[:-self.backupCount]
|
|
375
|
+
for file_to_delete in files_to_delete:
|
|
376
|
+
try:
|
|
377
|
+
Path(file_to_delete).unlink()
|
|
378
|
+
except OSError as e:
|
|
379
|
+
print(f"Error deleting old log file {file_to_delete}: {e}")
|
|
304
380
|
|
|
305
381
|
def _get_rollover_format(self):
|
|
306
382
|
"""
|
mlog_util/log_manager.py
CHANGED
|
@@ -19,7 +19,7 @@ class LogManager:
|
|
|
19
19
|
log_file: Optional[str] = None,
|
|
20
20
|
add_console: bool = True,
|
|
21
21
|
level: int = logging.INFO,
|
|
22
|
-
custom_handlers: Optional[
|
|
22
|
+
custom_handlers: Optional[logging.Handler] = None,
|
|
23
23
|
) -> logging.Logger:
|
|
24
24
|
"""
|
|
25
25
|
获取或创建一个配置好的 logger。
|
|
@@ -69,10 +69,9 @@ class LogManager:
|
|
|
69
69
|
"%(asctime)s | %(name)s | %(levelname)-8s | %(message)s",
|
|
70
70
|
datefmt="%Y-%m-%d %H:%M:%S"
|
|
71
71
|
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
logger.addHandler(handler)
|
|
72
|
+
if custom_handlers not in logger.handlers:
|
|
73
|
+
custom_handlers.setFormatter(custom_formatter)
|
|
74
|
+
logger.addHandler(custom_handlers)
|
|
76
75
|
|
|
77
76
|
return logger
|
|
78
77
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
mlog_util/__init__.py,sha256=jQ2B37jm5GLPH4crurCo32GIcM8iLbJegSRiDOYHTNU,167
|
|
2
|
+
mlog_util/handlers.py,sha256=q5ne-in0jolqufrqerurSri1ynKcwLfUE2alPrYipWo,14303
|
|
3
|
+
mlog_util/log_manager.py,sha256=XhytVOHgL4HmneYdAU7r67avtDtzbirDoHi6BtMEGiw,4046
|
|
4
|
+
mlog_util-0.1.7.dist-info/METADATA,sha256=0kz4Y-igGgO6CoE-rTlNsSMBHngFvJGcztEBtcDSGbo,207
|
|
5
|
+
mlog_util-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
+
mlog_util-0.1.7.dist-info/top_level.txt,sha256=-liqOVoloTs4GLglhy_pzBBpK-ltsV3IZpg0OQsoD_4,10
|
|
7
|
+
mlog_util-0.1.7.dist-info/RECORD,,
|
mlog_util-0.1.6.dist-info/RECORD
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
mlog_util/__init__.py,sha256=ZzcwRSCJDIHq_enQIRpvl8VRZz7izkL4VsnupRsR3AY,144
|
|
2
|
-
mlog_util/handlers.py,sha256=0EXqAlzxVlcfXEO478EdYJSDdmlZZp5lRame4hEGnmA,10638
|
|
3
|
-
mlog_util/log_manager.py,sha256=xD1_RmN3O9u1nxqhMqKxAhY00x0pBj_jpvxgL4cFZBE,4088
|
|
4
|
-
mlog_util-0.1.6.dist-info/METADATA,sha256=QLY0lX7AHZCfz7VhKFeK9UdurOFg5p5e9mH-ZZvvVTM,207
|
|
5
|
-
mlog_util-0.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
-
mlog_util-0.1.6.dist-info/top_level.txt,sha256=-liqOVoloTs4GLglhy_pzBBpK-ltsV3IZpg0OQsoD_4,10
|
|
7
|
-
mlog_util-0.1.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|