Initial commit: 000-platform project skeleton
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
111
2026-01-23 14:32:09 +08:00
commit daa8125c58
31 changed files with 1517 additions and 0 deletions

125
sdk/logger.py Normal file
View File

@@ -0,0 +1,125 @@
"""统一日志模块"""
import os
import json
import logging
from datetime import datetime
from typing import Optional, Any
from functools import lru_cache
from .trace import get_trace_id, get_tenant_id, get_user_id
class PlatformLogger:
"""平台日志器
支持本地输出和远程上报两种模式
"""
def __init__(
self,
app_code: str,
platform_url: Optional[str] = None,
api_key: Optional[str] = None,
local_only: bool = True
):
self.app_code = app_code
self.platform_url = platform_url or os.getenv("PLATFORM_URL", "")
self.api_key = api_key or os.getenv("PLATFORM_API_KEY", "")
self.local_only = local_only or not self.platform_url
# 设置本地日志
self._logger = logging.getLogger(app_code)
self._logger.setLevel(logging.DEBUG)
if not self._logger.handlers:
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(
'%(asctime)s | %(levelname)s | %(name)s | %(message)s'
))
self._logger.addHandler(handler)
def _format_message(self, message: str, **kwargs) -> str:
"""格式化日志消息包含trace_id"""
trace_id = get_trace_id()
prefix = f"[{trace_id[:8]}] " if trace_id else ""
if kwargs:
extra = " | " + " ".join(f"{k}={v}" for k, v in kwargs.items())
else:
extra = ""
return f"{prefix}{message}{extra}"
def _log(
self,
level: str,
message: str,
log_type: str = "app",
category: Optional[str] = None,
context: Optional[dict] = None,
**kwargs
):
"""内部日志方法"""
formatted = self._format_message(message, **kwargs)
# 本地日志
log_method = getattr(self._logger, level.lower(), self._logger.info)
log_method(formatted)
# TODO: 远程上报(异步)
if not self.local_only:
self._send_to_platform(level, message, log_type, category, context, kwargs)
def _send_to_platform(
self,
level: str,
message: str,
log_type: str,
category: Optional[str],
context: Optional[dict],
extra: dict
):
"""发送日志到平台(异步,后续实现)"""
# TODO: 使用httpx异步发送
pass
def debug(self, message: str, **kwargs):
"""调试日志"""
self._log("debug", message, **kwargs)
def info(self, message: str, **kwargs):
"""信息日志"""
self._log("info", message, **kwargs)
def warn(self, message: str, **kwargs):
"""警告日志"""
self._log("warn", message, **kwargs)
def warning(self, message: str, **kwargs):
"""警告日志(别名)"""
self.warn(message, **kwargs)
def error(self, message: str, error: Optional[Exception] = None, **kwargs):
"""错误日志"""
if error:
kwargs["error_type"] = type(error).__name__
kwargs["error_msg"] = str(error)
self._log("error", message, log_type="error", **kwargs)
def audit(self, action: str, target_type: str, target_id: str, **kwargs):
"""审计日志"""
self._log(
"info",
f"AUDIT: {action} {target_type}:{target_id}",
log_type="audit",
action=action,
target_type=target_type,
target_id=target_id,
**kwargs
)
@lru_cache()
def get_logger(app_code: str) -> PlatformLogger:
"""获取日志器(单例)"""
return PlatformLogger(app_code)