@@ -21,6 +21,8 @@ extern ngx_module_t otel_ngx_module;
2121#include  " nginx_config.h" 
2222#include  " nginx_utils.h" 
2323#include  " propagate.h" 
24+ #include  " post_span_processor.h" 
25+ #include  " post_batch_span_processor.h" 
2426#include  < opentelemetry/context/context.h> 
2527#include  < opentelemetry/nostd/shared_ptr.h> 
2628#include  < opentelemetry/sdk/trace/batch_span_processor.h> 
@@ -496,21 +498,13 @@ TraceContext* CreateTraceContext(ngx_http_request_t* req, ngx_http_variable_valu
496498  return  context;
497499}
498500
499- ngx_int_t  StartNgxSpan (ngx_http_request_t * req) {
500-   if  (!IsOtelEnabled (req)) {
501-     return  NGX_DECLINED;
502-   }
503- 
504-   //  Internal requests must be called from another location in nginx, that should already have a trace. Without this check, a call would generate an extra (unrelated) span without much information
505-   if  (req->internal ) {
506-     return  NGX_DECLINED;
507-   }
501+ TraceContext* StartNgxTrace (ngx_http_request_t * req)  {
508502
509503  ngx_http_variable_value_t * val = ngx_http_get_indexed_variable (req, otel_ngx_variables[0 ].index );
510504
511505  if  (!val) {
512506    ngx_log_error (NGX_LOG_ERR, req->connection ->log , 0 , " Unable to find OpenTelemetry context"  );
513-     return  NGX_DECLINED ;
507+     return  nullptr ;
514508  }
515509
516510  TraceContext* context = CreateTraceContext (req, val);
@@ -524,30 +518,43 @@ ngx_int_t StartNgxSpan(ngx_http_request_t* req) {
524518    incomingContext = ExtractContext (&carrier);
525519  }
526520
527-   trace::StartSpanOptions startOpts;
528-   startOpts.kind  = trace::SpanKind::kServer ;
529-   startOpts.parent  = GetCurrentSpan (incomingContext);
521+   const  auto  operationName = GetOperationName (req);
522+ 
523+   std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>> commonAttributes = {
524+           {" http.method"  , FromNgxString (req->method_name )},
525+           {" http.flavor"  , NgxHttpFlavor (req)},
526+           {" http.target"  , FromNgxString (req->unparsed_uri )},
527+           //  Add http.route as its according to OpenTelemetry spec
528+           {" http.route"  , FromNgxString (req->unparsed_uri )}
529+   };
530+ 
531+   trace::StartSpanOptions requestStartOpts;
532+   requestStartOpts.parent  = GetCurrentSpan (incomingContext);
533+   requestStartOpts.kind  = trace::SpanKind::kServer ;
534+   context->request_span  = GetTracer ()->StartSpan (operationName,commonAttributes, requestStartOpts);
530535
531-   context->request_span  = GetTracer ()->StartSpan (
532-     GetOperationName (req),
533-     {
534-       {" http.method"  , FromNgxString (req->method_name )},
535-       {" http.flavor"  , NgxHttpFlavor (req)},
536-       {" http.target"  , FromNgxString (req->unparsed_uri )},
537-     },
538-     startOpts);
536+   trace::StartSpanOptions innerSpanOpts;
537+   innerSpanOpts.parent  = context->request_span ->GetContext ();
538+   innerSpanOpts.kind  = trace::SpanKind::kInternal ;
539+ 
540+   context->inner_span  = GetTracer ()->StartSpan (operationName,commonAttributes,innerSpanOpts);
539541
540542  nostd::string_view serverName = GetNgxServerName (req);
541543  if  (!serverName.empty ()) {
542544    context->request_span ->SetAttribute (" http.server_name"  , serverName);
545+     context->inner_span ->SetAttribute (" http.server_name"  , serverName);
543546  }
544547
545548  if  (req->headers_in .host ) {
546-     context->request_span ->SetAttribute (" http.host"  , FromNgxString (req->headers_in .host ->value ));
549+     const  auto  host = FromNgxString (req->headers_in .host ->value );
550+     context->request_span ->SetAttribute (" http.host"  , host);
551+     context->inner_span ->SetAttribute (" http.host"  , host);
547552  }
548553
549554  if  (req->headers_in .user_agent ) {
550-     context->request_span ->SetAttribute (" http.user_agent"  , FromNgxString (req->headers_in .user_agent ->value ));
555+     const  auto  userAgent = FromNgxString (req->headers_in .user_agent ->value );
556+     context->request_span ->SetAttribute (" http.user_agent"  , userAgent);
557+     context->inner_span ->SetAttribute (" http.user_agent"  , userAgent);
551558  }
552559
553560  if  (locConf->captureHeaders ) {
@@ -559,10 +566,24 @@ ngx_int_t StartNgxSpan(ngx_http_request_t* req) {
559566                       {excludedHeaders, 2 });
560567  }
561568
562-   auto  outgoingContext = incomingContext.SetValue (trace::kSpanKey , context->request_span );
569+   auto  outgoingContext = incomingContext.SetValue (trace::kSpanKey , context->inner_span );
563570
564571  InjectContext (&carrier, outgoingContext);
565572
573+   return  context;
574+ }
575+ 
576+ ngx_int_t  StartNgxSpan (ngx_http_request_t * req) {
577+   if  (!IsOtelEnabled (req)) {
578+     return  NGX_DECLINED;
579+   }
580+ 
581+   if  (req->internal ) {
582+     return  NGX_DECLINED;
583+   }
584+ 
585+   StartNgxTrace (req);
586+ 
566587  return  NGX_DECLINED;
567588}
568589
@@ -591,18 +612,7 @@ void AddScriptAttributes(
591612  }
592613}
593614
594- ngx_int_t  FinishNgxSpan (ngx_http_request_t * req) {
595-   if  (!IsOtelEnabled (req)) {
596-     return  NGX_DECLINED;
597-   }
598- 
599-   TraceContext* context = GetTraceContext (req);
600- 
601-   if  (!context) {
602-     return  NGX_DECLINED;
603-   }
604- 
605-   auto  span = context->request_span ;
615+ void  UpdateSpan (nostd::shared_ptr<opentelemetry::trace::Span> span, ngx_http_request_t * req, bool  captureHeaders = false ) {
606616  const  auto  status_code = req->headers_out .status ;
607617  span->SetAttribute (" http.status_code"  , status_code);
608618
@@ -612,9 +622,9 @@ ngx_int_t FinishNgxSpan(ngx_http_request_t* req) {
612622    span->SetStatus (trace::StatusCode::kOk );
613623  }
614624
615-   OtelNgxLocationConf*  locConf = GetOtelLocationConf (req);
625+   OtelNgxLocationConf * locConf = GetOtelLocationConf (req);
616626
617-   if  (locConf->captureHeaders ) {
627+   if  (locConf->captureHeaders  && captureHeaders ) {
618628    OtelCaptureHeaders (span, ngx_string (" http.response.header."  ), &req->headers_out .headers ,
619629#if  (NGX_PCRE)
620630                       locConf->sensitiveHeaderNames , locConf->sensitiveHeaderValues ,
@@ -625,7 +635,46 @@ ngx_int_t FinishNgxSpan(ngx_http_request_t* req) {
625635  AddScriptAttributes (span.get (), GetOtelMainConf (req)->scriptAttributes , req);
626636  AddScriptAttributes (span.get (), locConf->customAttributes , req);
627637
638+   if  (req->headers_out .status  >= 400  && req->headers_out .status  <= 599 ) {
639+     span->SetStatus (trace::StatusCode::kError );
640+   }
641+ 
628642  span->UpdateName (GetOperationName (req));
643+ }
644+ 
645+ ngx_int_t  FinishNgxSpan (ngx_http_request_t * req) {
646+   if  (!IsOtelEnabled (req)) {
647+     return  NGX_DECLINED;
648+   }
649+ 
650+   if  (req->internal ) {
651+     return  NGX_DECLINED;
652+   }
653+ 
654+   TraceContext* context = GetTraceContext (req);
655+ 
656+   /* 
657+    * When nginx fails to process request (bad request, prematurely closed) then span is not started, here we can start 
658+    * a new span because there were no upstream calls anyway. 
659+    */  
660+   if  (!context) {
661+     context = StartNgxTrace (req);
662+   }
663+ 
664+   auto  span = context->request_span ;
665+   UpdateSpan (span, req, true );
666+ 
667+   if  (context->inner_span ) {
668+ 
669+     const  bool  hasUpstream = req->upstream  != nullptr ;
670+     if  (hasUpstream) {
671+       context->inner_span ->SetAttribute (" span.kind"  , static_cast <int >(trace::SpanKind::kClient ));
672+     }
673+ 
674+     UpdateSpan (context->inner_span , req);
675+ 
676+     context->inner_span ->End ();
677+   }
629678
630679  span->End ();
631680  return  NGX_DECLINED;
@@ -642,7 +691,7 @@ static ngx_int_t InitModule(ngx_conf_t* conf) {
642691
643692  const  PhaseHandler handlers[] = {
644693    {NGX_HTTP_REWRITE_PHASE, StartNgxSpan},
645-     {NGX_HTTP_LOG_PHASE, FinishNgxSpan}, 
694+     {NGX_HTTP_LOG_PHASE, FinishNgxSpan}
646695  };
647696
648697  for  (const  PhaseHandler& ph : handlers) {
@@ -1300,11 +1349,11 @@ CreateProcessor(const OtelNgxAgentConfig* conf, std::unique_ptr<sdktrace::SpanEx
13001349    opts.max_export_batch_size  = conf->processor .batch .maxExportBatchSize ;
13011350
13021351    return  std::unique_ptr<sdktrace::SpanProcessor>(
1303-       new  sdktrace::BatchSpanProcessor (std::move (exporter), opts));
1352+       new  PostBatchSpanProcessor (std::move (exporter), opts));
13041353  }
13051354
13061355  return  std::unique_ptr<sdktrace::SpanProcessor>(
1307-     new  sdktrace::SimpleSpanProcessor (std::move (exporter)));
1356+     new  PostSpanProcessor (std::move (exporter)));
13081357}
13091358
13101359static  std::unique_ptr<sdktrace::Sampler> CreateSampler (const  OtelNgxAgentConfig* conf) {
0 commit comments