@@ -310,9 +310,56 @@ def test_metric_uninstrument(self):
310
310
if isinstance (point , NumberDataPoint ):
311
311
self .assertEqual (point .value , 0 )
312
312
313
+ def test_sub_app_fastapi_call (self ):
314
+ """
315
+ This test is to ensure that a span in case of a sub app targetted contains the correct server url
316
+
317
+ As this test case covers manual instrumentation, we won't see any additional spans for the sub app.
318
+ In this case all generated spans might suffice the requirements for the attributes already
319
+ (as the testcase is not setting a root_path for the outer app here)
320
+ """
321
+
322
+ self ._client .get ("/sub/home" )
323
+ spans = self .memory_exporter .get_finished_spans ()
324
+ self .assertEqual (len (spans ), 3 )
325
+ for span in spans :
326
+ # As we are only looking to the "outer" app, we would see only the "GET /sub" spans
327
+ self .assertIn ("GET /sub" , span .name )
328
+
329
+ # We now want to specifically test all spans including the
330
+ # - HTTP_TARGET
331
+ # - HTTP_URL
332
+ # attributes to be populated with the expected values
333
+ spans_with_http_attributes = [
334
+ span
335
+ for span in spans
336
+ if (
337
+ SpanAttributes .HTTP_URL in span .attributes
338
+ or SpanAttributes .HTTP_TARGET in span .attributes
339
+ )
340
+ ]
341
+
342
+ # We expect only one span to have the HTTP attributes set (the SERVER span from the app itself)
343
+ # the sub app is not instrumented with manual instrumentation tests.
344
+ self .assertEqual (1 , len (spans_with_http_attributes ))
345
+
346
+ for span in spans_with_http_attributes :
347
+ self .assertEqual (
348
+ "/sub/home" , span .attributes [SpanAttributes .HTTP_TARGET ]
349
+ )
350
+ self .assertEqual (
351
+ "https://testserver:443/sub/home" ,
352
+ span .attributes [SpanAttributes .HTTP_URL ],
353
+ )
354
+
313
355
@staticmethod
314
356
def _create_fastapi_app ():
315
357
app = fastapi .FastAPI ()
358
+ sub_app = fastapi .FastAPI ()
359
+
360
+ @sub_app .get ("/home" )
361
+ async def _ ():
362
+ return {"message" : "sub hi" }
316
363
317
364
@app .get ("/foobar" )
318
365
async def _ ():
@@ -330,6 +377,8 @@ async def _(param: str):
330
377
async def _ ():
331
378
return {"message" : "ok" }
332
379
380
+ app .mount ("/sub" , app = sub_app )
381
+
333
382
return app
334
383
335
384
@@ -444,6 +493,58 @@ def tearDown(self):
444
493
self ._instrumentor .uninstrument ()
445
494
super ().tearDown ()
446
495
496
+ def test_sub_app_fastapi_call (self ):
497
+ """
498
+ !!! Attention: we need to override this testcase for the auto-instrumented variant
499
+ The reason is, that with auto instrumentation, the sub app is instrumented as well
500
+ and therefore we would see the spans for the sub app as well
501
+
502
+ This test is to ensure that a span in case of a sub app targeted contains the correct server url
503
+ """
504
+
505
+ self ._client .get ("/sub/home" )
506
+ spans = self .memory_exporter .get_finished_spans ()
507
+ self .assertEqual (len (spans ), 6 )
508
+
509
+ for span in spans :
510
+ # As we are only looking to the "outer" app, we would see only the "GET /sub" spans
511
+ # -> the outer app is not aware of the sub_apps internal routes
512
+ sub_in = "GET /sub" in span .name
513
+ # The sub app spans are named GET /home as from the sub app perspective the request targets /home
514
+ # -> the sub app is technically not aware of the /sub prefix
515
+ home_in = "GET /home" in span .name
516
+
517
+ # We expect the spans to be either from the outer app or the sub app
518
+ self .assertTrue (
519
+ sub_in or home_in ,
520
+ f"Span { span .name } does not have /sub or /home in its name" ,
521
+ )
522
+
523
+ # We now want to specifically test all spans including the
524
+ # - HTTP_TARGET
525
+ # - HTTP_URL
526
+ # attributes to be populated with the expected values
527
+ spans_with_http_attributes = [
528
+ span
529
+ for span in spans
530
+ if (
531
+ SpanAttributes .HTTP_URL in span .attributes
532
+ or SpanAttributes .HTTP_TARGET in span .attributes
533
+ )
534
+ ]
535
+
536
+ # We now expect spans with attributes from both the app and its sub app
537
+ self .assertEqual (2 , len (spans_with_http_attributes ))
538
+
539
+ for span in spans_with_http_attributes :
540
+ self .assertEqual (
541
+ "/sub/home" , span .attributes [SpanAttributes .HTTP_TARGET ]
542
+ )
543
+ self .assertEqual (
544
+ "https://testserver:443/sub/home" ,
545
+ span .attributes [SpanAttributes .HTTP_URL ],
546
+ )
547
+
447
548
448
549
class TestAutoInstrumentationHooks (TestFastAPIManualInstrumentationHooks ):
449
550
"""
@@ -485,6 +586,58 @@ def tearDown(self):
485
586
self ._instrumentor .uninstrument ()
486
587
super ().tearDown ()
487
588
589
+ def test_sub_app_fastapi_call (self ):
590
+ """
591
+ !!! Attention: we need to override this testcase for the auto-instrumented variant
592
+ The reason is, that with auto instrumentation, the sub app is instrumented as well
593
+ and therefore we would see the spans for the sub app as well
594
+
595
+ This test is to ensure that a span in case of a sub app targeted contains the correct server url
596
+ """
597
+
598
+ self ._client .get ("/sub/home" )
599
+ spans = self .memory_exporter .get_finished_spans ()
600
+ self .assertEqual (len (spans ), 6 )
601
+
602
+ for span in spans :
603
+ # As we are only looking to the "outer" app, we would see only the "GET /sub" spans
604
+ # -> the outer app is not aware of the sub_apps internal routes
605
+ sub_in = "GET /sub" in span .name
606
+ # The sub app spans are named GET /home as from the sub app perspective the request targets /home
607
+ # -> the sub app is technically not aware of the /sub prefix
608
+ home_in = "GET /home" in span .name
609
+
610
+ # We expect the spans to be either from the outer app or the sub app
611
+ self .assertTrue (
612
+ sub_in or home_in ,
613
+ f"Span { span .name } does not have /sub or /home in its name" ,
614
+ )
615
+
616
+ # We now want to specifically test all spans including the
617
+ # - HTTP_TARGET
618
+ # - HTTP_URL
619
+ # attributes to be populated with the expected values
620
+ spans_with_http_attributes = [
621
+ span
622
+ for span in spans
623
+ if (
624
+ SpanAttributes .HTTP_URL in span .attributes
625
+ or SpanAttributes .HTTP_TARGET in span .attributes
626
+ )
627
+ ]
628
+
629
+ # We now expect spans with attributes from both the app and its sub app
630
+ self .assertEqual (2 , len (spans_with_http_attributes ))
631
+
632
+ for span in spans_with_http_attributes :
633
+ self .assertEqual (
634
+ "/sub/home" , span .attributes [SpanAttributes .HTTP_TARGET ]
635
+ )
636
+ self .assertEqual (
637
+ "https://testserver:443/sub/home" ,
638
+ span .attributes [SpanAttributes .HTTP_URL ],
639
+ )
640
+
488
641
489
642
class TestAutoInstrumentationLogic (unittest .TestCase ):
490
643
def test_instrumentation (self ):
0 commit comments