Skip to content

Commit f09c169

Browse files
tiithansenaasasiku
authored andcommitted
Improve tracing
1) Add possibility to use HTTP OTLP exporter. 2) Improve tracing so that module creates internal or client span depending if the request is handled by nginx directly or passed upstream. 3) Set span status depending on result. If status code is between 400 - 599 set status as error. 4) Catch internal processing failures in log phase. 5) Rewrite tests in JS
1 parent 718b130 commit f09c169

File tree

14 files changed

+2972
-57
lines changed

14 files changed

+2972
-57
lines changed

.github/workflows/nginx.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,17 @@ jobs:
9999
rm -rf /tmp/buildx-cache/nginx
100100
mv /tmp/buildx-cache/express-new /tmp/buildx-cache/express
101101
mv /tmp/buildx-cache/nginx-new /tmp/buildx-cache/nginx
102-
- name: run tests
102+
#- name: run tests
103+
# run: |
104+
# cd instrumentation/nginx/test/instrumentation
105+
# mix test
106+
- name: Setup node
107+
uses: actions/setup-node@v2
108+
with:
109+
node-version: '18'
110+
- name: Install dependencies
103111
run: |
104112
cd instrumentation/nginx/test/instrumentation
105-
mix local.hex --force --if-missing
106-
mix local.rebar --force --if-missing
107-
mix deps.get
108113
mix test
109114
- name: copy artifacts
110115
id: artifacts

instrumentation/nginx/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ add_library(otel_ngx_module SHARED
2323
src/otel_ngx_module_modules.c
2424
src/propagate.cpp
2525
src/script.cpp
26+
src/post_batch_span_processor.cpp
27+
src/post_span_processor.cpp
2628
)
2729

2830
target_compile_options(otel_ngx_module

instrumentation/nginx/src/otel_ngx_module.cpp

Lines changed: 63 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
@@ -38,6 +40,7 @@ extern ngx_module_t otel_ngx_module;
3840
#include <opentelemetry/exporters/otlp/otlp_http_exporter.h>
3941

4042
namespace trace = opentelemetry::trace;
43+
namespace common = opentelemetry::common;
4144
namespace nostd = opentelemetry::nostd;
4245
namespace sdktrace = opentelemetry::sdk::trace;
4346
namespace otlp = opentelemetry::exporter::otlp;
@@ -134,6 +137,8 @@ static ngx_int_t OtelGetContextVar(ngx_http_request_t*, ngx_http_variable_value_
134137
return NGX_OK;
135138
}
136139

140+
141+
137142
static ngx_int_t
138143
OtelGetTraceContextVar(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t data);
139144

@@ -496,21 +501,13 @@ TraceContext* CreateTraceContext(ngx_http_request_t* req, ngx_http_variable_valu
496501
return context;
497502
}
498503

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-
}
504+
TraceContext* StartNgxTrace(ngx_http_request_t* req) {
508505

509506
ngx_http_variable_value_t* val = ngx_http_get_indexed_variable(req, otel_ngx_variables[0].index);
510507

511508
if (!val) {
512509
ngx_log_error(NGX_LOG_ERR, req->connection->log, 0, "Unable to find OpenTelemetry context");
513-
return NGX_DECLINED;
510+
return nullptr;
514511
}
515512

516513
TraceContext* context = CreateTraceContext(req, val);
@@ -524,30 +521,43 @@ ngx_int_t StartNgxSpan(ngx_http_request_t* req) {
524521
incomingContext = ExtractContext(&carrier);
525522
}
526523

527-
trace::StartSpanOptions startOpts;
528-
startOpts.kind = trace::SpanKind::kServer;
529-
startOpts.parent = GetCurrentSpan(incomingContext);
524+
const auto operationName = GetOperationName(req);
530525

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);
526+
std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>> commonAttributes = {
527+
{"http.method", FromNgxString(req->method_name)},
528+
{"http.flavor", NgxHttpFlavor(req)},
529+
{"http.target", FromNgxString(req->unparsed_uri)},
530+
// Add http.route as its according to OpenTelemetry spec
531+
{"http.route", FromNgxString(req->unparsed_uri)}
532+
};
533+
534+
trace::StartSpanOptions requestStartOpts;
535+
requestStartOpts.parent = GetCurrentSpan(incomingContext);
536+
requestStartOpts.kind = trace::SpanKind::kServer;
537+
context->request_span = GetTracer()->StartSpan(operationName,commonAttributes, requestStartOpts);
538+
539+
trace::StartSpanOptions innerSpanOpts;
540+
innerSpanOpts.parent = context->request_span->GetContext();
541+
innerSpanOpts.kind = trace::SpanKind::kInternal;
542+
543+
context->inner_span = GetTracer()->StartSpan(operationName,commonAttributes,innerSpanOpts);
539544

