Skip to content

Commit

Permalink
Add support for B3Multi headers propagation
Browse files Browse the repository at this point in the history
Some services expect [B3 Multi][1] headers as input information.
To support that we need to be able to Inject them into upstream requests. Lowercase headers used to be [compatible][2] with Istio Envoy.

Tests will be added as a separate commit later on.

Solving open-telemetry#36

[1]: https://github.com/openzipkin/b3-propagation#multiple-headers
[2]: open-telemetry/opentelemetry-go#765
  • Loading branch information
kuzaxak committed Sep 8, 2022
1 parent 786c3ad commit 1c339b5
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 1 deletion.
3 changes: 2 additions & 1 deletion instrumentation/nginx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ be started. The default propagator is W3C.
The same inheritance rules as [`proxy_set_header`](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header) apply, which means this directive is applied at the current configuration level if and only if there are no `proxy_set_header` directives defined on a lower level.

- **required**: `false`
- **syntax**: `opentelemetry_propagate` or `opentelemetry_propagate b3`
- **syntax**: `opentelemetry_propagate` or `opentelemetry_propagate b3` or `opentelemetry_propagate b3multi`
- **block**: `http`, `server`, `location`

### `opentelemetry_capture_headers`
Expand Down Expand Up @@ -232,6 +232,7 @@ The following nginx variables are set by the instrumentation:
context](https://www.w3.org/TR/trace-context/#trace-context-http-headers-format), e.g.: `00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01`
- `opentelemetry_context_b3` - Trace context in the [B3
format](https://github.com/openzipkin/b3-propagation#single-header). Only set when using `opentelemetry_propagate b3`.
- `opentelemetry_sampled` - does current Span records information, "1" or "0"
- `opentelemetry_trace_id` - Trace Id of the current span
- `opentelemetry_span_id` - Span Id of the current span

Expand Down
58 changes: 58 additions & 0 deletions instrumentation/nginx/src/otel_ngx_module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ OtelGetTraceId(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t
static ngx_int_t
OtelGetSpanId(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t data);

static ngx_int_t
OtelGetSampled(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t data);

static ngx_http_variable_t otel_ngx_variables[] = {
{
ngx_string("otel_ctx"),
Expand Down Expand Up @@ -169,6 +172,14 @@ static ngx_http_variable_t otel_ngx_variables[] = {
NGX_HTTP_VAR_NOCACHEABLE | NGX_HTTP_VAR_NOHASH,
0,
},
{
ngx_string("opentelemetry_sampled"),
nullptr,
OtelGetSampled,
0,
NGX_HTTP_VAR_NOCACHEABLE | NGX_HTTP_VAR_NOHASH,
0,
},
ngx_http_null_variable,
};

Expand Down Expand Up @@ -217,6 +228,38 @@ nostd::string_view WithoutOtelVarPrefix(ngx_str_t value) {
return {(const char*)value.data + prefixLength, value.len - prefixLength};
}

static ngx_int_t
OtelGetSampled(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t data) {
TraceContext* traceContext = GetTraceContext(req);

if (traceContext == nullptr || !traceContext->request_span) {
ngx_log_error(
NGX_LOG_ERR, req->connection->log, 0,
"Unable to get trace context when getting span id");
return NGX_OK;
}

trace::SpanContext spanContext = traceContext->request_span->GetContext();

if (spanContext.IsValid()) {
u_char* isSampled = spanContext.trace_flags().IsSampled() ? (u_char*) "1" : (u_char*) "0";

v->len = strlen((const char*)isSampled);
v->valid = 1;
v->no_cacheable = 1;
v->not_found = 0;
v->data = isSampled;
} else {
v->len = 0;
v->valid = 0;
v->no_cacheable = 1;
v->not_found = 1;
v->data = nullptr;
}

return NGX_OK;
}

static ngx_int_t
OtelGetTraceContextVar(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t data) {
if (!IsOtelEnabled(req)) {
Expand Down Expand Up @@ -758,6 +801,17 @@ std::vector<HeaderPropagation> B3PropagationVars() {
};
}

std::vector<HeaderPropagation> B3MultiPropagationVars() {
return {
{"proxy_set_header", "x-b3-traceid", "$opentelemetry_trace_id"},
{"proxy_set_header", "x-b3-spanid", "$opentelemetry_span_id"},
{"proxy_set_header", "x-b3-sampled", "$opentelemetry_sampled"},
{"fastcgi_param", "HTTP_B3_TRACEID", "$opentelemetry_trace_id"},
{"fastcgi_param", "HTTP_B3_SPANID", "$opentelemetry_span_id"},
{"fastcgi_param", "HTTP_B3_SAMPLED", "$opentelemetry_sampled"},
};
}

std::vector<HeaderPropagation> OtelPropagationVars() {
return {
{"proxy_set_header", "traceparent", "$opentelemetry_context_traceparent"},
Expand All @@ -778,6 +832,8 @@ char* OtelNgxSetPropagation(ngx_conf_t* conf, ngx_command_t*, void* locConf) {

if (propagationType == "b3") {
locationConf->propagationType = TracePropagationB3;
} else if (propagationType == "b3multi") {
locationConf->propagationType = TracePropagationB3Multi;
} else if (propagationType == "w3c") {
locationConf->propagationType = TracePropagationW3C;
} else {
Expand All @@ -791,6 +847,8 @@ char* OtelNgxSetPropagation(ngx_conf_t* conf, ngx_command_t*, void* locConf) {
std::vector<HeaderPropagation> propagationVars;
if (locationConf->propagationType == TracePropagationB3) {
propagationVars = B3PropagationVars();
} else if (locationConf->propagationType == TracePropagationB3Multi) {
propagationVars = B3MultiPropagationVars();
} else {
propagationVars = OtelPropagationVars();
}
Expand Down
7 changes: 7 additions & 0 deletions instrumentation/nginx/src/propagate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ opentelemetry::context::Context ExtractContext(OtelCarrier* carrier) {
case TracePropagationW3C: {
return OtelW3CPropagator().Extract(textMapCarrier, root);
}
case TracePropagationB3Multi: {
return OtelB3MultiPropagator().Extract(textMapCarrier, root);
}
case TracePropagationB3: {
if (HasHeader(carrier->req, "b3")) {
return OtelB3Propagator().Extract(textMapCarrier, root);
Expand All @@ -104,6 +107,10 @@ void InjectContext(OtelCarrier* carrier, opentelemetry::context::Context context
OtelB3Propagator().Inject(textMapCarrier, context);
break;
}
case TracePropagationB3Multi: {
OtelB3MultiPropagator().Inject(textMapCarrier, context);
break;
}
default:
break;
}
Expand Down
1 change: 1 addition & 0 deletions instrumentation/nginx/src/trace_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ enum TracePropagationType {
TracePropagationUnset,
TracePropagationW3C,
TracePropagationB3,
TracePropagationB3Multi,
};

struct TraceContext {
Expand Down

0 comments on commit 1c339b5

Please sign in to comment.