1+ import os
2+ from opentelemetry import baggage as baggage_api
3+ from opentelemetry import trace
4+ from opentelemetry .instrumentation .django import DjangoInstrumentor
5+ from opentelemetry .instrumentation .mysqlclient import MySQLClientInstrumentor
6+ from opentelemetry .instrumentation .redis import RedisInstrumentor
7+ from opentelemetry .sdk .resources import Resource
8+ from opentelemetry .sdk .trace import TracerProvider
9+ from opentelemetry .sdk .trace .export import BatchSpanProcessor , ConsoleSpanExporter
10+ from opentelemetry .exporter .otlp .proto .http .trace_exporter import OTLPSpanExporter
11+
12+
13+ OTEL_EXPORTER_OTLP_ENDPOINT = os .getenv ('OTEL_EXPORTER_OTLP_ENDPOINT' )
14+ OTEL_EXPORTER_MODE = os .getenv ('OTEL_EXPORTER_MODE' )
15+
16+ class DjangoTelemetry :
17+
18+ @staticmethod
19+ def request_hook (span , request ):
20+ if not span .is_recording ():
21+ return
22+
23+ # Attach CF-Ray header
24+ span .set_attribute ("cf.ray_id" , request .headers .get ("Cf-Ray" , "" ))
25+
26+ # Attach baggage if present
27+ baggage_val = baggage_api .get_baggage ("cf.ray_id" )
28+ if baggage_val :
29+ span .set_attribute ("baggage.cf.ray_id" , baggage_val )
30+
31+ @staticmethod
32+ def response_hook (span , request , response ):
33+ if span .is_recording () and hasattr (response , "content" ):
34+ span .set_attribute ("http.response.length" , len (response .content ))
35+
36+ @staticmethod
37+ def mysql_hook (span , instance , cursor , statement , parameters ):
38+ """Enrich MySQL spans with DB info"""
39+ if not span .is_recording ():
40+ return
41+ try :
42+ span .set_attribute ("db.system" , "mysql" )
43+ span .set_attribute ("db.name" , os .getenv ('DB_NAME' , 'db' ))
44+ span .set_attribute ("db.statement" , statement )
45+ except Exception :
46+ pass
47+
48+ @staticmethod
49+ def redis_hook (span , instance , args , kwargs ):
50+ """Enrich Redis spans with command + keys"""
51+ if not span .is_recording ():
52+ return
53+ try :
54+ cmd = args [0 ] if args else ""
55+ span .set_attribute ("db.system" , "redis" )
56+ span .set_attribute ("redis.command" , cmd )
57+ if len (args ) > 1 :
58+ # Add first key only (avoid leaking big payloads)
59+ span .set_attribute ("redis.key" , str (args [1 ]))
60+ except Exception :
61+ pass
62+
63+ @classmethod
64+ def setup (cls , environment ):
65+ if environment != "test" :
66+ # set the OTEL_EXPORTER_MODE to null
67+ # No Exporter setup if the env is dev and you want to run the tests locally
68+ if OTEL_EXPORTER_MODE :
69+ resource = Resource .create ({
70+ "service.name" : os .getenv ("OTEL_SERVICE_NAME" , "marketing-api" )
71+ })
72+ # Provider with resource
73+ provider = TracerProvider (resource = resource )
74+ trace .set_tracer_provider (provider )
75+ if OTEL_EXPORTER_MODE == "otel_endpoint" :
76+ exporter = OTLPSpanExporter (endpoint = OTEL_EXPORTER_OTLP_ENDPOINT )
77+ provider .add_span_processor (BatchSpanProcessor (exporter ))
78+ elif OTEL_EXPORTER_MODE == "console" :
79+ exporter = ConsoleSpanExporter ()
80+ provider .add_span_processor (BatchSpanProcessor (exporter ))
81+
82+ # Django
83+ DjangoInstrumentor ().instrument (
84+ request_hook = cls .request_hook ,
85+ response_hook = cls .response_hook ,
86+ )
87+ # MySQL
88+ MySQLClientInstrumentor ().instrument (
89+ enable_commenter = True ,
90+ cursor_instrumentation_enabled = True ,
91+ span_callback = cls .mysql_hook ,
92+ )
93+ # Redis
94+ RedisInstrumentor ().instrument (
95+ tracer_provider = trace .get_tracer_provider (),
96+ request_hook = cls .redis_hook ,
97+ )
0 commit comments