-
Notifications
You must be signed in to change notification settings - Fork 54.4k
Expand file tree
/
Copy pathlogging_config.py
More file actions
169 lines (138 loc) · 5.58 KB
/
Copy pathlogging_config.py
File metadata and controls
169 lines (138 loc) · 5.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# -*- coding: utf-8 -*-
"""
===================================
日志配置模块 - 统一的日志系统初始化
===================================
职责:
1. 提供统一的日志格式和配置常量
2. 支持控制台 + 文件(常规/调试)三层日志输出
3. 自动降低第三方库日志级别
"""
import logging
import sys
from datetime import datetime
from logging.handlers import RotatingFileHandler
from pathlib import Path
from typing import List, Optional
LOG_FORMAT = "%(asctime)s | %(levelname)-8s | %(pathname)s:%(lineno)d | %(message)s"
LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
_sensitive_log_preview_enabled = False
def set_sensitive_log_preview_enabled(enabled: bool) -> None:
"""Persist an explicit per-process override for sensitive LLM previews."""
global _sensitive_log_preview_enabled
_sensitive_log_preview_enabled = bool(enabled)
def is_sensitive_log_preview_enabled() -> bool:
"""Return whether an explicit per-process preview override is enabled."""
return _sensitive_log_preview_enabled
class RelativePathFormatter(logging.Formatter):
"""自定义 Formatter,输出相对路径而非绝对路径"""
def __init__(self, fmt=None, datefmt=None, relative_to=None):
super().__init__(fmt, datefmt)
self.relative_to = Path(relative_to) if relative_to else Path.cwd()
def format(self, record):
# 将绝对路径转为相对路径
try:
record.pathname = str(Path(record.pathname).relative_to(self.relative_to))
except ValueError:
# 如果无法转换为相对路径,保持原样
pass
return super().format(record)
# 默认需要降低日志级别的第三方库
DEFAULT_QUIET_LOGGERS = [
'urllib3',
'sqlalchemy',
'google',
'httpx',
]
def setup_logging(
log_prefix: str = "app",
log_dir: str = "./logs",
console_level: Optional[int] = None,
debug: bool = False,
extra_quiet_loggers: Optional[List[str]] = None,
) -> None:
"""
统一的日志系统初始化
配置三层日志输出:
1. 控制台:根据 debug 参数或 console_level 设置级别
2. 常规日志文件:INFO 级别,10MB 轮转,保留 5 个备份
3. 调试日志文件:DEBUG 级别,50MB 轮转,保留 3 个备份
Args:
log_prefix: 日志文件名前缀(如 "api_server" -> api_server_20240101.log)
log_dir: 日志文件目录,默认 ./logs
console_level: 控制台日志级别(可选,优先于 debug 参数)
debug: 是否启用调试模式(控制台输出 DEBUG 级别)
extra_quiet_loggers: 额外需要降低日志级别的第三方库列表
"""
# 确定控制台日志级别
if console_level is not None:
level = console_level
else:
level = logging.DEBUG if debug else logging.INFO
# Only an explicit process-level debug override should bypass runtime config checks.
set_sensitive_log_preview_enabled(debug)
# 创建日志目录
log_path = Path(log_dir)
log_path.mkdir(parents=True, exist_ok=True)
# 日志文件路径(按日期分文件)
today_str = datetime.now().strftime('%Y%m%d')
log_file = log_path / f"{log_prefix}_{today_str}.log"
debug_log_file = log_path / f"{log_prefix}_debug_{today_str}.log"
# 配置根 logger
root_logger = logging.getLogger()
root_logger.setLevel(logging.DEBUG) # 根 logger 设为 DEBUG,由 handler 控制输出级别
# 清除已有 handler,避免重复添加
if root_logger.handlers:
root_logger.handlers.clear()
# 创建相对路径 Formatter(相对于项目根目录)
project_root = Path.cwd()
rel_formatter = RelativePathFormatter(
LOG_FORMAT, LOG_DATE_FORMAT, relative_to=project_root
)
# Handler 1: 控制台输出
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(level)
console_handler.setFormatter(rel_formatter)
root_logger.addHandler(console_handler)
# Handler 2: 常规日志文件(INFO 级别,10MB 轮转)
file_handler = RotatingFileHandler(
log_file,
maxBytes=10 * 1024 * 1024, # 10MB
backupCount=5,
encoding='utf-8'
)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(rel_formatter)
root_logger.addHandler(file_handler)
# Handler 3: 调试日志文件(DEBUG 级别,包含所有详细信息)
debug_handler = RotatingFileHandler(
debug_log_file,
maxBytes=50 * 1024 * 1024, # 50MB
backupCount=3,
encoding='utf-8'
)
debug_handler.setLevel(logging.DEBUG)
debug_handler.setFormatter(rel_formatter)
root_logger.addHandler(debug_handler)
# 降低第三方库的日志级别
quiet_loggers = DEFAULT_QUIET_LOGGERS.copy()
if extra_quiet_loggers:
quiet_loggers.extend(extra_quiet_loggers)
for logger_name in quiet_loggers:
logging.getLogger(logger_name).setLevel(logging.WARNING)
# 输出初始化完成信息(使用相对路径)
try:
rel_log_path = log_path.resolve().relative_to(project_root)
except ValueError:
rel_log_path = log_path
try:
rel_log_file = log_file.resolve().relative_to(project_root)
except ValueError:
rel_log_file = log_file
try:
rel_debug_log_file = debug_log_file.resolve().relative_to(project_root)
except ValueError:
rel_debug_log_file = debug_log_file
logging.info(f"日志系统初始化完成,日志目录: {rel_log_path}")
logging.info(f"常规日志: {rel_log_file}")
logging.info(f"调试日志: {rel_debug_log_file}")