modify:日志命名问题
This commit is contained in:
@@ -43,6 +43,10 @@ UMask=0027
|
|||||||
# 每机可选覆盖(如 APP_WORKERS),文件不存在时忽略(- 前缀)
|
# 每机可选覆盖(如 APP_WORKERS),文件不存在时忽略(- 前缀)
|
||||||
EnvironmentFile=-/etc/chongming.env
|
EnvironmentFile=-/etc/chongming.env
|
||||||
Environment=PYTHONUNBUFFERED=1
|
Environment=PYTHONUNBUFFERED=1
|
||||||
|
# ffmpeg 位于非标准路径(AI Box 的 sophon-ffmpeg):覆盖默认 PATH 才能被
|
||||||
|
# shutil.which / create_subprocess_exec 找到(RTSP 拉流依赖);若 ffmpeg 还依赖
|
||||||
|
# 私有动态库,再补一条 Environment=LD_LIBRARY_PATH=/opt/sophon/sophon-ffmpeg-latest/lib
|
||||||
|
Environment=PATH=/opt/sophon/sophon-ffmpeg-latest/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
|
||||||
# 优雅停机:SIGTERM 由 gunicorn 排空 worker,等在途请求/告警上传完成
|
# 优雅停机:SIGTERM 由 gunicorn 排空 worker,等在途请求/告警上传完成
|
||||||
KillSignal=SIGTERM
|
KillSignal=SIGTERM
|
||||||
|
|||||||
@@ -67,6 +67,80 @@ class ColoredFormatter(logging.Formatter):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class DailyRotatingFileHandler(RotatingFileHandler):
|
||||||
|
"""
|
||||||
|
按自然日切分 + 大小轮转的文件处理器
|
||||||
|
|
||||||
|
日志文件名形如 {log_name}-2026-08-19.log:进程启动时按当天日期命名,
|
||||||
|
跨天后写入时自动切换到新日期文件,避免长时间运行的进程把跨天日志
|
||||||
|
全部堆积到启动当天的文件里。同时保留 RotatingFileHandler 的大小轮转
|
||||||
|
(单文件超限追加 .1/.2 后缀)。
|
||||||
|
|
||||||
|
历史文件(跨天文件与大小备份)按修改时间保留最近 backup_count 个,
|
||||||
|
超出部分在切换日期的瞬间清理。
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
log_dir: str,
|
||||||
|
log_name: str,
|
||||||
|
max_bytes: int,
|
||||||
|
backup_count: int,
|
||||||
|
encoding: str = "utf-8",
|
||||||
|
):
|
||||||
|
self.log_dir = Path(log_dir)
|
||||||
|
self.log_name = log_name
|
||||||
|
self._current_date = datetime.date.today()
|
||||||
|
super().__init__(
|
||||||
|
str(self.log_dir / f"{log_name}-{self._current_date}.log"),
|
||||||
|
encoding=encoding,
|
||||||
|
maxBytes=max_bytes,
|
||||||
|
backupCount=backup_count,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _build_filename(self, day: datetime.date) -> str:
|
||||||
|
return str(self.log_dir / f"{self.log_name}-{day}.log")
|
||||||
|
|
||||||
|
def _cleanup_old_files(self) -> None:
|
||||||
|
"""保留最近 backup_count 个日志文件,删除更早的历史文件"""
|
||||||
|
# 按 (日期, 轮转序号) 排序,不依赖 mtime(同日备份可能同时落盘):
|
||||||
|
# 日期越大越新;同日内主文件(.log)最新,.N 备份按 N 递减更旧
|
||||||
|
pattern = re.compile(
|
||||||
|
rf"^{re.escape(self.log_name)}-(\d{{4}}-\d{{2}}-\d{{2}})\.log(?:\.(\d+))?$"
|
||||||
|
)
|
||||||
|
|
||||||
|
def sort_key(path: Path):
|
||||||
|
match = pattern.match(path.name)
|
||||||
|
if not match:
|
||||||
|
return (datetime.date.min, 0)
|
||||||
|
return (
|
||||||
|
datetime.date.fromisoformat(match.group(1)),
|
||||||
|
-int(match.group(2) or 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
files = sorted(
|
||||||
|
self.log_dir.glob(f"{self.log_name}-*.log*"),
|
||||||
|
key=sort_key,
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
for old in files[self.backupCount :]:
|
||||||
|
try:
|
||||||
|
old.unlink()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
# 跨天:关闭旧文件流,切换到新日期文件,并清理过期历史文件
|
||||||
|
today = datetime.date.today()
|
||||||
|
if today != self._current_date:
|
||||||
|
self._current_date = today
|
||||||
|
self.close()
|
||||||
|
self.baseFilename = self._build_filename(today)
|
||||||
|
self.stream = self._open()
|
||||||
|
self._cleanup_old_files()
|
||||||
|
super().emit(record)
|
||||||
|
|
||||||
|
|
||||||
class ExcludeWatchFilesFilter(logging.Filter):
|
class ExcludeWatchFilesFilter(logging.Filter):
|
||||||
"""
|
"""
|
||||||
日志过滤器,用于排除第三方库的日志输出
|
日志过滤器,用于排除第三方库的日志输出
|
||||||
@@ -112,7 +186,7 @@ def setup_logging(logger: logging.Logger, log_name: str = "app"):
|
|||||||
|
|
||||||
为指定的 logger 配置以下功能:
|
为指定的 logger 配置以下功能:
|
||||||
1. 设置日志级别和格式
|
1. 设置日志级别和格式
|
||||||
2. 创建按日期命名的轮转文件处理器(支持文件大小限制和备份数量控制)
|
2. 创建按自然日切分的轮转文件处理器(跨天自动切换文件,支持文件大小限制和备份数量控制)
|
||||||
3. 创建带颜色格式化的控制台处理器
|
3. 创建带颜色格式化的控制台处理器
|
||||||
4. 应用过滤器排除第三方库日志
|
4. 应用过滤器排除第三方库日志
|
||||||
|
|
||||||
@@ -145,15 +219,15 @@ def setup_logging(logger: logging.Logger, log_name: str = "app"):
|
|||||||
plain_formatter = logging.Formatter(log_format)
|
plain_formatter = logging.Formatter(log_format)
|
||||||
color_formatter = ColoredFormatter(log_format)
|
color_formatter = ColoredFormatter(log_format)
|
||||||
|
|
||||||
# 创建轮转文件处理器 - 限制文件大小为 10MB,保留 5 个备份文件
|
# 创建按自然日切分的轮转文件处理器(跨天自动切文件,单文件超限再按大小轮转)
|
||||||
max_bytes = log_config.get("max_size", 10 * 1024 * 1024) # 默认 10MB
|
max_bytes = log_config.get("max_size", 10 * 1024 * 1024) # 默认 10MB
|
||||||
backup_count = log_config.get("backup_count", 5) # 默认保留 5 个备份
|
backup_count = log_config.get("backup_count", 5) # 默认保留 5 个历史文件
|
||||||
|
|
||||||
file_handler = RotatingFileHandler(
|
file_handler = DailyRotatingFileHandler(
|
||||||
f"{log_config['file']}/{log_name}-{datetime.date.today()}.log",
|
log_config["file"],
|
||||||
encoding="utf-8",
|
log_name,
|
||||||
maxBytes=max_bytes,
|
max_bytes=max_bytes,
|
||||||
backupCount=backup_count,
|
backup_count=backup_count,
|
||||||
)
|
)
|
||||||
file_handler.setFormatter(plain_formatter)
|
file_handler.setFormatter(plain_formatter)
|
||||||
file_handler.addFilter(ExcludeWatchFilesFilter())
|
file_handler.addFilter(ExcludeWatchFilesFilter())
|
||||||
|
|||||||
Reference in New Issue
Block a user