Skip to content

Commit c80826c

Browse files
seemkVladimir Kuznichenkovkuzaxak
authored
Add B3 multi header propagation support (open-telemetry#495)
* Add support for B3Multi headers propagation 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 * add tests for b3 multi propagation --------- Co-authored-by: Vladimir Kuznichenkov <[email protected]> Co-authored-by: Vladimir Kuznichenkov <[email protected]>
1 parent e1ef658 commit c80826c

File tree

8 files changed

+234
-22
lines changed

8 files changed

+234
-22
lines changed

instrumentation/nginx/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ be started. The default propagator is W3C.
169169
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.
170170

171171
- **required**: `false`
172-
- **syntax**: `opentelemetry_propagate` or `opentelemetry_propagate b3`
172+
- **syntax**: `opentelemetry_propagate` or `opentelemetry_propagate b3` or `opentelemetry_propagate b3multi`
173173
- **block**: `http`, `server`, `location`
174174

175175
### `opentelemetry_capture_headers`
@@ -255,6 +255,7 @@ The following nginx variables are set by the instrumentation:
255255

256256
- `opentelemetry_context_traceparent` - [W3C trace context](https://www.w3.org/TR/trace-context/#trace-context-http-headers-format), e.g.: `00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01`
257257
- `opentelemetry_context_b3` - Trace context in the [B3 format](https://github.com/openzipkin/b3-propagation#single-header). Only set when using `opentelemetry_propagate b3`.
258+
- `opentelemetry_sampled` - does current Span records information, "1" or "0"
258259
- `opentelemetry_trace_id` - Trace Id of the current span
259260
- `opentelemetry_span_id` - Span Id of the current span
260261

instrumentation/nginx/src/otel_ngx_module.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ OtelGetTraceId(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t
143143
static ngx_int_t
144144
OtelGetSpanId(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t data);
145145

146+
static ngx_int_t
147+
OtelGetSampled(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t data);
148+
146149
static ngx_http_variable_t otel_ngx_variables[] = {
147150
{
148151
ngx_string("otel_ctx"),
@@ -176,6 +179,14 @@ static ngx_http_variable_t otel_ngx_variables[] = {
176179
NGX_HTTP_VAR_NOCACHEABLE | NGX_HTTP_VAR_NOHASH,
177180
0,
178181
},
182+
{
183+
ngx_string("opentelemetry_sampled"),
184+
nullptr,
185+
OtelGetSampled,
186+
0,
187+
NGX_HTTP_VAR_NOCACHEABLE | NGX_HTTP_VAR_NOHASH,
188+
0,
189+
},
179190
ngx_http_null_variable,
180191
};
181192

@@ -224,6 +235,46 @@ nostd::string_view WithoutOtelVarPrefix(ngx_str_t value) {
224235
return {(const char*)value.data + prefixLength, value.len - prefixLength};
225236
}
226237

238+
static ngx_int_t
239+
OtelGetSampled(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t data) {
240+
(void)data;
241+
242+
if (!IsOtelEnabled(req)) {
243+
v->valid = 0;
244+
v->not_found = 1;
245+
return NGX_OK;
246+
}
247+
248+
TraceContext* traceContext = GetTraceContext(req);
249+
250+
if (traceContext == nullptr || !traceContext->request_span) {
251+
ngx_log_error(
252+
NGX_LOG_ERR, req->connection->log, 0,
253+
"Unable to get trace context when getting span id");
254+
return NGX_OK;
255+
}
256+
257+
trace::SpanContext spanContext = traceContext->request_span->GetContext();
258+
259+
if (spanContext.IsValid()) {
260+
u_char* isSampled = spanContext.trace_flags().IsSampled() ? (u_char*) "1" : (u_char*) "0";
261+
262+
v->len = 1;
263+
v->valid = 1;
264+
v->no_cacheable = 1;
265+
v->not_found = 0;
266+
v->data = isSampled;
267+
} else {
268+
v->len = 0;
269+
v->valid = 0;
270+
v->no_cacheable = 1;
271+
v->not_found = 1;
272+
v->data = nullptr;
273+
}
274+
275+
return NGX_OK;
276+
}
277+
227278
static ngx_int_t
228279
OtelGetTraceContextVar(ngx_http_request_t* req, ngx_http_variable_value_t* v, uintptr_t data) {
229280
if (!IsOtelEnabled(req)) {
@@ -778,6 +829,17 @@ std::vector<HeaderPropagation> B3PropagationVars() {
778829
};
779830
}
780831

832+
std::vector<HeaderPropagation> B3MultiPropagationVars() {
833+
return {
834+
{"proxy_set_header", "x-b3-traceid", "$opentelemetry_trace_id"},
835+
{"proxy_set_header", "x-b3-spanid", "$opentelemetry_span_id"},
836+
{"proxy_set_header", "x-b3-sampled", "$opentelemetry_sampled"},
837+
{"fastcgi_param", "HTTP_B3_TRACEID", "$opentelemetry_trace_id"},
838+
{"fastcgi_param", "HTTP_B3_SPANID", "$opentelemetry_span_id"},
839+
{"fastcgi_param", "HTTP_B3_SAMPLED", "$opentelemetry_sampled"},
840+
};
841+
}
842+
781843
std::vector<HeaderPropagation> OtelPropagationVars() {
782844
return {
783845
{"proxy_set_header", "traceparent", "$opentelemetry_context_traceparent"},
@@ -798,6 +860,8 @@ char* OtelNgxSetPropagation(ngx_conf_t* conf, ngx_command_t*, void* locConf) {
798860

799861
if (propagationType == "b3") {
800862
locationConf->propagationType = TracePropagationB3;
863+
} else if (propagationType == "b3multi") {
864+
locationConf->propagationType = TracePropagationB3Multi;
801865
} else if (propagationType == "w3c") {
802866
locationConf->propagationType = TracePropagationW3C;
803867
} else {
@@ -811,6 +875,8 @@ char* OtelNgxSetPropagation(ngx_conf_t* conf, ngx_command_t*, void* locConf) {
811875
std::vector<HeaderPropagation> propagationVars;
812876
if (locationConf->propagationType == TracePropagationB3) {
813877
propagationVars = B3PropagationVars();
878+
} else if (locationConf->propagationType == TracePropagationB3Multi) {
879+
propagationVars = B3MultiPropagationVars();
814880
} else {
815881
propagationVars = OtelPropagationVars();
816882
}

instrumentation/nginx/src/propagate.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,6 @@ static bool FindHeader(ngx_http_request_t* req, nostd::string_view key, nostd::s
4242
}
4343

4444

45-
static bool HasHeader(ngx_http_request_t* req, nostd::string_view header) {
46-
nostd::string_view value;
47-
return FindHeader(req, header, &value);
48-
}
49-
5045
class TextMapCarrierNgx : public opentelemetry::context::propagation::TextMapCarrier
5146
{
5247
public:
@@ -79,12 +74,9 @@ opentelemetry::context::Context ExtractContext(OtelCarrier* carrier) {
7974
case TracePropagationW3C: {
8075
return OtelW3CPropagator().Extract(textMapCarrier, root);
8176
}
77+
case TracePropagationB3Multi:
8278
case TracePropagationB3: {
83-
if (HasHeader(carrier->req, "b3")) {
84-
return OtelB3Propagator().Extract(textMapCarrier, root);
85-
}
86-
87-
return OtelB3MultiPropagator().Extract(textMapCarrier, root);
79+
return OtelB3Propagator().Extract(textMapCarrier, root);
8880
}
8981
default:
9082
return root;
@@ -104,6 +96,10 @@ void InjectContext(OtelCarrier* carrier, opentelemetry::context::Context context
10496
OtelB3Propagator().Inject(textMapCarrier, context);
10597
break;
10698
}
99+
case TracePropagationB3Multi: {
100+
OtelB3MultiPropagator().Inject(textMapCarrier, context);
101+
break;
102+
}
107103
default:
108104
break;
109105
}

instrumentation/nginx/src/trace_context.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ enum TracePropagationType {
1515
TracePropagationUnset,
1616
TracePropagationW3C,
1717
TracePropagationB3,
18+
TracePropagationB3Multi,
1819
};
1920

2021
struct TraceContext {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
$b3_trace_id = $_SERVER["HTTP_B3_TRACEID"];
3+
$b3_span_id = $_SERVER["HTTP_B3_SPANID"];
4+
$b3_sampled = $_SERVER["HTTP_B3_SAMPLED"];
5+
6+
if (!preg_match("/^([0-9a-f]{32}|[0-9a-f]{16})$/", $b3_trace_id)) {
7+
throw new Exception("invalid or missing x-b3-traceid header");
8+
}
9+
10+
if (!preg_match("/^[0-9a-f]{16}$/", $b3_span_id)) {
11+
throw new Exception("invalid or missing x-b3-spanid header");
12+
}
13+
14+
if (!preg_match("/^[0-1]$/", $b3_sampled)) {
15+
throw new Exception("invalid or missing x-b3-sampled header");
16+
}
17+
18+
header("Content-Type: application/json");
19+
echo(json_encode(array(
20+
"x-b3-traceid" => $b3_trace_id,
21+
"x-b3-spanid" => $b3_span_id,
22+
"x-b3-sampled" => $b3_sampled
23+
)));
24+
?>

instrumentation/nginx/test/backend/simple_express/index.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
const express = require('express')
2-
const app = express()
3-
const port = 3500
1+
const express = require('express');
2+
const app = express();
3+
const port = 3500;
44

55
const traceparentRegex = /00-[0-9a-f]{32}-[0-9a-f]{16}-0[0-1]/;
66

@@ -22,6 +22,30 @@ app.get("/b3", (req, res) => {
2222
res.json({"b3": header});
2323
});
2424

25+
app.get("/b3multi", (req, res) => {
26+
let traceId = req.header("x-b3-traceid");
27+
let spanId = req.header("x-b3-spanid");
28+
let sampled = req.header("x-b3-sampled");
29+
30+
if (!/^([0-9a-f]{32}|[0-9a-f]{16})$/.test(traceId)) {
31+
throw "Missing x-b3-traceid header";
32+
}
33+
34+
if (!/^[0-9a-f]{16}$/.test(spanId)) {
35+
throw "Missing x-b3-spanid header";
36+
}
37+
38+
if (!["0", "1"].includes(sampled)) {
39+
throw "Missing x-b3-sampled header";
40+
}
41+
42+
res.json({
43+
"x-b3-traceid": traceId,
44+
"x-b3-spanid": spanId,
45+
"x-b3-sampled": sampled
46+
});
47+
});
48+
2549
app.get("/off", (req, res) => {
2650
if (req.header("traceparent") !== undefined) {
2751
throw "Found traceparent header, but expected none";

instrumentation/nginx/test/conf/nginx.conf

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ http {
4242
proxy_pass http://node-backend/b3;
4343
}
4444

45+
location = /b3multi {
46+
opentelemetry_operation_name test_b3multi;
47+
opentelemetry_propagate b3multi;
48+
proxy_pass http://node-backend/b3multi;
49+
}
50+
4551
location = /off {
4652
opentelemetry off;
4753
proxy_pass http://node-backend/off;
@@ -126,6 +132,22 @@ http {
126132
return 200 "";
127133
}
128134

135+
location = /b3multi.php {
136+
include /etc/nginx/fastcgi_params;
137+
root /var/www/html/php;
138+
opentelemetry_operation_name php_fpm_b3multi;
139+
opentelemetry_propagate b3multi;
140+
fastcgi_pass php-backend:9000;
141+
}
142+
143+
location = /b3.php {
144+
include /etc/nginx/fastcgi_params;
145+
root /var/www/html/php;
146+
opentelemetry_operation_name php_fpm_b3;
147+
opentelemetry_propagate b3;
148+
fastcgi_pass php-backend:9000;
149+
}
150+
129151
location ~ \.php$ {
130152
include /etc/nginx/fastcgi_params;
131153
root /var/www/html/php;

0 commit comments

Comments
 (0)