From bb17a8b175f926e8f3322c17e7a7062b168a1337 Mon Sep 17 00:00:00 2001 From: Pavel Pautov Date: Sat, 9 Nov 2024 15:51:33 -0800 Subject: [PATCH] Support custom resource attributes (fix #32). Now attributes can be set with "otel_resource_attr" directive, e.g. otel_resource_attr my.name "my value"; --- src/batch_exporter.hpp | 11 +++-- src/http_module.cpp | 93 ++++++++++++++++++++++++++++++++---------- 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/src/batch_exporter.hpp b/src/batch_exporter.hpp index d160d2ca..2432fe34 100644 --- a/src/batch_exporter.hpp +++ b/src/batch_exporter.hpp @@ -112,7 +112,8 @@ class BatchExporter { }; BatchExporter(StrView target, - size_t batchSize, size_t batchCount, StrView serviceName) : + size_t batchSize, size_t batchCount, + const std::map& resourceAttrs) : batchSize(batchSize), client(std::string(target)) { free.reserve(batchCount); @@ -120,9 +121,11 @@ class BatchExporter { free.emplace_back(); auto resourceSpans = free.back().add_resource_spans(); - auto attr = resourceSpans->mutable_resource()->add_attributes(); - attr->set_key("service.name"); - attr->mutable_value()->set_string_value(std::string(serviceName)); + for (auto& attr : resourceAttrs) { + auto kv = resourceSpans->mutable_resource()->add_attributes(); + kv->set_key(std::string(attr.first)); + kv->mutable_value()->set_string_value(std::string(attr.second)); + } auto scopeSpans = resourceSpans->add_scope_spans(); scopeSpans->mutable_scope()->set_name("nginx"); diff --git a/src/http_module.cpp b/src/http_module.cpp index 1ddffc30..5c73ef07 100644 --- a/src/http_module.cpp +++ b/src/http_module.cpp @@ -15,7 +15,7 @@ struct OtelCtx { TraceContext current; }; -struct MainConf { +struct MainConfBase { ngx_str_t endpoint; ngx_msec_t interval; size_t batchSize; @@ -24,6 +24,10 @@ struct MainConf { ngx_str_t serviceName; }; +struct MainConf : MainConfBase { + std::map resourceAttrs; +}; + struct SpanAttr { ngx_str_t name; ngx_http_complex_value_t value; @@ -38,6 +42,7 @@ struct LocationConf { }; char* setExporter(ngx_conf_t* cf, ngx_command_t* cmd, void* conf); +char* addResourceAttr(ngx_conf_t* cf, ngx_command_t* cmd, void* conf); char* addSpanAttr(ngx_conf_t* cf, ngx_command_t* cmd, void* conf); namespace Propagation { @@ -59,14 +64,17 @@ ngx_command_t gCommands[] = { { ngx_string("otel_exporter"), NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, - setExporter, - NGX_HTTP_MAIN_CONF_OFFSET }, + setExporter }, + + { ngx_string("otel_resource_attr"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2, + addResourceAttr }, { ngx_string("otel_service_name"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, - offsetof(MainConf, serviceName) }, + offsetof(MainConfBase, serviceName) }, { ngx_string("otel_trace"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, @@ -101,25 +109,25 @@ ngx_command_t gExporterCommands[] = { NGX_CONF_TAKE1, ngx_conf_set_str_slot, 0, - offsetof(MainConf, endpoint) }, + offsetof(MainConfBase, endpoint) }, { ngx_string("interval"), NGX_CONF_TAKE1, ngx_conf_set_msec_slot, 0, - offsetof(MainConf, interval) }, + offsetof(MainConfBase, interval) }, { ngx_string("batch_size"), NGX_CONF_TAKE1, ngx_conf_set_size_slot, 0, - offsetof(MainConf, batchSize) }, + offsetof(MainConfBase, batchSize) }, { ngx_string("batch_count"), NGX_CONF_TAKE1, ngx_conf_set_size_slot, 0, - offsetof(MainConf, batchCount) }, + offsetof(MainConfBase, batchCount) }, ngx_null_command }; @@ -136,6 +144,18 @@ ngx_str_t toNgxStr(StrView str) return ngx_str_t{str.size(), (u_char*)str.data()}; } +MainConf* getMainConf(ngx_conf_t* cf) +{ + return static_cast( + (MainConfBase*)ngx_http_conf_get_module_main_conf(cf, gHttpModule)); +} + +MainConf* getMainConf(ngx_cycle_t* cycle) +{ + return static_cast( + (MainConfBase*)ngx_http_cycle_get_module_main_conf(cycle, gHttpModule)); +} + LocationConf* getLocationConf(ngx_http_request_t* r) { return (LocationConf*)ngx_http_get_module_loc_conf(r, gHttpModule); @@ -527,8 +547,7 @@ ngx_int_t initModule(ngx_conf_t* cf) ngx_int_t initWorkerProcess(ngx_cycle_t* cycle) { - auto mcf = (MainConf*)ngx_http_cycle_get_module_main_conf( - cycle, gHttpModule); + auto mcf = getMainConf(cycle); // no 'http' or 'otel_exporter' blocks if (mcf == NULL || mcf->endpoint.len == 0) { @@ -540,7 +559,7 @@ ngx_int_t initWorkerProcess(ngx_cycle_t* cycle) toStrView(mcf->endpoint), mcf->batchSize, mcf->batchCount, - toStrView(mcf->serviceName))); + mcf->resourceAttrs)); } catch (const std::exception& e) { ngx_log_error(NGX_LOG_CRIT, cycle->log, 0, "OTel worker init error: %s", e.what()); @@ -561,8 +580,7 @@ ngx_int_t initWorkerProcess(ngx_cycle_t* cycle) "OTel flush error: %s", e.what()); } - auto mcf = (MainConf*)ngx_http_cycle_get_module_main_conf( - ngx_cycle, gHttpModule); + auto mcf = getMainConf((ngx_cycle_t*)ngx_cycle); ngx_add_timer(ev, mcf->interval); }; @@ -590,7 +608,7 @@ void exitWorkerProcess(ngx_cycle_t* cycle) char* setExporter(ngx_conf_t* cf, ngx_command_t* cmd, void* conf) { - auto mcf = (MainConf*)conf; + auto mcf = getMainConf(cf); if (mcf->endpoint.len) { return (char*)"is duplicate"; @@ -649,31 +667,64 @@ char* setExporter(ngx_conf_t* cf, ngx_command_t* cmd, void* conf) return NGX_CONF_OK; } +char* addResourceAttr(ngx_conf_t* cf, ngx_command_t* cmd, void* conf) +{ + auto mcf = getMainConf(cf); + + try { + auto args = (ngx_str_t*)cf->args->elts; + mcf->resourceAttrs[toStrView(args[1])] = toStrView(args[2]); + } catch (const std::exception& e) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "OTel: %s", e.what()); + return (char*)NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + void* createMainConf(ngx_conf_t* cf) { - auto mcf = (MainConf*)ngx_pcalloc(cf->pool, sizeof(MainConf)); - if (mcf == NULL) { + auto cln = ngx_pool_cleanup_add(cf->pool, sizeof(MainConf)); + if (cln == NULL) { + return NULL; + } + + MainConf* mcf; + try { + mcf = new (cln->data) MainConf{}; + } catch (const std::exception& e) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "OTel: %s", e.what()); return NULL; } + cln->handler = [](void* data) { + ((MainConf*)data)->~MainConf(); + }; + mcf->interval = NGX_CONF_UNSET_MSEC; mcf->batchSize = NGX_CONF_UNSET_SIZE; mcf->batchCount = NGX_CONF_UNSET_SIZE; - return mcf; + return static_cast(mcf); } char* initMainConf(ngx_conf_t* cf, void* conf) { auto mcf = (MainConf*)conf; - ngx_conf_init_msec_value(mcf->interval, 5000); ngx_conf_init_size_value(mcf->batchSize, 512); ngx_conf_init_size_value(mcf->batchCount, 4); - if (mcf->serviceName.data == NULL) { - mcf->serviceName = ngx_string("unknown_service:nginx"); + try { + if (mcf->serviceName.data == NULL) { + mcf->resourceAttrs.emplace("service.name", "unknown_service:nginx"); + } else { + mcf->resourceAttrs["service.name"] = toStrView(mcf->serviceName); + } + } catch (const std::exception& e) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "OTel: %s", e.what()); + return (char*)NGX_CONF_ERROR; } return NGX_CONF_OK; @@ -811,7 +862,7 @@ char* mergeLocationConf(ngx_conf_t* cf, void* parent, void* child) conf->spanAttrs = prev->spanAttrs; } - auto mcf = (MainConf*)ngx_http_conf_get_module_main_conf(cf, gHttpModule); + auto mcf = getMainConf(cf); if (mcf->endpoint.len == 0 && conf->trace) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,