Files
YG-Datasets/backend/app/core/logging.py
Developer 68453cead8 feat(backend): 完善日志系统,支持按日期分目录存储
- 实现 logs/YYYY-MM-DD/ 日期文件夹结构
- 添加 success.log 和 failure.log 专用日志
- 使用 TimedRotatingFileHandler 实现按天切割
- 添加 log_success 和 log_failure 便捷函数
- 集成 markitdown 进行文件转换
- 优化文件存储路径,按项目ID分类存储

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 10:44:09 +08:00

140 lines
4.3 KiB
Python

"""
Logging Configuration
日志配置
"""
import logging
import sys
from datetime import datetime
from typing import Any
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
from pathlib import Path
from app.core.config import get_settings
settings = get_settings()
# Log directory
LOG_DIR = Path("./logs")
LOG_DIR.mkdir(exist_ok=True)
# 日期格式
LOG_DATE = datetime.now().strftime("%Y-%m-%d")
# 当天的日志目录
CURRENT_LOG_DIR = LOG_DIR / LOG_DATE
CURRENT_LOG_DIR.mkdir(exist_ok=True)
def get_log_path(filename: str) -> Path:
"""获取当天的日志文件路径"""
return CURRENT_LOG_DIR / filename
def setup_logging(name: str = "yg_dataset") -> logging.Logger:
"""Setup application logging"""
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG if settings.DEBUG else logging.INFO)
# Avoid duplicate handlers
if logger.handlers:
return logger
# Console handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.DEBUG if settings.DEBUG else logging.INFO)
console_formatter = logging.Formatter(
fmt="%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# Main log file handler - app.log
main_file_handler = TimedRotatingFileHandler(
get_log_path("app.log"),
when="midnight",
interval=1,
backupCount=30,
encoding="utf-8"
)
main_file_handler.setLevel(logging.INFO)
main_file_formatter = logging.Formatter(
fmt="%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
main_file_handler.setFormatter(main_file_formatter)
logger.addHandler(main_file_handler)
return logger
# Create default logger
logger = setup_logging()
# ============== Success Logger ==============
def get_success_logger() -> logging.Logger:
"""获取成功日志记录器"""
success_logger = logging.getLogger("yg_dataset.success")
if not success_logger.handlers:
handler = RotatingFileHandler(
get_log_path("success.log"),
maxBytes=10 * 1024 * 1024,
backupCount=30,
encoding="utf-8"
)
handler.setLevel(logging.INFO)
formatter = logging.Formatter(
fmt="%(asctime)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
handler.setFormatter(formatter)
success_logger.addHandler(handler)
success_logger.setLevel(logging.INFO)
return success_logger
# ============== Failure Logger ==============
def get_failure_logger() -> logging.Logger:
"""获取失败日志记录器"""
failure_logger = logging.getLogger("yg_dataset.failure")
if not failure_logger.handlers:
handler = RotatingFileHandler(
get_log_path("failure.log"),
maxBytes=10 * 1024 * 1024,
backupCount=30,
encoding="utf-8"
)
handler.setLevel(logging.WARNING)
formatter = logging.Formatter(
fmt="%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
handler.setFormatter(formatter)
failure_logger.addHandler(handler)
failure_logger.setLevel(logging.WARNING)
return failure_logger
# ============== Convenience functions ==============
def log_success(message: str, **kwargs):
"""记录成功日志"""
extra_info = " | ".join([f"{k}={v}" for k, v in kwargs.items()]) if kwargs else ""
full_message = f"{message} | {extra_info}" if extra_info else message
get_success_logger().info(full_message)
def log_failure(message: str, **kwargs):
"""记录失败日志"""
extra_info = " | ".join([f"{k}={v}" for k, v in kwargs.items()]) if kwargs else ""
full_message = f"{message} | {extra_info}" if extra_info else message
get_failure_logger().warning(full_message)
class LoggerMixin:
"""Mixin to add logging capability to classes"""
@property
def log(self) -> logging.Logger:
"""Get logger for this class"""
return logging.getLogger(self.__class__.__module__ + "." + self.__class__.__name__)