mirror of
https://git.linux-kernel.at/oliver/ivatar.git
synced 2025-11-16 21:18:02 +00:00
234 lines
8.4 KiB
Python
234 lines
8.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
OpenTelemetry configuration for ivatar project.
|
|
|
|
This module provides OpenTelemetry setup and configuration for the ivatar
|
|
Django application, including tracing, metrics, and logging integration.
|
|
"""
|
|
|
|
import os
|
|
import logging
|
|
|
|
from opentelemetry import trace, metrics
|
|
from opentelemetry.sdk.trace import TracerProvider
|
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
from opentelemetry.sdk.metrics import MeterProvider
|
|
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
|
from opentelemetry.sdk.resources import Resource
|
|
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
|
|
from opentelemetry.exporter.prometheus import PrometheusMetricReader
|
|
from opentelemetry.instrumentation.django import DjangoInstrumentor
|
|
from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor
|
|
from opentelemetry.instrumentation.pymysql import PyMySQLInstrumentor
|
|
from opentelemetry.instrumentation.requests import RequestsInstrumentor
|
|
from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor
|
|
|
|
# Note: Memcached instrumentation not available in OpenTelemetry Python
|
|
|
|
logger = logging.getLogger("ivatar")
|
|
|
|
|
|
class OpenTelemetryConfig:
|
|
"""
|
|
OpenTelemetry configuration manager for ivatar.
|
|
|
|
Handles setup of tracing, metrics, and instrumentation for the Django application.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.enabled = self._is_enabled()
|
|
self.service_name = self._get_service_name()
|
|
self.environment = self._get_environment()
|
|
self.resource = self._create_resource()
|
|
|
|
def _is_enabled(self) -> bool:
|
|
"""Check if OpenTelemetry is enabled via environment variable and Django settings."""
|
|
# First check Django settings (for F/LOSS deployments)
|
|
try:
|
|
from django.conf import settings
|
|
from django.core.exceptions import ImproperlyConfigured
|
|
|
|
try:
|
|
if getattr(settings, "ENABLE_OPENTELEMETRY", False):
|
|
return True
|
|
except ImproperlyConfigured:
|
|
# Django settings not configured yet, fall back to environment variable
|
|
pass
|
|
except ImportError:
|
|
# Django not available yet, fall back to environment variable
|
|
pass
|
|
|
|
# Then check OpenTelemetry-specific environment variable
|
|
return os.environ.get("OTEL_ENABLED", "false").lower() in ("true", "1", "yes")
|
|
|
|
def _get_service_name(self) -> str:
|
|
"""Get service name from environment or default."""
|
|
return os.environ.get("OTEL_SERVICE_NAME", "ivatar")
|
|
|
|
def _get_environment(self) -> str:
|
|
"""Get environment name (production, development, etc.)."""
|
|
return os.environ.get("OTEL_ENVIRONMENT", "development")
|
|
|
|
def _create_resource(self) -> Resource:
|
|
"""Create OpenTelemetry resource with service information."""
|
|
return Resource.create(
|
|
{
|
|
"service.name": self.service_name,
|
|
"service.version": os.environ.get("IVATAR_VERSION", "1.8.0"),
|
|
"service.namespace": "libravatar",
|
|
"deployment.environment": self.environment,
|
|
"service.instance.id": os.environ.get("HOSTNAME", "unknown"),
|
|
}
|
|
)
|
|
|
|
def setup_tracing(self) -> None:
|
|
"""Set up OpenTelemetry tracing."""
|
|
if not self.enabled:
|
|
logger.info("OpenTelemetry tracing disabled")
|
|
return
|
|
|
|
try:
|
|
# Set up tracer provider
|
|
trace.set_tracer_provider(TracerProvider(resource=self.resource))
|
|
tracer_provider = trace.get_tracer_provider()
|
|
|
|
# Configure OTLP exporter if endpoint is provided
|
|
otlp_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
|
|
if otlp_endpoint:
|
|
otlp_exporter = OTLPSpanExporter(endpoint=otlp_endpoint)
|
|
span_processor = BatchSpanProcessor(otlp_exporter)
|
|
tracer_provider.add_span_processor(span_processor)
|
|
logger.info(
|
|
f"OpenTelemetry tracing configured with OTLP endpoint: {otlp_endpoint}"
|
|
)
|
|
else:
|
|
logger.info("OpenTelemetry tracing configured without OTLP exporter")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to setup OpenTelemetry tracing: {e}")
|
|
self.enabled = False
|
|
|
|
def setup_metrics(self) -> None:
|
|
"""Set up OpenTelemetry metrics."""
|
|
if not self.enabled:
|
|
logger.info("OpenTelemetry metrics disabled")
|
|
return
|
|
|
|
try:
|
|
# Configure metric readers
|
|
metric_readers = []
|
|
|
|
# Configure Prometheus exporter for metrics
|
|
prometheus_endpoint = os.environ.get(
|
|
"OTEL_PROMETHEUS_ENDPOINT", "0.0.0.0:9464"
|
|
)
|
|
prometheus_reader = PrometheusMetricReader()
|
|
metric_readers.append(prometheus_reader)
|
|
|
|
# Configure OTLP exporter if endpoint is provided
|
|
otlp_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
|
|
if otlp_endpoint:
|
|
otlp_exporter = OTLPMetricExporter(endpoint=otlp_endpoint)
|
|
metric_reader = PeriodicExportingMetricReader(otlp_exporter)
|
|
metric_readers.append(metric_reader)
|
|
logger.info(
|
|
f"OpenTelemetry metrics configured with OTLP endpoint: {otlp_endpoint}"
|
|
)
|
|
|
|
# Set up meter provider with readers
|
|
meter_provider = MeterProvider(
|
|
resource=self.resource, metric_readers=metric_readers
|
|
)
|
|
metrics.set_meter_provider(meter_provider)
|
|
|
|
logger.info(
|
|
f"OpenTelemetry metrics configured with Prometheus endpoint: {prometheus_endpoint}"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to setup OpenTelemetry metrics: {e}")
|
|
self.enabled = False
|
|
|
|
def setup_instrumentation(self) -> None:
|
|
"""Set up OpenTelemetry instrumentation for various libraries."""
|
|
if not self.enabled:
|
|
logger.info("OpenTelemetry instrumentation disabled")
|
|
return
|
|
|
|
try:
|
|
# Django instrumentation
|
|
DjangoInstrumentor().instrument()
|
|
logger.info("Django instrumentation enabled")
|
|
|
|
# Database instrumentation
|
|
Psycopg2Instrumentor().instrument()
|
|
PyMySQLInstrumentor().instrument()
|
|
logger.info("Database instrumentation enabled")
|
|
|
|
# HTTP client instrumentation
|
|
RequestsInstrumentor().instrument()
|
|
URLLib3Instrumentor().instrument()
|
|
logger.info("HTTP client instrumentation enabled")
|
|
|
|
# Note: Memcached instrumentation not available in OpenTelemetry Python
|
|
# Cache operations will be traced through Django instrumentation
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to setup OpenTelemetry instrumentation: {e}")
|
|
self.enabled = False
|
|
|
|
def get_tracer(self, name: str) -> trace.Tracer:
|
|
"""Get a tracer instance."""
|
|
return trace.get_tracer(name)
|
|
|
|
def get_meter(self, name: str) -> metrics.Meter:
|
|
"""Get a meter instance."""
|
|
return metrics.get_meter(name)
|
|
|
|
|
|
# Global OpenTelemetry configuration instance (lazy-loaded)
|
|
_ot_config = None
|
|
|
|
|
|
def get_ot_config():
|
|
"""Get the global OpenTelemetry configuration instance."""
|
|
global _ot_config
|
|
if _ot_config is None:
|
|
_ot_config = OpenTelemetryConfig()
|
|
return _ot_config
|
|
|
|
|
|
def setup_opentelemetry() -> None:
|
|
"""
|
|
Set up OpenTelemetry for the ivatar application.
|
|
|
|
This function should be called during Django application startup.
|
|
"""
|
|
logger.info("Setting up OpenTelemetry...")
|
|
|
|
ot_config = get_ot_config()
|
|
ot_config.setup_tracing()
|
|
ot_config.setup_metrics()
|
|
ot_config.setup_instrumentation()
|
|
|
|
if ot_config.enabled:
|
|
logger.info("OpenTelemetry setup completed successfully")
|
|
else:
|
|
logger.info("OpenTelemetry setup skipped (disabled)")
|
|
|
|
|
|
def get_tracer(name: str) -> trace.Tracer:
|
|
"""Get a tracer instance for the given name."""
|
|
return get_ot_config().get_tracer(name)
|
|
|
|
|
|
def get_meter(name: str) -> metrics.Meter:
|
|
"""Get a meter instance for the given name."""
|
|
return get_ot_config().get_meter(name)
|
|
|
|
|
|
def is_enabled() -> bool:
|
|
"""Check if OpenTelemetry is enabled."""
|
|
return get_ot_config().enabled
|