540545
nostd::string_view serverName = GetNgxServerName(req);
541546
if (!serverName.empty()) {
542547
context->request_span->SetAttribute("http.server_name", serverName);
548+
context->inner_span->SetAttribute("http.server_name", serverName);
543549
}
544550

545551
if (req->headers_in.host) {
546-
context->request_span->SetAttribute("http.host", FromNgxString(req->headers_in.host->value));
552+
const auto host = FromNgxString(req->headers_in.host->value);
553+
context->request_span->SetAttribute("http.host", host);
554+
context->inner_span->SetAttribute("http.host", host);
547555
}
548556

549557
if (req->headers_in.user_agent) {
550-
context->request_span->SetAttribute("http.user_agent", FromNgxString(req->headers_in.user_agent->value));
558+
const auto userAgent = FromNgxString(req->headers_in.user_agent->value);
559+
context->request_span->SetAttribute("http.user_agent", userAgent);
560+
context->inner_span->SetAttribute("http.user_agent", userAgent);
551561
}
552562

553563
if (locConf->captureHeaders) {
@@ -559,10 +569,24 @@ ngx_int_t StartNgxSpan(ngx_http_request_t* req) {
559569
{excludedHeaders, 2});
560570
}
561571

562-
auto outgoingContext = incomingContext.SetValue(trace::kSpanKey, context->request_span);
572+
auto outgoingContext = incomingContext.SetValue(trace::kSpanKey, context->inner_span);
563573

564574
InjectContext(&carrier, outgoingContext);
565575

576+
return context;
577+
}
578+
579+
ngx_int_t StartNgxSpan(ngx_http_request_t* req) {
580+
if (!IsOtelEnabled(req)) {
581+
return NGX_DECLINED;
582+
}
583+
584+
if (req->internal) {
585+
return NGX_DECLINED;
586+
}
587+
588+
StartNgxTrace(req);
589+
566590
return NGX_DECLINED;
567591
}
568592

@@ -591,10 +615,7 @@ void AddScriptAttributes(
591615
}
592616
}
593617

