Files
security-ai-edge/utils/logger.py
16337 b0ddb6ee1a feat(project): move edge_inference_service contents to root and update paths
- Moved all project files and directories (config, core, models, etc.) from
  edge_inference_service/ to the repository root ai_edge/
- Updated model path in config/settings.py to reflect new structure
- Revised usage paths in __init__.py documentation
2026-01-29 18:43:19 +08:00

374 lines
12 KiB
Python

"""
分级日志系统
提供多级别日志支持,包含文件输出、控制台输出、性能指标记录
"""
import os
import sys
import logging
import logging.handlers
import time
import threading
import json
from datetime import datetime
from typing import Any, Dict, Optional, Union
from pathlib import Path
from config.settings import get_settings
class PerformanceLogger:
"""性能指标记录器"""
def __init__(self):
self._metrics: Dict[str, list] = {}
self._lock = threading.Lock()
def record(self, metric_name: str, value: float, tags: Optional[Dict[str, str]] = None):
"""记录性能指标"""
with self._lock:
key = metric_name
if metric_name not in self._metrics:
self._metrics[metric_name] = []
self._metrics[metric_name].append({
"value": value,
"timestamp": time.time(),
"datetime": datetime.now().isoformat(),
"tags": tags or {}
})
def get_metrics(self, metric_name: Optional[str] = None) -> Dict[str, Any]:
"""获取性能指标"""
with self._lock:
if metric_name:
return self._metrics.get(metric_name, [])
return dict(self._metrics)
def get_last_value(self, metric_name: str) -> Optional[float]:
"""获取最新指标值"""
with self._lock:
metrics = self._metrics.get(metric_name, [])
if metrics:
return metrics[-1].get("value")
return None
def get_statistics(self, metric_name: str) -> Dict[str, float]:
"""获取指标统计信息"""
with self._lock:
values = [m["value"] for m in self._metrics.get(metric_name, [])]
if not values:
return {}
return {
"count": len(values),
"min": min(values),
"max": max(values),
"avg": sum(values) / len(values),
"sum": sum(values),
}
def clear(self):
"""清空所有指标"""
with self._lock:
self._metrics.clear()
class StructuredLogger:
"""结构化日志记录器"""
def __init__(self, name: str = "edge_inference"):
self.name = name
self._logger = None
self._performance_logger = PerformanceLogger()
self._log_dir = "./logs"
self._init_logger()
def _init_logger(self):
"""初始化日志配置"""
settings = get_settings()
self._log_level = getattr(logging, settings.log_level.upper(), logging.INFO)
self._log_dir = settings.log_dir
self._max_size = settings.log_file_max_size
self._backup_count = settings.log_file_backup_count
os.makedirs(self._log_dir, exist_ok=True)
self._logger = logging.getLogger(self.name)
self._logger.setLevel(self._log_level)
self._logger.handlers.clear()
formatter = logging.Formatter(
fmt='%(asctime)s | %(levelname)-8s | %(name)s | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(self._log_level)
console_handler.setFormatter(formatter)
self._logger.addHandler(console_handler)
self._add_file_handler(formatter)
def _add_file_handler(self, formatter: logging.Formatter):
"""添加文件处理器"""
log_file = os.path.join(self._log_dir, f"{self.name}.log")
try:
file_handler = logging.handlers.RotatingFileHandler(
log_file,
maxBytes=self._max_size,
backupCount=self._backup_count,
encoding='utf-8'
)
file_handler.setLevel(self._log_level)
file_handler.setFormatter(formatter)
self._logger.addHandler(file_handler)
error_file = os.path.join(self._log_dir, f"{self.name}_error.log")
error_handler = logging.handlers.RotatingFileHandler(
error_file,
maxBytes=self._max_size,
backupCount=self._backup_count,
encoding='utf-8'
)
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(formatter)
self._logger.addHandler(error_handler)
except Exception as e:
sys.stderr.write(f"创建日志文件处理器失败: {e}\n")
def _log(
self,
level: int,
message: str,
extra: Optional[Dict[str, Any]] = None,
exc_info: bool = False
):
"""结构化日志记录"""
log_data = {
"timestamp": datetime.now().isoformat(),
"logger": self.name,
}
if extra:
log_data.update(extra)
extra_fields = {"structured_data": json.dumps(log_data, ensure_ascii=False)}
self._logger.log(level, message, extra=extra_fields, exc_info=exc_info)
def debug(self, message: str, **kwargs):
"""DEBUG级别日志"""
self._log(logging.DEBUG, message, kwargs)
def info(self, message: str, **kwargs):
"""INFO级别日志"""
self._log(logging.INFO, message, kwargs)
def warning(self, message: str, **kwargs):
"""WARNING级别日志"""
self._log(logging.WARNING, message, kwargs)
def error(self, message: str, exc_info: bool = True, **kwargs):
"""ERROR级别日志"""
self._log(logging.ERROR, message, kwargs, exc_info=exc_info)
def critical(self, message: str, exc_info: bool = True, **kwargs):
"""CRITICAL级别日志"""
self._log(logging.CRITICAL, message, kwargs, exc_info=exc_info)
def performance(self, metric_name: str, value: float,
duration_ms: Optional[float] = None, **tags):
"""记录性能指标"""
self._performance_logger.record(metric_name, value, tags)
perf_data = {
"metric": metric_name,
"value": value,
"duration_ms": duration_ms,
"tags": tags
}
self.info(f"性能指标: {metric_name} = {value}", **perf_data)
def log_inference_latency(self, latency_ms: float, batch_size: int = 1):
"""记录推理延迟"""
self.performance(
"inference_latency_ms",
latency_ms,
batch_size=batch_size,
throughput_fps=1000.0 / latency_ms if latency_ms > 0 else 0
)
def log_frame_rate(self, fps: float, camera_id: str):
"""记录帧率"""
self.performance(
"frame_rate_fps",
fps,
camera_id=camera_id
)
def log_resource_usage(
self,
cpu_percent: float,
memory_mb: float,
gpu_memory_mb: Optional[float] = None
):
"""记录资源使用情况"""
self.performance(
"cpu_percent",
cpu_percent,
memory_mb=memory_mb,
gpu_memory_mb=gpu_memory_mb
)
def log_alert(self, alert_type: str, camera_id: str, roi_id: str,
confidence: Optional[float] = None):
"""记录告警事件"""
self.info(
f"告警触发: {alert_type}",
alert_type=alert_type,
camera_id=camera_id,
roi_id=roi_id,
confidence=confidence
)
def log_connection_event(self, event_type: str,
connection_type: str,
target: str,
success: bool,
error_msg: Optional[str] = None):
"""记录连接事件"""
self.info(
f"连接事件: {event_type} - {connection_type} -> {target}",
event_type=event_type,
connection_type=connection_type,
target=target,
success=success,
error_msg=error_msg
)
def get_performance_metrics(self) -> Dict[str, Any]:
"""获取性能指标"""
return {
"metrics": self._performance_logger.get_metrics(),
"statistics": {
name: self._performance_logger.get_statistics(name)
for name in self._performance_logger.get_metrics()
}
}
def get_statistics(self, metric_name: str) -> Dict[str, float]:
"""获取指定指标统计"""
return self._performance_logger.get_statistics(metric_name)
def export_metrics(self, output_path: str):
"""导出性能指标到文件"""
metrics = self.get_performance_metrics()
try:
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(metrics, f, ensure_ascii=False, indent=2)
self.info(f"性能指标已导出: {output_path}")
except Exception as e:
self.error(f"导出性能指标失败: {e}")
def flush(self):
"""刷新日志处理器"""
for handler in self._logger.handlers:
if hasattr(handler, 'flush'):
handler.flush()
def close(self):
"""关闭日志系统"""
self.flush()
for handler in self._logger.handlers:
handler.close()
self._logger.handlers.clear()
class ContextLogger:
"""上下文日志记录器,自动附加上下文信息"""
def __init__(self, base_logger: StructuredLogger, context: Dict[str, Any]):
self._base_logger = base_logger
self._context = context
def _add_context(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
"""添加上下文信息"""
result = dict(self._context)
result.update(kwargs)
return result
def debug(self, message: str, **kwargs):
"""DEBUG级别日志"""
self._base_logger.debug(message, **self._add_context(kwargs))
def info(self, message: str, **kwargs):
"""INFO级别日志"""
self._base_logger.info(message, **self._add_context(kwargs))
def warning(self, message: str, **kwargs):
"""WARNING级别日志"""
self._base_logger.warning(message, **self._add_context(kwargs))
def error(self, message: str, **kwargs):
"""ERROR级别日志"""
self._base_logger.error(message, **self._add_context(kwargs))
def critical(self, message: str, **kwargs):
"""CRITICAL级别日志"""
self._base_logger.critical(message, **self._add_context(kwargs))
def with_context(self, **additional_context) -> 'ContextLogger':
"""添加额外上下文"""
new_context = dict(self._context)
new_context.update(additional_context)
return ContextLogger(self._base_logger, new_context)
_structured_logger_instance = None
def get_logger(name: str = "edge_inference") -> StructuredLogger:
"""获取结构化日志记录器单例"""
global _structured_logger_instance
if _structured_logger_instance is None:
_structured_logger_instance = StructuredLogger(name)
return _structured_logger_instance
def create_logger_with_context(base_logger: StructuredLogger,
context: Dict[str, Any]) -> ContextLogger:
"""创建带上下文的日志记录器"""
return ContextLogger(base_logger, context)
def log_performance_operation(operation_name: str, logger_instance: StructuredLogger):
"""性能日志装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
try:
result = func(*args, **kwargs)
duration_ms = (time.perf_counter() - start_time) * 1000
logger_instance.log_inference_latency(
duration_ms,
batch_size=1
)
logger_instance.debug(
f"操作完成: {operation_name}, 耗时: {duration_ms:.2f}ms"
)
return result
except Exception as e:
duration_ms = (time.perf_counter() - start_time) * 1000
logger_instance.error(
f"操作失败: {operation_name}, 耗时: {duration_ms:.2f}ms, 错误: {e}"
)
raise
return wrapper
return decorator