Files
chat-bot/api/logger.py
lychang 64ce30fdfd init
2025-08-26 09:35:29 +08:00

140 lines
4.1 KiB
Python

# !/usr/bin/env python
# -*- coding:utf-8 -*-
# @Filename: logger.py
# @Author: lychang
# @Time: 2021/7/15 16:41
import json
import logging
import sys
import time
from typing import Any, Dict
def _ensure_str(msg: Any) -> str:
"""确保消息为字符串类型"""
return msg if isinstance(msg, str) else str(msg)
class JsonFormatter(logging.Formatter):
"""自定义JSON格式化器"""
default_msec_format = '%s.%03d'
def __init__(self) -> None:
super().__init__()
def format(self, record: logging.LogRecord) -> str:
"""格式化日志记录为JSON字符串"""
formatted_record = self._build_record_dict(record)
return json.dumps(formatted_record, ensure_ascii=False)
def _build_record_dict(self, record: logging.LogRecord) -> Dict[str, Any]:
"""构建日志字典结构"""
return {
'deal_time': self.formatTime(record),
'level': record.levelname,
'app_type': record.name,
'business_type': getattr(record, 'business_type', ''),
'deal_stage': getattr(record, 'deal_stage', ''),
'object_uuid': getattr(record, 'object_uuid', ''),
'trace_id': getattr(record, 'trace_id', ''),
'source': getattr(record, 'source', ''),
'message': record.getMessage(),
}
def formatTime(self, record: logging.LogRecord, datefmt: str = None) -> str:
"""格式化时间(包含毫秒)"""
ct = self.converter(record.created)
if datefmt:
s = time.strftime(datefmt, ct)
else:
s = time.strftime(self.default_time_format, ct)
return self.default_msec_format % (s, record.msecs)
# 初始化日志系统
INSTLOG = logging.getLogger("private_assistant")
INSTLOG.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JsonFormatter())
INSTLOG.addHandler(handler)
INSTLOG.propagate = False # 防止日志向上传播
DEFAULT_EXTRA = {
"business_type": "",
"deal_stage": "",
"object_uuid": "",
"trace_id": "",
"source": ""
}
def set_extra(
deal_stage: str = "",
business_type: str = "",
object_uuid: str = "",
trace_id: str = "",
source: str = ""
) -> None:
"""设置全局日志附加信息"""
global DEFAULT_EXTRA
DEFAULT_EXTRA.update({
'deal_stage': deal_stage,
'business_type': business_type,
'object_uuid': object_uuid,
'trace_id': trace_id,
'source': source,
})
def _merge_extra(**kwargs: Dict) -> Dict:
"""合并用户extra与默认配置"""
extra = kwargs.get('extra', {})
return {
'business_type': extra.get('business_type', DEFAULT_EXTRA['business_type']),
'deal_stage': extra.get('deal_stage', DEFAULT_EXTRA['deal_stage']),
'object_uuid': extra.get('object_uuid', DEFAULT_EXTRA['object_uuid']),
'trace_id': extra.get('trace_id', DEFAULT_EXTRA['trace_id']),
'source': extra.get('source', DEFAULT_EXTRA['source']),
}
def _log_wrapper(logger_method: Any) -> Any:
"""日志方法装饰器"""
def wrapper(msg: Any, *args: Any, **kwargs: Any) -> None:
msg = _ensure_str(msg)
kwargs['extra'] = _merge_extra(**kwargs)
logger_method(msg, *args, **kwargs)
return wrapper
# 使用装饰器统一处理日志方法
info = _log_wrapper(INSTLOG.info)
debug = _log_wrapper(INSTLOG.debug)
warning = _log_wrapper(INSTLOG.warning)
error = _log_wrapper(INSTLOG.error)
def exception(msg: Any, *args: Any, **kwargs: Any) -> None:
"""异常日志特殊处理"""
msg = _ensure_str(msg)
kwargs['extra'] = _merge_extra(**kwargs)
INSTLOG.error(msg, *args, exc_info=True, **kwargs)
def set_log_level(level: str) -> None:
"""设置日志级别"""
level_map = {
'DEBUG': logging.DEBUG,
'INFO': logging.INFO,
'WARNING': logging.WARNING,
'ERROR': logging.ERROR,
}
INSTLOG.setLevel(level_map.get(level.upper(), logging.INFO))
def set_log_root_name(name: str) -> None:
"""设置日志根名称"""
INSTLOG.name = name