594-
ngx_int_t FinishNgxSpan(ngx_http_request_t* req) {
595-
if (!IsOtelEnabled(req)) {
596-
return NGX_DECLINED;
597-
}
618+
void UpdateSpan(nostd::shared_ptr<opentelemetry::trace::Span> span, ngx_http_request_t* req, bool captureHeaders = false) {
598619

599620
TraceContext* context = GetTraceContext(req);
600621

@@ -603,31 +624,22 @@ ngx_int_t FinishNgxSpan(ngx_http_request_t* req) {
603624
}
604625

605626
auto span = context->request_span;
606-
const auto status_code = req->headers_out.status;
607-
span->SetAttribute("http.status_code", status_code);
608-
609-
if (status_code >= 500) {
610-
span->SetStatus(trace::StatusCode::kError);
611-
} else {
612-
span->SetStatus(trace::StatusCode::kOk);
613-
}
627+
UpdateSpan(span, req, true);
614628

615-
OtelNgxLocationConf* locConf = GetOtelLocationConf(req);
629+
if (context->inner_span) {
616630

617-
if (locConf->captureHeaders) {
618-
OtelCaptureHeaders(span, ngx_string("http.response.header."), &req->headers_out.headers,
619-
#if (NGX_PCRE)
620-
locConf->sensitiveHeaderNames, locConf->sensitiveHeaderValues,
621-
#endif
622-
{});
623-
}
631+
const bool hasUpstream = req->upstream != nullptr;
632+
if (hasUpstream) {
633+
context->inner_span->SetAttribute("span.kind", static_cast<int>(trace::SpanKind::kClient));
634+
}
624635

625-
AddScriptAttributes(span.get(), GetOtelMainConf(req)->scriptAttributes, req);
626-
AddScriptAttributes(span.get(), locConf->customAttributes, req);
636+
UpdateSpan(context->inner_span, req);
627637

628-
span->UpdateName(GetOperationName(req));
638+
context->inner_span->End();
639+
}
629640

630641
span->End();
642+
631643
return NGX_DECLINED;
632644
}
633645

@@ -642,7 +654,7 @@ static ngx_int_t InitModule(ngx_conf_t* conf) {
642654

643655
const PhaseHandler handlers[] = {
644656
{NGX_HTTP_REWRITE_PHASE, StartNgxSpan},
645-
{NGX_HTTP_LOG_PHASE, FinishNgxSpan},
657+
{NGX_HTTP_LOG_PHASE, FinishNgxSpan}
646658
};
647659

648660
for (const PhaseHandler& ph : handlers) {
@@ -1292,6 +1304,7 @@ static std::unique_ptr<sdktrace::SpanExporter> CreateExporter(const OtelNgxAgent
12921304

12931305
static std::unique_ptr<sdktrace::SpanProcessor>
12941306
CreateProcessor(const OtelNgxAgentConfig* conf, std::unique_ptr<sdktrace::SpanExporter> exporter) {
1307+
12951308
if (conf->processor.type == OtelProcessorBatch) {
12961309
sdktrace::BatchSpanProcessorOptions opts;
12971310
opts.max_queue_size = conf->processor.batch.maxQueueSize;
@@ -1300,11 +1313,11 @@ CreateProcessor(const OtelNgxAgentConfig* conf, std::unique_ptr<sdktrace::SpanEx
13001313
opts.max_export_batch_size = conf->processor.batch.maxExportBatchSize;
13011314

13021315
return std::unique_ptr<sdktrace::SpanProcessor>(
1303-
new sdktrace::BatchSpanProcessor(std::move(exporter), opts));
1316+
new PostBatchSpanProcessor(std::move(exporter), opts));
13041317
}
13051318

13061319
return std::unique_ptr<sdktrace::SpanProcessor>(
1307-
new sdktrace::SimpleSpanProcessor(std::move(exporter)));
1320+
new PostSpanProcessor(std::move(exporter)));
13081321
}
13091322

13101323
static std::unique_ptr<sdktrace::Sampler> CreateSampler(const OtelNgxAgentConfig* conf) {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "post_batch_span_processor.h"
2+
#include <opentelemetry/exporters/otlp/otlp_recordable.h>
3+
4+
void PostBatchSpanProcessor::OnEnd(std::unique_ptr<opentelemetry::sdk::trace::Recordable> &&span) noexcept
5+
{
6+
ProxyRecordable* proxy = static_cast<ProxyRecordable*>(span.get());
7+
BatchSpanProcessor::OnEnd(std::move(proxy->GetRealRecordable()));
8+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#ifndef OPENTELEMETRY_NGINX_POST_BATCH_SPAN_PROCESSOR_H
2+
#define OPENTELEMETRY_NGINX_POST_BATCH_SPAN_PROCESSOR_H
3+
4+
#include "proxy_recordable.h"
5+
#include <opentelemetry/nostd/shared_ptr.h>
6+
#include <opentelemetry/sdk/trace/batch_span_processor.h>
7+
8+
namespace trace = opentelemetry::trace;
9+
namespace nostd = opentelemetry::nostd;
10+
namespace sdktrace = opentelemetry::sdk::trace;
11+
12+
class PostBatchSpanProcessor : public sdktrace::BatchSpanProcessor {
13+
public:
14+
15+
explicit PostBatchSpanProcessor(std::unique_ptr<sdktrace::SpanExporter> &&exporter,
16+
const sdktrace::BatchSpanProcessorOptions &options) noexcept
17+
: BatchSpanProcessor(std::move(exporter), options)
18+
{}
19+
20+
void OnEnd(std::unique_ptr<opentelemetry::sdk::trace::Recordable> &&span) noexcept override;
21+
22+
std::unique_ptr<opentelemetry::sdk::trace::Recordable> MakeRecordable() noexcept override {
23+
return std::unique_ptr<ProxyRecordable>(new ProxyRecordable());
24+
}
25+
};
26+
27+
#endif //OPENTELEMETRY_NGINX_POST_BATCH_SPAN_PROCESSOR_H
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "post_span_processor.h"
2+
3+
void PostSpanProcessor::OnEnd(std::unique_ptr<opentelemetry::sdk::trace::Recordable> &&span) noexcept
4+
{
5+
ProxyRecordable* proxy = static_cast<ProxyRecordable*>(span.get());
6+
SimpleSpanProcessor::OnEnd(std::move(proxy->GetRealRecordable()));
7+
}
8+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#ifndef OPENTELEMETRY_NGINX_POST_SPAN_PROCESSOR_H
2+
#define OPENTELEMETRY_NGINX_POST_SPAN_PROCESSOR_H
3+
4+
#include <opentelemetry/nostd/shared_ptr.h>
5+
#include <opentelemetry/sdk/trace/simple_processor.h>
6+
#include "proxy_recordable.h"
7+
8+
namespace trace = opentelemetry::trace;
9+
namespace nostd = opentelemetry::nostd;
10+
namespace sdktrace = opentelemetry::sdk::trace;
11+
12+
class PostSpanProcessor : public sdktrace::SimpleSpanProcessor {
13+
public:
14+
15+
explicit PostSpanProcessor(std::unique_ptr<sdktrace::SpanExporter> &&exporter) noexcept
16+
: SimpleSpanProcessor(std::move(exporter))
17+
{}
18+
19+
void OnEnd(std::unique_ptr<opentelemetry::sdk::trace::Recordable> &&span) noexcept override;
20+
21+
std::unique_ptr<opentelemetry::sdk::trace::Recordable> MakeRecordable() noexcept override {
22+
return std::unique_ptr<ProxyRecordable>(new ProxyRecordable());
23+
}
24+
};
25+
26+
#endif //OPENTELEMETRY_NGINX_POST_SPAN_PROCESSOR_H

0 commit comments

Comments
 (0)