VLAlert / lkalert /utils /logger.py
AsianPlayer's picture
Add VLAlert code
1e05592 verified
Raw
History Blame Contribute Delete
4.15 kB
"""
日志系统
提供统一的日志接口,支持文件和终端输出
"""
import logging
import sys
from pathlib import Path
from datetime import datetime
import json
class ColoredFormatter(logging.Formatter):
"""带颜色的日志格式化器"""
COLORS = {
'DEBUG': '\033[36m', # 青色
'INFO': '\033[32m', # 绿色
'WARNING': '\033[33m', # 黄色
'ERROR': '\033[31m', # 红色
'CRITICAL': '\033[35m', # 紫色
}
RESET = '\033[0m'
def format(self, record):
log_color = self.COLORS.get(record.levelname, self.RESET)
record.levelname = f"{log_color}{record.levelname}{self.RESET}"
return super().format(record)
def setup_logger(
name: str,
log_file: str = None,
level: int = logging.INFO,
console: bool = True
):
"""
设置logger
Args:
name: logger名称
log_file: 日志文件路径(可选)
level: 日志级别
console: 是否输出到控制台
Returns:
logger实例
"""
logger = logging.getLogger(name)
logger.setLevel(level)
logger.handlers.clear() # 清除已有的handlers
# 格式
fmt = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
datefmt = '%Y-%m-%d %H:%M:%S'
# 控制台handler
if console:
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(level)
console_formatter = ColoredFormatter(fmt, datefmt=datefmt)
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# 文件handler
if log_file:
log_path = Path(log_file)
log_path.parent.mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler(log_file, encoding='utf-8')
file_handler.setLevel(level)
file_formatter = logging.Formatter(fmt, datefmt=datefmt)
file_handler.setFormatter(file_formatter)
logger.addHandler(file_handler)
return logger
class MetricsLogger:
"""
指标记录器
记录训练/验证指标到JSON文件
"""
def __init__(self, log_dir: str, exp_name: str):
self.log_dir = Path(log_dir)
self.log_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
self.log_file = self.log_dir / f"{exp_name}_{timestamp}.json"
self.metrics = {
'train': [],
'val': [],
'config': {}
}
def log_config(self, config: dict):
"""记录配置"""
self.metrics['config'] = config
self._save()
def log_train(self, step: int, metrics: dict):
"""记录训练指标"""
metrics['step'] = step
metrics['timestamp'] = datetime.now().isoformat()
self.metrics['train'].append(metrics)
self._save()
def log_val(self, epoch: int, metrics: dict):
"""记录验证指标"""
metrics['epoch'] = epoch
metrics['timestamp'] = datetime.now().isoformat()
self.metrics['val'].append(metrics)
self._save()
def _save(self):
"""保存到文件"""
with open(self.log_file, 'w', encoding='utf-8') as f:
json.dump(self.metrics, f, indent=2, ensure_ascii=False)
def get_best_metric(self, metric_name: str, mode: str = 'min'):
"""获取最佳指标"""
if not self.metrics['val']:
return None
values = [m[metric_name] for m in self.metrics['val'] if metric_name in m]
if not values:
return None
if mode == 'min':
best_val = min(values)
best_epoch = values.index(best_val)
else:
best_val = max(values)
best_epoch = values.index(best_val)
return {
'value': best_val,
'epoch': self.metrics['val'][best_epoch]['epoch']
}
# 创建全局logger
def get_logger(name: str = "lkalert"):
"""获取或创建logger"""
return logging.getLogger(name)