from opentelemetry import metrics, trace from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.trace import TracerProvider, Tracer from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter from prometheus_client import start_http_server from opentelemetry.exporter.prometheus import PrometheusMetricReader from opentelemetry.sdk.resources import SERVICE_NAME, Resource import os def initialize_trace(app=None, name=None) -> Tracer: # Resource can be required for some backends, e.g. Jaeger or Prometheus # If resource wouldn't be set - traces wouldn't appear in Jaeger. # The 'name' value is the name shown in Jaeger. resource = Resource( attributes={ SERVICE_NAME: ( name if name else os.environ.get("APPLICATION_NAME", "Python App") ) } ) # # 1. Tracing (Jaeger) configuration # ---------------------------------- # # Configure the OTLP exporter (or HTTP exporter the same way) # the recommended configuration is via environment variables. The mandatory ones: # OTEL_EXPORTER_OTLP_ENDPOINT: "http://jaeger:4317" # OTEL_EXPORTER_OTLP_INSECURE: "true" # for more see here: https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html # alternatively these values can be provided as input values to OTLPSpanExporter() constructor. # # otlp_exporter = OTLPSpanExporter() # or provide endpoint explicitly, as otlp_exporter = OTLPSpanExporter(endpoint="http://jaeger:4317") # # Initialize the tracer provider. This is abstract API call. # The actual implementation is given by the included dependency # The opentelemetry default is the OTLP trace provider. provider = TracerProvider(resource=resource) # Add the exporters to the batch span processor span_processor = BatchSpanProcessor(otlp_exporter) provider.add_span_processor(span_processor) # Sets the global default tracer provider trace.set_tracer_provider(provider) # # logging.info(' * Instrumenting application..... ') # if Flask, then use: # FlaskInstrumentor().instrument_app(flask_app) # if FastAPI, then use # FastAPIInstrumentor.instrument_app(app) # # Python Flask is now automatically instrumented to export details of each REST endpoint call. # For Django based app, use 'opentelemetry-instrumentation-django' import instead. see here: # https://opentelemetry-python.readthedocs.io/en/latest/examples/django/README.html for example # # If the used library has no instrumentation support in opentelemetry.io, then the trace # can be started manually. In this code, # 'trace' should be global shared variable, initialized once per application (see tracing.py) # 'tracer' can be shared global or local variable, application can use multiple uniquely # named tracers, Jaeger will group the generated traces by the name of the tracer # that was used to start the Span. See this example code: # # tracer = trace.get_tracer(name) # # @app.route("/server_request") # def server_request(): # with tracer.start_as_current_span( # "server_request", # context=extract(request.headers), # kind=trace.SpanKind.SERVER, # attributes=collect_request_attributes(request.environ), # ): # # do the work here, like logging.info something and return response # logging.info(request.args.get("param")) # return "served" # # Note: # the otlp code looks for HTTP header named 'traceparent' that, if present, should contain # the otlp parent span identification, and the span created here will be made a child # of that parent span, indicating visually the progression of requests via different services # for as long as the current span is provided in the traceparent header to next service. # 2. Metrics (Prometheus) configuration # ------------------------------------- # # Start Prometheus client start_http_server(port=9464, addr="localhost") # Initialize PrometheusMetricReader which pulls metrics from the SDK # on-demand to respond to scrape requests (unlike Jaeger, which worls with push model - application # pushes traces when those become available to Jaeger's collector. Prometheus works with pull model # where it periodically polls pre-configured and/or discovered endpoints for metrics. reader = PrometheusMetricReader() provider = MeterProvider(resource=resource, metric_readers=[reader]) metrics.set_meter_provider(provider) return trace.get_tracer(name)