Add variables.

This commit is contained in:
Pavel Pautov 2022-12-12 23:05:18 -08:00 committed by dplotnikov-f5
parent 20f365b3c1
commit 3ad853ea59
2 changed files with 177 additions and 15 deletions

View file

@ -51,7 +51,7 @@ Dumping all the requests could be useful even in non-distributed environment.
http {
server {
location / {
otel_trace on;
otel_trace $otel_parent_sampled;
otel_trace_context propagate;
proxy_pass http://backend;
@ -60,6 +60,33 @@ http {
}
```
### Ratio-based Tracing
```nginx
http {
# trace 10% of requests
split_clients $otel_trace_id $ratio_sampler {
10% on;
* off;
}
# or we can trace 10% of user sessions
split_clients $cookie_sessionid $session_sampler {
10% on;
* off;
}
server {
location / {
otel_trace $ratio_sampler;
otel_trace_context inject;
proxy_pass http://backend;
}
}
}
```
## How to Use
### Directives
@ -111,6 +138,44 @@ Maximum number of spans to be sent in one batch per worker. Detault is 512.
Maximum number of pending batches per worker, over the limit spans are dropped. Default is 4.
### Variables
`$otel_trace_id` - trace id.
`$otel_span_id` - current span id.
`$otel_parent_id` - parent span id.
`$otel_parent_sampled` - `sampled` flag of parent span, `1`/`0`.
### Default span [attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md)
`http.method`
`http.target`
`http.route`
`http.scheme`
`http.flavor`
`http.user_agent`
`http.request_content_length`
`http.response_content_length`
`http.status_code`
`net.host.name`
`net.host.port`
`net.sock.peer.addr`
`net.sock.peer.port`
## License
[Apache License, Version 2.0](https://github.com/nginxinc/nginx-otel/blob/main/LICENSE)

View file

@ -118,6 +118,11 @@ ngx_str_t toNgxStr(StrView str)
return ngx_str_t{str.size(), (u_char*)str.data()};
}
LocationConf* getLocationConf(ngx_http_request_t* r)
{
return (LocationConf*)ngx_http_get_module_loc_conf(r, gHttpModule);
}
OtelCtx* getOtelCtx(ngx_http_request_t* r)
{
return (OtelCtx*)ngx_http_get_module_ctx(r, gHttpModule);
@ -235,6 +240,28 @@ ngx_int_t inject(ngx_http_request_t* r, const TraceContext& tc)
return setHeader(r, "tracestate", tc.state);
}
OtelCtx* ensureOtelCtx(ngx_http_request_t* r)
{
auto ctx = getOtelCtx(r);
if (ctx) {
return ctx;
}
ctx = createOtelCtx(r);
if (!ctx) {
return NULL;
}
auto lcf = getLocationConf(r);
if (lcf->traceContext & Propagation::Extract) {
ctx->parent = extract(r);
}
ctx->current = TraceContext::generate(false, ctx->parent);
return ctx;
}
ngx_int_t onRequestStart(ngx_http_request_t* r)
{
// don't let internal redirects to override sampling decision
@ -244,35 +271,26 @@ ngx_int_t onRequestStart(ngx_http_request_t* r)
bool sampled = false;
auto lcf = (LocationConf*)ngx_http_get_module_loc_conf(r, gHttpModule);
auto lcf = getLocationConf(r);
if (lcf->trace != NULL) {
ngx_str_t trace;
if (ngx_http_complex_value(r, lcf->trace, &trace) != NGX_OK) {
return NGX_ERROR;
}
sampled = toStrView(trace) == "on";
sampled = toStrView(trace) == "on" || toStrView(trace) == "1";
}
if (!lcf->traceContext && !sampled) {
return NGX_DECLINED;
}
auto ctx = getOtelCtx(r);
if (ctx) {
return NGX_DECLINED;
}
ctx = createOtelCtx(r);
auto ctx = ensureOtelCtx(r);
if (!ctx) {
return NGX_ERROR;
}
if (lcf->traceContext & Propagation::Extract) {
ctx->parent = extract(r);
}
ctx->current = TraceContext::generate(sampled, ctx->parent);
ctx->current.sampled = sampled;
ngx_int_t rc = NGX_OK;
@ -571,6 +589,85 @@ char* initMainConf(ngx_conf_t* cf, void* conf)
return NGX_CONF_OK;
}
template <class Id>
ngx_int_t hexIdVar(ngx_http_request_t* r, ngx_http_variable_value_t* v,
uintptr_t data)
{
auto ctx = ensureOtelCtx(r);
if (!ctx) {
return NGX_ERROR;
}
auto id = (Id*)((char*)ctx + data);
if (id->IsValid()) {
auto size = id->Id().size() * 2;
auto buf = (char*)ngx_pnalloc(r->pool, size);
if (buf == NULL) {
return NGX_ERROR;
}
id->ToLowerBase16({buf, size});
v->len = size;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char*)buf;
} else {
v->not_found = 1;
}
return NGX_OK;
}
ngx_int_t parentSampledVar(ngx_http_request_t* r, ngx_http_variable_value_t* v,
uintptr_t data)
{
auto ctx = ensureOtelCtx(r);
if (!ctx) {
return NGX_ERROR;
}
v->len = 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char*)(ctx->parent.sampled ? "1" : "0");
return NGX_OK;
}
ngx_int_t addVariables(ngx_conf_t* cf)
{
using namespace opentelemetry::trace;
ngx_http_variable_t vars[] = {
{ ngx_string("otel_trace_id"), NULL, hexIdVar<TraceId>,
offsetof(OtelCtx, current.traceId) },
{ ngx_string("otel_span_id"), NULL, hexIdVar<SpanId>,
offsetof(OtelCtx, current.spanId) },
{ ngx_string("otel_parent_id"), NULL, hexIdVar<SpanId>,
offsetof(OtelCtx, parent.spanId) },
{ ngx_string("otel_parent_sampled"), NULL, parentSampledVar }
};
for (auto& v : vars) {
auto var = ngx_http_add_variable(cf, &v.name, 0);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v.get_handler;
var->data = v.data;
}
return NGX_OK;
}
void* createLocationConf(ngx_conf_t* cf)
{
auto conf = (LocationConf*)ngx_pcalloc(cf->pool, sizeof(LocationConf));
@ -596,7 +693,7 @@ char* mergeLocationConf(ngx_conf_t* cf, void* parent, void* child)
}
ngx_http_module_t gHttpModuleCtx = {
NULL, /* preconfiguration */
addVariables, /* preconfiguration */
initModule, /* postconfiguration */
createMainConf, /* create main configuration */