mirror of
https://github.com/NVIDIA/TensorRT-LLM.git
synced 2026-02-03 17:52:19 +08:00
145 lines
4.9 KiB
Python
145 lines
4.9 KiB
Python
"""Unified logging module for disagg benchmark.
|
|
|
|
Provides structured logging with both console and file output.
|
|
Logger is automatically initialized and ready to use upon import.
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
import sys
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
|
|
class DisaggLogger:
|
|
"""Unified logger that writes to both console and file.
|
|
|
|
Features:
|
|
- Multi-level logging (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
- Simultaneous output to stdout and file
|
|
- Automatic log file creation in output directory
|
|
- Clean formatting without emoji icons
|
|
|
|
Note: Use get_logger() function to get the singleton instance.
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""Initialize logger.
|
|
|
|
Note: This should only be called once by get_logger().
|
|
Multiple calls are safe due to handler clearing.
|
|
"""
|
|
self.logger = logging.getLogger("disagg_benchmark")
|
|
self.logger.setLevel(logging.INFO)
|
|
self.logger.handlers.clear() # Clear any existing handlers to avoid duplicates
|
|
|
|
# Console handler - outputs to stdout
|
|
console_handler = logging.StreamHandler(sys.stdout)
|
|
console_handler.setLevel(logging.INFO)
|
|
console_formatter = logging.Formatter(
|
|
"%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
|
|
)
|
|
console_handler.setFormatter(console_formatter)
|
|
self.logger.addHandler(console_handler)
|
|
|
|
# File handler will be added when output path is available
|
|
self.file_handler: Optional[logging.FileHandler] = None
|
|
|
|
def setup_file_logging(self, output_path: str) -> None:
|
|
"""Setup file logging to output directory.
|
|
|
|
Args:
|
|
output_path: Output directory path from EnvManager.get_output_path()
|
|
"""
|
|
if self.file_handler:
|
|
# File handler already exists
|
|
return
|
|
|
|
try:
|
|
os.makedirs(output_path, exist_ok=True)
|
|
|
|
# Create log file with timestamp
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
log_file = os.path.join(output_path, f"disagg_benchmark_{timestamp}.log")
|
|
|
|
self.file_handler = logging.FileHandler(log_file, mode="a", encoding="utf-8")
|
|
self.file_handler.setLevel(logging.INFO)
|
|
file_formatter = logging.Formatter(
|
|
"%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
|
|
)
|
|
self.file_handler.setFormatter(file_formatter)
|
|
self.logger.addHandler(self.file_handler)
|
|
|
|
self.info(f"Log file created: {log_file}")
|
|
except Exception as e:
|
|
self.warning(f"Failed to setup file logging: {e}")
|
|
|
|
def debug(self, msg: str) -> None:
|
|
"""Log debug message."""
|
|
self.logger.debug(msg)
|
|
|
|
def info(self, msg: str) -> None:
|
|
"""Log info message."""
|
|
self.logger.info(msg)
|
|
|
|
def warning(self, msg: str) -> None:
|
|
"""Log warning message."""
|
|
self.logger.warning(msg)
|
|
|
|
def error(self, msg: str) -> None:
|
|
"""Log error message."""
|
|
self.logger.error(msg)
|
|
|
|
def critical(self, msg: str) -> None:
|
|
"""Log critical message."""
|
|
self.logger.critical(msg)
|
|
|
|
def success(self, msg: str) -> None:
|
|
"""Log success message (as INFO level)."""
|
|
self.logger.info(f"[SUCCESS] {msg}")
|
|
|
|
def failure(self, msg: str) -> None:
|
|
"""Log failure message (as ERROR level)."""
|
|
self.logger.error(f"[FAILED] {msg}")
|
|
|
|
|
|
# Global logger instance - lazy initialization via proxy class
|
|
_logger_instance: Optional[DisaggLogger] = None
|
|
|
|
|
|
class LazyLogger:
|
|
"""Lazy logger proxy that initializes on first access."""
|
|
|
|
def _ensure_logger(self) -> DisaggLogger:
|
|
"""Ensure logger is initialized and return it."""
|
|
global _logger_instance
|
|
if _logger_instance is None:
|
|
_logger_instance = DisaggLogger()
|
|
# Auto-setup file logging on first access
|
|
try:
|
|
from .common import EnvManager
|
|
|
|
output_path = EnvManager.get_output_path()
|
|
if output_path and not output_path.startswith("<"):
|
|
_logger_instance.setup_file_logging(output_path)
|
|
else:
|
|
_logger_instance.warning(
|
|
f"OUTPUT_PATH not configured (current: '{output_path}'). Logging to console only."
|
|
)
|
|
_logger_instance.info(
|
|
"Set OUTPUT_PATH environment variable to enable file logging."
|
|
)
|
|
except Exception as e:
|
|
_logger_instance.warning(
|
|
f"Failed to setup file logging: {e}. Logging to console only."
|
|
)
|
|
return _logger_instance
|
|
|
|
def __getattr__(self, name):
|
|
"""Delegate all attribute access to the real logger."""
|
|
return getattr(self._ensure_logger(), name)
|
|
|
|
|
|
# Export lazy logger instance - only initializes when actually used
|
|
logger = LazyLogger()
|