Support custom span name and attributes.

This commit is contained in:
Pavel Pautov 2022-12-13 21:32:53 -08:00 committed by dplotnikov-f5
parent 3ad853ea59
commit 85a3fcb38c
4 changed files with 119 additions and 4 deletions

View file

@ -101,6 +101,14 @@ The argument is a “complex value”, which should result in `on`/`off` or `1`/
Defines how to propagate traceparent/tracestate headers. `extract` uses existing trace context from request. `inject` adds new context to request, rewriting existing headers if any. `propagate` updates existing context (i.e. combines `extract` and `inject`). `ignore` skips context headers processing. Default is `ignore`. Defines how to propagate traceparent/tracestate headers. `extract` uses existing trace context from request. `inject` adds new context to request, rewriting existing headers if any. `propagate` updates existing context (i.e. combines `extract` and `inject`). `ignore` skips context headers processing. Default is `ignore`.
**`otel_span_name`** `name;`
Default is requests location name.
**`otel_span_attr`** `name “$var”;`
If name starts with `http.(request|response).header.` the type of added attribute will be `string[]` to match semantic conventions (i.e. header value will be represented as a single element array). Otherwise, the attribute type will be `string`.
#### Available in `http` context #### Available in `http` context
**`otel_exporter`**`;` **`otel_exporter`**`;`

View file

