Files
kami_spider_monorepo/observability/tracing.py
danial 0e41e7acce feat(core): 初始化核心配置和部署文件
- 添加 .env.example 环境变量配置示例
- 添加 .gitignore 忽略文件配置
- 添加 core/config.py 配置管理模块
- 添加 deployments/k8s/configmap.yaml Kubernetes 配置
- 添加 core/database.py 数据库连接管理模块
- 添加 core/dependencies.py 全局依赖模块
- 添加 DEPENDENCIES_UPDATED.md 依赖更新记录
- 添加 deployments/k8s/deployment.yaml Kubernetes 部署配置- 添加 deployments/swarm/docker-compose.swarm.yml Docker Swarm 部署配置
- 添加 deployments/docker/docker-compose.yml Docker 部署配置
- 添加 deployments/docker/Dockerfile 应用镜像构建文件
- 添加 middleware/error_handler.py 全局异常处理中间件
2025-11-01 14:32:29 +08:00

164 lines
4.2 KiB
Python

"""
OpenTelemetry tracing configuration with gRPC exporter.
Provides distributed tracing for the application.
"""
from typing import Optional
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider, SpanProcessor
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
from opentelemetry.instrumentation.redis import RedisInstrumentor
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased
from core.config import settings
tracer_provider: Optional[TracerProvider] = None
span_processor: Optional[SpanProcessor] = None
def init_tracing() -> trace.Tracer:
"""
Initialize OpenTelemetry tracing with gRPC exporter.
This should be called at application startup.
Returns:
trace.Tracer: Configured tracer instance
"""
global tracer_provider, span_processor
if not settings.otel_enabled:
# Return no-op tracer if OpenTelemetry is disabled
return trace.get_tracer(__name__)
# Create resource with service name
resource = Resource(attributes={
SERVICE_NAME: settings.otel_service_name
})
# Create sampler based on sample rate
sampler = TraceIdRatioBased(settings.otel_sample_rate)
# Create tracer provider
tracer_provider = TracerProvider(
resource=resource,
sampler=sampler
)
# Create OTLP gRPC exporter
otlp_exporter = OTLPSpanExporter(
endpoint=settings.otel_exporter_endpoint,
insecure=settings.otel_exporter_insecure
)
# Create batch span processor
span_processor = BatchSpanProcessor(otlp_exporter)
tracer_provider.add_span_processor(span_processor)
# Set global tracer provider
trace.set_tracer_provider(tracer_provider)
# Return tracer
return trace.get_tracer(__name__)
def instrument_app(app) -> None:
"""
Instrument FastAPI application with OpenTelemetry.
Args:
app: FastAPI application instance
"""
if not settings.otel_enabled:
return
# Instrument FastAPI
FastAPIInstrumentor.instrument_app(app)
# Instrument HTTP client
HTTPXClientInstrumentor().instrument()
# Redis instrumentation is done when Redis client is created
# SQLAlchemy instrumentation is done when engine is created
def instrument_sqlalchemy(engine) -> None:
"""
Instrument SQLAlchemy engine with OpenTelemetry.
Args:
engine: SQLAlchemy engine instance
"""
if not settings.otel_enabled:
return
SQLAlchemyInstrumentor().instrument(
engine=engine.sync_engine
)
def instrument_redis() -> None:
"""
Instrument Redis client with OpenTelemetry.
"""
if not settings.otel_enabled:
return
RedisInstrumentor().instrument()
async def shutdown_tracing() -> None:
"""
Shutdown tracing and flush remaining spans.
This should be called at application shutdown.
"""
global tracer_provider, span_processor
if span_processor:
span_processor.shutdown()
if tracer_provider:
tracer_provider.shutdown()
def get_tracer(name: str = __name__) -> trace.Tracer:
"""
Get tracer instance.
Args:
name: Tracer name (usually __name__)
Returns:
trace.Tracer: Tracer instance
"""
return trace.get_tracer(name)
def get_current_span() -> trace.Span:
"""
Get current active span.
Returns:
trace.Span: Current span
"""
return trace.get_current_span()
def get_trace_id() -> str:
"""
Get current trace ID as hex string.
Returns:
str: Trace ID or empty string if no active span
"""
span = get_current_span()
if span and span.get_span_context().is_valid:
return format(span.get_span_context().trace_id, '032x')
return ""