# log_manager.py import json import logging import os import time import threading from collections import deque from datetime import datetime from typing import Dict, List, Optional, Union class ProgramFilter(logging.Filter): """自定义日志过滤器,为日志记录添加程序名称属性""" def filter(self, record: logging.LogRecord) -> bool: if not hasattr(record, 'program'): record.program = "UNKNOWN" return True class UnifiedLogger: """统一日志管理器(线程安全单例),支持多程序日志收集""" _instance: Optional['UnifiedLogger'] = None _lock = threading.Lock() def __new__(cls) -> 'UnifiedLogger': with cls._lock: if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self) -> None: # 避免重复初始化 if hasattr(self, '_initialized') and self._initialized: return # 每视频状态使用线程本地存储:批处理多线程并发时,每个视频在各自线程内 # 独立记录日志/计时/统计,互不串扰。(嵌套子线程如OCR并发worker不应直接 # 调用本logger的状态方法——它们会落到worker线程的独立状态而丢失。) self._local = threading.local() # 是否打印详细日志到控制台。默认 False:控制台只显示"视频→步骤"里程碑, # 详细日志仍完整存进每视频 JSON(log/