mirror of
https://github.com/PaddlePaddle/FastDeploy.git
synced 2026-04-23 00:17:25 +08:00
[Feature] implement log channel separation and request log level system (#7190)
* feat: implement log channel separation and request log level system * fix: log system improvements based on review * add request_id to error logs, use RequestLogLevel enum, and unify logger implementation from utils to logger module
This commit is contained in:
+107
-17
@@ -20,21 +20,36 @@ This module provides the get_logger method to uniformly manage logging behavior
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
|
||||
from fastdeploy import envs
|
||||
from fastdeploy.logger.formatters import CustomFormatter
|
||||
from fastdeploy.logger.config import resolve_log_level
|
||||
from fastdeploy.logger.formatters import ColoredFormatter, CustomFormatter
|
||||
from fastdeploy.logger.handlers import DailyRotatingFileHandler, LazyFileHandler
|
||||
from fastdeploy.logger.setup_logging import setup_logging
|
||||
|
||||
# Standard log format
|
||||
_LOG_FORMAT = "%(levelname)-8s %(asctime)s %(process)-5s %(filename)s[line:%(lineno)d] %(message)s"
|
||||
|
||||
|
||||
class FastDeployLogger:
|
||||
_instance = None
|
||||
_initialized = False
|
||||
_lock = threading.RLock()
|
||||
|
||||
# Channel to file mapping
|
||||
_channel_files = {
|
||||
"main": "fastdeploy.log",
|
||||
"request": "request.log",
|
||||
"console": "console.log",
|
||||
}
|
||||
|
||||
# Cache for channel loggers that have been configured
|
||||
_configured_channels = set()
|
||||
|
||||
def __new__(cls):
|
||||
"""Singleton pattern implementation"""
|
||||
if cls._instance is None:
|
||||
@@ -50,7 +65,7 @@ class FastDeployLogger:
|
||||
setup_logging()
|
||||
self._initialized = True
|
||||
|
||||
def get_logger(self, name, file_name=None, without_formater=False, print_to_console=False):
|
||||
def get_logger(self, name, file_name=None, without_formater=False, print_to_console=False, channel=None):
|
||||
"""
|
||||
Get logger (compatible with the original interface)
|
||||
|
||||
@@ -59,7 +74,14 @@ class FastDeployLogger:
|
||||
file_name: Log file name (for compatibility)
|
||||
without_formater: Whether to not use a formatter
|
||||
print_to_console: Whether to print to console
|
||||
channel: Log channel (main, request, console)
|
||||
"""
|
||||
# If channel is specified, use channel-based logging
|
||||
if channel is not None:
|
||||
if not self._initialized:
|
||||
self._initialize()
|
||||
return self._get_channel_logger(name, channel)
|
||||
|
||||
# If only one parameter is provided, use the new unified naming convention
|
||||
if file_name is None and not without_formater and not print_to_console:
|
||||
# Lazy initialization
|
||||
@@ -74,27 +96,95 @@ class FastDeployLogger:
|
||||
"""
|
||||
New unified way to get logger
|
||||
"""
|
||||
if name is None:
|
||||
return logging.getLogger("fastdeploy")
|
||||
return self._get_channel_logger(name, "main")
|
||||
|
||||
# Handle __main__ special case
|
||||
if name == "__main__":
|
||||
def _get_channel_logger(self, name, channel):
|
||||
"""
|
||||
Get logger through channel with manual handler setup.
|
||||
|
||||
Uses manual addHandler instead of dictConfig for better performance.
|
||||
Handlers are attached to the channel root logger (fastdeploy.{channel}),
|
||||
and child loggers propagate to it.
|
||||
|
||||
Args:
|
||||
name: logger name
|
||||
channel: log channel (main, request, console)
|
||||
"""
|
||||
# Get or create the channel root logger (all handlers go here)
|
||||
channel_root_name = f"fastdeploy.{channel}"
|
||||
channel_logger = logging.getLogger(channel_root_name)
|
||||
|
||||
# Configure the channel root logger once
|
||||
if channel not in self._configured_channels:
|
||||
self._configured_channels.add(channel)
|
||||
|
||||
log_dir = envs.FD_LOG_DIR
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
|
||||
# Resolve log level (priority: FD_LOG_LEVEL > FD_DEBUG)
|
||||
log_level = resolve_log_level()
|
||||
channel_logger.setLevel(logging.DEBUG if log_level == "DEBUG" else logging.INFO)
|
||||
|
||||
# Create formatters
|
||||
file_formatter = logging.Formatter(_LOG_FORMAT)
|
||||
console_formatter = ColoredFormatter(_LOG_FORMAT)
|
||||
|
||||
# Clear existing handlers
|
||||
for handler in channel_logger.handlers[:]:
|
||||
channel_logger.removeHandler(handler)
|
||||
|
||||
# Create file handler for this channel
|
||||
file_name = self._channel_files.get(channel, f"{channel}.log")
|
||||
log_file = os.path.join(log_dir, file_name)
|
||||
backup_count = int(envs.FD_LOG_BACKUP_COUNT)
|
||||
|
||||
file_handler = LazyFileHandler(log_file, backupCount=backup_count)
|
||||
file_handler.setFormatter(file_formatter)
|
||||
channel_logger.addHandler(file_handler)
|
||||
|
||||
# Error file handler (all channels write errors to error.log)
|
||||
error_log_file = os.path.join(log_dir, "error.log")
|
||||
error_file_handler = LazyFileHandler(
|
||||
filename=error_log_file, backupCount=backup_count, level=logging.ERROR
|
||||
)
|
||||
error_file_handler.setFormatter(file_formatter)
|
||||
channel_logger.addHandler(error_file_handler)
|
||||
|
||||
# Stderr handler for ERROR level (all channels output errors to stderr)
|
||||
stderr_handler = logging.StreamHandler(sys.stderr)
|
||||
stderr_handler.setLevel(logging.ERROR)
|
||||
stderr_handler.setFormatter(console_formatter)
|
||||
channel_logger.addHandler(stderr_handler)
|
||||
|
||||
# Console stdout handler for console channel only
|
||||
if channel == "console":
|
||||
stdout_handler = logging.StreamHandler(sys.stdout)
|
||||
stdout_handler.setLevel(logging.DEBUG if log_level == "DEBUG" else logging.INFO)
|
||||
stdout_handler.setFormatter(console_formatter)
|
||||
# Filter to exclude ERROR and above (they go to stderr)
|
||||
stdout_handler.addFilter(lambda record: record.levelno < logging.ERROR)
|
||||
channel_logger.addHandler(stdout_handler)
|
||||
|
||||
channel_logger.propagate = False
|
||||
|
||||
# Determine the actual logger name and return the appropriate logger
|
||||
if name is None or name == "fastdeploy":
|
||||
return channel_logger
|
||||
elif name == "__main__":
|
||||
import __main__
|
||||
|
||||
# Get the __file__ attribute of the main module
|
||||
if hasattr(__main__, "__file__"):
|
||||
# Get the main module file name
|
||||
base_name = Path(__main__.__file__).stem
|
||||
# Create logger with prefix
|
||||
return logging.getLogger(f"fastdeploy.main.{base_name}")
|
||||
return logging.getLogger("fastdeploy.main")
|
||||
|
||||
# If already in fastdeploy namespace, use directly
|
||||
if name.startswith("fastdeploy.") or name == "fastdeploy":
|
||||
return logging.getLogger(name)
|
||||
logger_name = f"{channel_root_name}.{base_name}"
|
||||
else:
|
||||
return channel_logger
|
||||
elif name.startswith("fastdeploy."):
|
||||
logger_name = name
|
||||
else:
|
||||
# Add fastdeploy prefix for other cases
|
||||
return logging.getLogger(f"fastdeploy.{name}")
|
||||
logger_name = f"{channel_root_name}.{name}"
|
||||
|
||||
# Child loggers propagate to channel_logger (which has handlers)
|
||||
return logging.getLogger(logger_name)
|
||||
|
||||
def get_trace_logger(self, name, file_name, without_formater=False, print_to_console=False):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user