@ -70,6 +70,16 @@ public:
add(key)->mutable_value()->set_int_value(value); add(key)->mutable_value()->set_int_value(value);
} }
void addArray(StrView key, StrView value)
{
auto elems = add(key)->mutable_value()->mutable_array_value()->
mutable_values();
auto elem = elems->size() > 0 ? elems->Mutable(0) : elems->Add();
elem->mutable_string_value()->assign(value.data(), value.size());
}
void setError() void setError()
{ {
span->mutable_status()->set_code( span->mutable_status()->set_code(

View file

@ -26,12 +26,21 @@ struct MainConf {
ngx_str_t serviceName; ngx_str_t serviceName;
}; };
struct SpanAttr {
ngx_str_t name;
ngx_http_complex_value_t value;
};
struct LocationConf { struct LocationConf {
ngx_http_complex_value_t* trace; ngx_http_complex_value_t* trace;
ngx_uint_t traceContext; ngx_uint_t traceContext;
ngx_http_complex_value_t* spanName;
ngx_array_t spanAttrs;
}; };
char* setExporter(ngx_conf_t* cf, ngx_command_t* cmd, void* conf); char* setExporter(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
char* addSpanAttr(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
namespace Propagation { namespace Propagation {
@ -74,6 +83,17 @@ ngx_command_t gCommands[] = {
offsetof(LocationConf, traceContext), offsetof(LocationConf, traceContext),
&Propagation::Types }, &Propagation::Types },
{ ngx_string("otel_span_name"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_set_complex_value_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(LocationConf, spanName) },
{ ngx_string("otel_span_attr"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
addSpanAttr,
NGX_HTTP_LOC_CONF_OFFSET },
ngx_null_command ngx_null_command
}; };
@ -369,6 +389,48 @@ void addDefaultAttrs(BatchExporter::Span& span, ngx_http_request_t* r)
span.add("net.sock.peer.port", ngx_inet_get_port(r->connection->sockaddr)); span.add("net.sock.peer.port", ngx_inet_get_port(r->connection->sockaddr));
} }
StrView getSpanName(ngx_http_request_t* r)
{
auto lcf = getLocationConf(r);
if (lcf->spanName) {
ngx_str_t result;
if (ngx_http_complex_value(r, lcf->spanName, &result) != NGX_OK) {
throw std::runtime_error("failed to compute complex value");
}
return toStrView(result);
} else {
auto clcf = (ngx_http_core_loc_conf_t*)
ngx_http_get_module_loc_conf(r, ngx_http_core_module);
return toStrView(clcf->name);
}
}
void addCustomAttrs(BatchExporter::Span& span, ngx_http_request_t* r)
{
auto lcf = getLocationConf(r);
auto attrs = (SpanAttr*)lcf->spanAttrs.elts;
for (ngx_uint_t i = 0; i < lcf->spanAttrs.nelts; i++) {
ngx_str_t value;
if (ngx_http_complex_value(r, &attrs[i].value, &value) != NGX_OK) {
throw std::runtime_error("failed to compute complex value");
}
StrView name = toStrView(attrs[i].name);
if (startsWith(name, "http.request.header.") ||
startsWith(name, "http.response.header."))
{
//TODO: remove this once headers are supported natively
span.addArray(name, toStrView(value));
} else {
span.add(name, toStrView(value));
}
}
}
ngx_int_t onRequestEnd(ngx_http_request_t* r) ngx_int_t onRequestEnd(ngx_http_request_t* r)
{ {
auto ctx = getOtelCtx(r); auto ctx = getOtelCtx(r);
@ -376,9 +438,6 @@ ngx_int_t onRequestEnd(ngx_http_request_t* r)
return NGX_DECLINED; return NGX_DECLINED;
} }
auto clcf = (ngx_http_core_loc_conf_t*)ngx_http_get_module_loc_conf(
r, ngx_http_core_module);
auto now = ngx_timeofday(); auto now = ngx_timeofday();
auto toNanoSec = [](time_t sec, ngx_msec_t msec) -> uint64_t { auto toNanoSec = [](time_t sec, ngx_msec_t msec) -> uint64_t {
@ -387,12 +446,13 @@ ngx_int_t onRequestEnd(ngx_http_request_t* r)
try { try {
BatchExporter::SpanInfo info{ BatchExporter::SpanInfo info{
toStrView(clcf->name), ctx->current, ctx->parent.spanId, getSpanName(r), ctx->current, ctx->parent.spanId,
toNanoSec(r->start_sec, r->start_msec), toNanoSec(r->start_sec, r->start_msec),
toNanoSec(now->sec, now->msec)}; toNanoSec(now->sec, now->msec)};
bool ok = gExporter->add(info, [r](BatchExporter::Span& span) { bool ok = gExporter->add(info, [r](BatchExporter::Span& span) {
addDefaultAttrs(span, r); addDefaultAttrs(span, r);
addCustomAttrs(span, r);
}); });
if (!ok) { if (!ok) {
@ -589,6 +649,32 @@ char* initMainConf(ngx_conf_t* cf, void* conf)
return NGX_CONF_OK; return NGX_CONF_OK;
} }
char* addSpanAttr(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
{
auto lcf = (LocationConf*)conf;
if (lcf->spanAttrs.elts == NULL && ngx_array_init(&lcf->spanAttrs,
cf->pool, 4, sizeof(SpanAttr)) != NGX_OK) {
return (char*)NGX_CONF_ERROR;
}
auto attr = (SpanAttr*)ngx_array_push(&lcf->spanAttrs);
if (attr == NULL) {
return (char*)NGX_CONF_ERROR;
}
auto args = (ngx_str_t*)cf->args->elts;
attr->name = args[1];
ngx_http_compile_complex_value_t ccv = { cf, &args[2], &attr->value };
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return (char*)NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
template <class Id> template <class Id>
ngx_int_t hexIdVar(ngx_http_request_t* r, ngx_http_variable_value_t* v, ngx_int_t hexIdVar(ngx_http_request_t* r, ngx_http_variable_value_t* v,
uintptr_t data) uintptr_t data)
@ -677,6 +763,7 @@ void* createLocationConf(ngx_conf_t* cf)
conf->trace = (ngx_http_complex_value_t*)NGX_CONF_UNSET_PTR; conf->trace = (ngx_http_complex_value_t*)NGX_CONF_UNSET_PTR;
conf->traceContext = NGX_CONF_UNSET_UINT; conf->traceContext = NGX_CONF_UNSET_UINT;
conf->spanName = (ngx_http_complex_value_t*)NGX_CONF_UNSET_PTR;
return conf; return conf;
} }
@ -688,6 +775,11 @@ char* mergeLocationConf(ngx_conf_t* cf, void* parent, void* child)
ngx_conf_merge_ptr_value(conf->trace, prev->trace, NULL); ngx_conf_merge_ptr_value(conf->trace, prev->trace, NULL);
ngx_conf_merge_uint_value(conf->traceContext, prev->traceContext, 0); ngx_conf_merge_uint_value(conf->traceContext, prev->traceContext, 0);
ngx_conf_merge_ptr_value(conf->spanName, prev->spanName, NULL);
if (conf->spanAttrs.elts == NULL) {
conf->spanAttrs = prev->spanAttrs;
}
return NGX_CONF_OK; return NGX_CONF_OK;
} }

View file

@ -3,3 +3,8 @@
#include <opentelemetry/nostd/string_view.h> #include <opentelemetry/nostd/string_view.h>
typedef opentelemetry::nostd::string_view StrView; typedef opentelemetry::nostd::string_view StrView;
inline bool startsWith(StrView str, StrView prefix)
{
return str.substr(0, prefix.size()) == prefix;
}