@@ -55,18 +55,24 @@ def set_and_clean_settings_env_vars(
5555 monkeypatch .setenv (
5656 "TRACING_OPENTELEMETRY_COLLECTOR_PORT" , f"{ tracing_settings_in [1 ]} "
5757 )
58+ sampling_probability_mocked = False
59+ if tracing_settings_in [2 ]:
60+ sampling_probability_mocked = True
61+ monkeypatch .setenv ("TRACING_SAMPLING_PROBABILITY" , f"{ tracing_settings_in [2 ]} " )
5862 yield
5963 if endpoint_mocked :
6064 monkeypatch .delenv ("TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT" )
6165 if port_mocked :
6266 monkeypatch .delenv ("TRACING_OPENTELEMETRY_COLLECTOR_PORT" )
67+ if sampling_probability_mocked :
68+ monkeypatch .delenv ("TRACING_SAMPLING_PROBABILITY" )
6369
6470
6571@pytest .mark .parametrize (
6672 "tracing_settings_in" ,
6773 [
68- ("http://opentelemetry-collector" , 4318 ),
69- ("http://opentelemetry-collector" , "4318" ),
74+ ("http://opentelemetry-collector" , 4318 , 1.0 ),
75+ ("http://opentelemetry-collector" , "4318" , 1.0 ),
7076 ],
7177 indirect = True ,
7278)
@@ -91,15 +97,16 @@ async def test_valid_tracing_settings(
9197@pytest .mark .parametrize (
9298 "tracing_settings_in" ,
9399 [
94- ("http://opentelemetry-collector" , 80 ),
95- ("http://opentelemetry-collector" , 1238712936 ),
96- ("opentelemetry-collector" , 4318 ),
97- ("httsdasp://ot@##el-collector" , 4318 ),
98- (" !@#$%^&*()[]{};:,<>?\\ |`~+=/'\" " , 4318 ),
100+ ("http://opentelemetry-collector" , 80 , 0.5 ),
101+ ("http://opentelemetry-collector" , 1238712936 , 0.5 ),
102+ ("opentelemetry-collector" , 4318 , 0.5 ),
103+ ("httsdasp://ot@##el-collector" , 4318 , 0.5 ),
104+ (" !@#$%^&*()[]{};:,<>?\\ |`~+=/'\" " , 4318 , 0.5 ),
99105 # The following exceeds max DNS name length
100106 (
101107 "" .join (random .choice (string .ascii_letters ) for _ in range (300 )),
102108 "1238712936" ,
109+ 0.5 ,
103110 ), # noqa: S311
104111 ],
105112 indirect = True ,
@@ -143,14 +150,14 @@ def manage_package(request):
143150 "tracing_settings_in, manage_package" ,
144151 [
145152 (
146- ("http://opentelemetry-collector" , 4318 ),
153+ ("http://opentelemetry-collector" , 4318 , 1.0 ),
147154 (
148155 "opentelemetry-instrumentation-botocore" ,
149156 "opentelemetry.instrumentation.botocore" ,
150157 ),
151158 ),
152159 (
153- ("http://opentelemetry-collector" , "4318" ),
160+ ("http://opentelemetry-collector" , "4318" , 1.0 ),
154161 (
155162 "opentelemetry-instrumentation-aiopg" ,
156163 "opentelemetry.instrumentation.aiopg" ,
@@ -184,7 +191,7 @@ async def test_tracing_setup_package_detection(
184191@pytest .mark .parametrize (
185192 "tracing_settings_in" ,
186193 [
187- ("http://opentelemetry-collector" , 4318 ),
194+ ("http://opentelemetry-collector" , 4318 , 1.0 ),
188195 ],
189196 indirect = True ,
190197)
@@ -233,7 +240,7 @@ async def handler(handler_data: dict):
233240@pytest .mark .parametrize (
234241 "tracing_settings_in" ,
235242 [
236- ("http://opentelemetry-collector" , 4318 ),
243+ ("http://opentelemetry-collector" , 4318 , 1.0 ),
237244 ],
238245 indirect = True ,
239246)
@@ -284,3 +291,57 @@ async def handler(handler_data: dict):
284291 for span in spans
285292 if span .context is not None and span .attributes is not None
286293 )
294+
295+
296+ @pytest .mark .parametrize (
297+ "tracing_settings_in" ,
298+ [
299+ ("http://opentelemetry-collector" , 4318 , 0.05 ),
300+ ],
301+ indirect = True ,
302+ )
303+ async def test_tracing_sampling_probability_effective (
304+ mock_otel_collector : InMemorySpanExporter ,
305+ mocked_app : FastAPI ,
306+ set_and_clean_settings_env_vars : Callable [[], None ],
307+ tracing_settings_in : Callable [[], dict [str , Any ]],
308+ ):
309+ """
310+ This test checks that the TRACING_SAMPLING_PROBABILITY setting in TracingSettings
311+ is effective by sending 1000 requests and verifying that the number of collected traces
312+ is close to 0.05 * 1000 (with some tolerance).
313+ """
314+ n_requests = 1000
315+ tolerance_probability = 0.5
316+
317+ tracing_settings = TracingSettings ()
318+
319+ async def handler ():
320+ return PlainTextResponse ("ok" )
321+
322+ mocked_app .get ("/" )(handler )
323+
324+ async for _ in get_tracing_instrumentation_lifespan (
325+ tracing_settings = tracing_settings ,
326+ service_name = "Mock-OpenTelemetry-Pytest" ,
327+ )(app = mocked_app ):
328+ initialize_fastapi_app_tracing (mocked_app , add_response_trace_id_header = True )
329+ client = TestClient (mocked_app )
330+ for _ in range (n_requests ):
331+ client .get ("/" )
332+ spans = mock_otel_collector .get_finished_spans ()
333+ trace_ids = set ()
334+ for span in spans :
335+ if span .context is not None :
336+ trace_ids .add (span .context .trace_id )
337+ num_traces = len (trace_ids )
338+ expected_num_traces = int (
339+ tracing_settings .TRACING_SAMPLING_PROBABILITY * n_requests
340+ )
341+ # Allow a 50% tolerance due to randomness
342+ tolerance = int (tolerance_probability * expected_num_traces )
343+ assert (
344+ expected_num_traces - tolerance
345+ <= num_traces
346+ <= expected_num_traces + tolerance
347+ ), f"Expected roughly { expected_num_traces } distinct trace ids, got { num_traces } "
0 commit comments