From ff12746ad7d815f6bf3ea3fde148e545378ffa5a Mon Sep 17 00:00:00 2001
From: Pavel Pautov
Date: Mon, 2 Dec 2024 21:07:38 -0800
Subject: [PATCH] Support sending custom headers to export endpoint (fix #62).
The headers are configured by "header" directive in "otel_exporter" block, e.g.
otel_exporter {
endpoint localhost:4317;
header X-API-Token "token value";
}
---
src/http_module.cpp | 36 +++++++++++++++++++++++++++++++++++-
src/trace_service_client.hpp | 23 ++++++++++++++++++++++-
2 files changed, 57 insertions(+), 2 deletions(-)
diff --git a/src/http_module.cpp b/src/http_module.cpp
index 93898b1c..ef77ffbd 100644
--- a/src/http_module.cpp
+++ b/src/http_module.cpp
@@ -30,6 +30,7 @@ struct MainConf : MainConfBase {
std::map resourceAttrs;
bool ssl;
std::string trustedCert;
+ Target::HeaderVec headers;
};
struct SpanAttr {
@@ -49,6 +50,7 @@ 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);
char* setTrustedCertificate(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
+char* addExporterHeader(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
namespace Propagation {
@@ -120,6 +122,10 @@ ngx_command_t gExporterCommands[] = {
NGX_CONF_TAKE1,
setTrustedCertificate },
+ { ngx_string("header"),
+ NGX_CONF_TAKE2,
+ addExporterHeader },
+
{ ngx_string("interval"),
NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
@@ -580,6 +586,7 @@ ngx_int_t initWorkerProcess(ngx_cycle_t* cycle)
target.endpoint = std::string(toStrView(mcf->endpoint));
target.ssl = mcf->ssl;
target.trustedCert = mcf->trustedCert;
+ target.headers = mcf->headers;
gExporter.reset(new BatchExporter(
target,
@@ -651,7 +658,7 @@ char* setExporter(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
continue;
}
- if (cf->args->nelts != 2) {
+ if (cf->args->nelts != static_cast(ffs(cmd->type))) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid number of arguments in \"%V\" "
"directive of \"otel_exporter\"", name);
@@ -747,6 +754,33 @@ char* setTrustedCertificate(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
return NGX_CONF_OK;
}
+char* addExporterHeader(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
+{
+ auto args = (ngx_str_t*)cf->args->elts;
+
+ // don't force on users lower case name requirement of gRPC
+ ngx_strlow(args[1].data, args[1].data, args[1].len);
+
+ try {
+ // validate header here to avoid runtime assert failure in gRPC
+ auto name = toStrView(args[1]);
+ if (!Target::validateHeaderName(name)) {
+ return (char*)"has invalid header name";
+ }
+ auto value = toStrView(args[2]);
+ if (!Target::validateHeaderValue(value)) {
+ return (char*)"has invalid header value";
+ }
+
+ getMainConf(cf)->headers.emplace_back(name, value);
+ } 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 cln = ngx_pool_cleanup_add(cf->pool, sizeof(MainConf));
diff --git a/src/trace_service_client.hpp b/src/trace_service_client.hpp
index 485143c8..4ed92e79 100644
--- a/src/trace_service_client.hpp
+++ b/src/trace_service_client.hpp
@@ -9,9 +9,24 @@
namespace otel_proto_trace = opentelemetry::proto::collector::trace::v1;
struct Target {
+ typedef std::vector> HeaderVec;
+
std::string endpoint;
bool ssl;
std::string trustedCert;
+ HeaderVec headers;
+
+ static bool validateHeaderName(StrView name)
+ {
+ return grpc_header_key_is_legal(
+ grpc_slice_from_static_buffer(name.data(), name.size()));
+ }
+
+ static bool validateHeaderValue(StrView value)
+ {
+ return grpc_header_nonbin_value_is_legal(
+ grpc_slice_from_static_buffer(value.data(), value.size()));
+ }
};
class TraceServiceClient {
@@ -23,7 +38,7 @@ class TraceServiceClient {
typedef std::function
ResponseCb;
- TraceServiceClient(const Target& target)
+ TraceServiceClient(const Target& target) : headers(target.headers)
{
std::shared_ptr creds;
if (target.ssl) {
@@ -44,6 +59,10 @@ class TraceServiceClient {
{
std::unique_ptr call{new ActiveCall{}};
+ for (auto& header : headers) {
+ call->context.AddMetadata(header.first, header.second);
+ }
+
call->request = std::move(req);
call->cb = std::move(cb);
@@ -113,6 +132,8 @@ class TraceServiceClient {
ResponseCb cb;
};
+ Target::HeaderVec headers;
+
std::unique_ptr stub;
grpc::CompletionQueue queue;