From b0e668fa0159c3662aa9689d8f4a0e29dbdec97f Mon Sep 17 00:00:00 2001 From: Jurek Elliesen <91612967+aff3npirat@users.noreply.github.com> Date: Sun, 17 May 2026 19:36:07 +0200 Subject: [PATCH 1/7] wip --- .gitignore | 1 + include/net/get_otel_tracer.h | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 include/net/get_otel_tracer.h diff --git a/.gitignore b/.gitignore index 6857461..e284228 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /deps /.idea .pkg.mutex +.vscode diff --git a/include/net/get_otel_tracer.h b/include/net/get_otel_tracer.h new file mode 100644 index 0000000..899113e --- /dev/null +++ b/include/net/get_otel_tracer.h @@ -0,0 +1,13 @@ +#pragma once + +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/trace/tracer.h" + + +namespace net { + +inline opentelemetry::nostd::shared_ptr get_otel_tracer() { + return opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("net") +} + +} // namespace net \ No newline at end of file From f5037cfcd08e7defa547c7ad6321b4eade699d0a Mon Sep 17 00:00:00 2001 From: Jurek Elliesen <91612967+aff3npirat@users.noreply.github.com> Date: Fri, 22 May 2026 17:38:31 +0200 Subject: [PATCH 2/7] wip --- .gitignore | 1 + include/net/get_otel_tracer.h | 12 ++++--- include/net/web_server/query_router.h | 46 +++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index e284228..48cd76d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /*build* /deps /.idea +/.vscode .pkg.mutex .vscode diff --git a/include/net/get_otel_tracer.h b/include/net/get_otel_tracer.h index 899113e..f89803d 100644 --- a/include/net/get_otel_tracer.h +++ b/include/net/get_otel_tracer.h @@ -1,13 +1,15 @@ #pragma once #include "opentelemetry/trace/provider.h" +#include "opentelemetry/trace/scope.h" +#include "opentelemetry/trace/span.h" #include "opentelemetry/trace/tracer.h" - namespace net { - -inline opentelemetry::nostd::shared_ptr get_otel_tracer() { - return opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("net") + +inline opentelemetry::nostd::shared_ptr +get_otel_tracer() { + return opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("net"); } -} // namespace net \ No newline at end of file +} // namespace net diff --git a/include/net/web_server/query_router.h b/include/net/web_server/query_router.h index b593d18..14ca986 100644 --- a/include/net/web_server/query_router.h +++ b/include/net/web_server/query_router.h @@ -17,10 +17,15 @@ #include "boost/json.hpp" #include "boost/url.hpp" +#include "opentelemetry/context/runtime_context.h" +#include "opentelemetry/sdk/resource/semantic_conventions.h" +#include "opentelemetry/trace/span_metadata.h" + #include "openapi/bad_request_exception.h" #include "net/bad_request_exception.h" #include "net/base64.h" +#include "net/get_otel_tracer.h" #include "net/not_found_exception.h" #include "net/too_many_exception.h" #include "net/web_server/content_encoding.h" @@ -287,10 +292,32 @@ struct query_router { void operator()(web_server::http_req_t req, web_server::http_res_cb_t cb, bool is_ssl) { - try { - auto const url = boost::urls::url_view{req.target()}; - auto const path = url.path(); + HttpTextMapCarrier> const carrier{ + req.headers}; + auto otel_propagator = opentelemetry::context::propagation:: + GlobalTextMapPropagator::GetGlobalPropagator(); + auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent(); + auto new_ctx = otel_propagator->Extract(carrier, current_ctx); + + auto const url = boost::urls::url_view{req.target()}; + auto const path = url.path(); + + auto span = get_otel_tracer()->StartSpan( + req.target(), + { + // TODO + {SemanticConventions::kHttpRequestMethod, req.method_string()}, + {SemanticConventions::kUrlPath, path}, + {SemanticConventions::kUrlQuery, url.query()}, + {SemanticConventions::kUrlScheme, "http"}, + }, + opentelemetry::trace::StartSpanOptions{ + .parent = opentelemetry::trace::GetSpan(new_ctx)->GetContext(), + .kind = opentelemetry::trace::SpanKind::kServer}); + auto const scope = get_otel_tracer()->WithActiveSpan(span); + + try { auto route = utl::find_if(routes_, [&](handler const& h) { return (h.method_ == "*" || h.method_ == req.method_string()) && path.starts_with(h.prefix_); @@ -310,11 +337,14 @@ struct query_router { decode_content(route_req); return exec_.exec( - [this, route, is_ssl, r = std::move(route_req)]() { + [this, route, is_ssl, span, r = std::move(route_req)]() { reply rep; using namespace boost::json; try { + span->AddEvent("Processing Request"); rep = route->request_handler_(r, is_ssl); + span->SetAttribute(SemanticConventions::kHttpResponseStatusCode, + rep.status_int()); } catch (openapi::bad_request_exception const& e) { rep = bad_request_response(r, serialize(value{{"error", e.what()}})); @@ -330,6 +360,8 @@ struct query_router { } catch (std::exception const& e) { rep = server_error_response( r, serialize(value{{"error", e.what()}})); + span->SetStatus(opentelemetry::trace::StatusCode::kError); + span->SetAttribute(SemanticConventions::kErrorType, rep.result()); } catch (...) { rep = server_error_response( r, serialize(value{{"error", "Unknown error"}})); @@ -354,9 +386,11 @@ struct query_router { }, std::move(cb)); } catch (...) { + constexpr auto const what = "malformed URI or request"; auto rep = reply{bad_request_response( - req, serialize( - boost::json::value{{"error", "malformed URI or request"}}))}; + req, serialize(boost::json::value{{"error", what}}))}; + Span->SetStatus(opentelemetry::trace::StatusCode::kError, what); + Span->SetAttribute(SemanticConventions::kErrorType, rep.result()); if (reply_hook_) { reply_hook_(rep); } From 869c674054681ba60316f433a49e893a26353ad9 Mon Sep 17 00:00:00 2001 From: Jurek Elliesen <91612967+aff3npirat@users.noreply.github.com> Date: Fri, 22 May 2026 18:21:27 +0200 Subject: [PATCH 3/7] wip --- include/net/web_server/query_router.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/web_server/query_router.h b/include/net/web_server/query_router.h index 14ca986..c0bb586 100644 --- a/include/net/web_server/query_router.h +++ b/include/net/web_server/query_router.h @@ -304,7 +304,7 @@ struct query_router { auto const path = url.path(); auto span = get_otel_tracer()->StartSpan( - req.target(), + req.method_string() + " " + req.target, { // TODO {SemanticConventions::kHttpRequestMethod, req.method_string()}, From 3359184b213eeac69d8fa01e5588bdbcac7f5ab8 Mon Sep 17 00:00:00 2001 From: Jurek Elliesen <91612967+aff3npirat@users.noreply.github.com> Date: Sun, 24 May 2026 13:45:25 +0200 Subject: [PATCH 4/7] wip --- include/net/web_server/query_router.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/net/web_server/query_router.h b/include/net/web_server/query_router.h index c0bb586..bd0f7b1 100644 --- a/include/net/web_server/query_router.h +++ b/include/net/web_server/query_router.h @@ -317,6 +317,11 @@ struct query_router { .kind = opentelemetry::trace::SpanKind::kServer}); auto const scope = get_otel_tracer()->WithActiveSpan(span); + for (auto const user_agent = req[boost::asio::http::field::user_agent]; + !user_agent.empty()) { + span->SetAttribute(SemanticConventions::kUserAgentOriginal, user_agent); + } + try { auto route = utl::find_if(routes_, [&](handler const& h) { return (h.method_ == "*" || h.method_ == req.method_string()) && From 067bd704a3c6878e02806826bee45967a314d90b Mon Sep 17 00:00:00 2001 From: Jurek Elliesen <91612967+aff3npirat@users.noreply.github.com> Date: Tue, 26 May 2026 15:44:04 +0200 Subject: [PATCH 5/7] Fix --- include/net/otel_text_map_carrier.h | 29 +++++++++++ include/net/web_server/query_router.h | 71 +++++++++++++++++++++------ 2 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 include/net/otel_text_map_carrier.h diff --git a/include/net/otel_text_map_carrier.h b/include/net/otel_text_map_carrier.h new file mode 100644 index 0000000..079544d --- /dev/null +++ b/include/net/otel_text_map_carrier.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "opentelemetry/context/propagation/text_map_propagator.h" + +namespace net { + +template +struct http_text_map_carrier + : public opentelemetry::context::propagation::TextMapCarrier { + explicit http_text_map_carrier(T& header) : header_{header} {} + + virtual opentelemetry::nostd::string_view Get( + opentelemetry::nostd::string_view key) const noexcept override { + return header_[key]; + } + + virtual void Set(opentelemetry::nostd::string_view key, + opentelemetry::nostd::string_view value) noexcept override { + if constexpr (!std::is_const_v) { + header_.set(key, value); + } + } + + T& header_; +}; + +} // namespace net \ No newline at end of file diff --git a/include/net/web_server/query_router.h b/include/net/web_server/query_router.h index bd0f7b1..3acfa6d 100644 --- a/include/net/web_server/query_router.h +++ b/include/net/web_server/query_router.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "utl/helpers/algorithm.h" @@ -17,8 +18,10 @@ #include "boost/json.hpp" #include "boost/url.hpp" +#include "opentelemetry/context/propagation/global_propagator.h" #include "opentelemetry/context/runtime_context.h" #include "opentelemetry/sdk/resource/semantic_conventions.h" +#include "opentelemetry/trace/context.h" #include "opentelemetry/trace/span_metadata.h" #include "openapi/bad_request_exception.h" @@ -27,6 +30,7 @@ #include "net/base64.h" #include "net/get_otel_tracer.h" #include "net/not_found_exception.h" +#include "net/otel_text_map_carrier.h" #include "net/too_many_exception.h" #include "net/web_server/content_encoding.h" #include "net/web_server/enable_cors.h" @@ -293,8 +297,10 @@ struct query_router { void operator()(web_server::http_req_t req, web_server::http_res_cb_t cb, bool is_ssl) { - HttpTextMapCarrier> const carrier{ - req.headers}; + namespace semconv = opentelemetry::sdk::resource::SemanticConventions; + + http_text_map_carrier const carrier{ + req.base()}; auto otel_propagator = opentelemetry::context::propagation:: GlobalTextMapPropagator::GetGlobalPropagator(); auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent(); @@ -304,24 +310,57 @@ struct query_router { auto const path = url.path(); auto span = get_otel_tracer()->StartSpan( - req.method_string() + " " + req.target, + // req.method_string() + " "sv + req.target(), + req.method_string(), { // TODO - {SemanticConventions::kHttpRequestMethod, req.method_string()}, - {SemanticConventions::kUrlPath, path}, - {SemanticConventions::kUrlQuery, url.query()}, - {SemanticConventions::kUrlScheme, "http"}, + {semconv::kHttpRequestMethod, req.method_string()}, + {semconv::kUrlPath, path}, + {semconv::kUrlQuery, url.query()}, + {semconv::kUrlScheme, "http"}, }, opentelemetry::trace::StartSpanOptions{ .parent = opentelemetry::trace::GetSpan(new_ctx)->GetContext(), .kind = opentelemetry::trace::SpanKind::kServer}); auto const scope = get_otel_tracer()->WithActiveSpan(span); - for (auto const user_agent = req[boost::asio::http::field::user_agent]; - !user_agent.empty()) { - span->SetAttribute(SemanticConventions::kUserAgentOriginal, user_agent); + if (auto const user_agent = req[boost::beast::http::field::user_agent]; + !user_agent.empty()) { + span->SetAttribute(semconv::kUserAgentOriginal, user_agent); } + // auto const set_otlp_status = [&](reply const& rep, bool is_error) { + // auto const field = + // is_error ? semconv::kErrorType : semconv::kHttpResponseStatusCode; + // std::visit(utl::overloaded{[&](web_server::string_res_t rep) { + // span->SetAttribute(field, + // rep.result_int()); + // }, + // [&](web_server::buffer_res_t rep) { + // span->SetAttribute(field, + // rep.result_int()); + // }, + // [&](web_server::file_res_t rep) { + // span->SetAttribute(field, + // rep.result_int()); + // }, + // [&](web_server::empty_res_t rep) { + // span->SetAttribute(field, + // rep.result_int()); + // }}, + // rep); + // }; + + auto const set_otlp_status = [&span](reply const& rep, bool is_error) { + auto const field = + is_error ? semconv::kErrorType : semconv::kHttpResponseStatusCode; + std::visit( + [&span, &field](auto& rep) { + span->SetAttribute(field, rep.result_int()); + }, + rep); + }; + try { auto route = utl::find_if(routes_, [&](handler const& h) { return (h.method_ == "*" || h.method_ == req.method_string()) && @@ -342,14 +381,14 @@ struct query_router { decode_content(route_req); return exec_.exec( - [this, route, is_ssl, span, r = std::move(route_req)]() { + [this, route, is_ssl, span, set_otlp_status, + r = std::move(route_req)]() { reply rep; using namespace boost::json; try { span->AddEvent("Processing Request"); rep = route->request_handler_(r, is_ssl); - span->SetAttribute(SemanticConventions::kHttpResponseStatusCode, - rep.status_int()); + set_otlp_status(rep, false); } catch (openapi::bad_request_exception const& e) { rep = bad_request_response(r, serialize(value{{"error", e.what()}})); @@ -366,7 +405,7 @@ struct query_router { rep = server_error_response( r, serialize(value{{"error", e.what()}})); span->SetStatus(opentelemetry::trace::StatusCode::kError); - span->SetAttribute(SemanticConventions::kErrorType, rep.result()); + set_otlp_status(rep, true); } catch (...) { rep = server_error_response( r, serialize(value{{"error", "Unknown error"}})); @@ -394,8 +433,8 @@ struct query_router { constexpr auto const what = "malformed URI or request"; auto rep = reply{bad_request_response( req, serialize(boost::json::value{{"error", what}}))}; - Span->SetStatus(opentelemetry::trace::StatusCode::kError, what); - Span->SetAttribute(SemanticConventions::kErrorType, rep.result()); + span->SetStatus(opentelemetry::trace::StatusCode::kError, what); + set_otlp_status(rep, true); if (reply_hook_) { reply_hook_(rep); } From 3a470a507aa31770604511fe6e02d6f622095d42 Mon Sep 17 00:00:00 2001 From: Jurek Elliesen <91612967+aff3npirat@users.noreply.github.com> Date: Tue, 26 May 2026 18:59:23 +0200 Subject: [PATCH 6/7] Fix --- include/net/web_server/query_router.h | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/include/net/web_server/query_router.h b/include/net/web_server/query_router.h index 3acfa6d..fdbdcc8 100644 --- a/include/net/web_server/query_router.h +++ b/include/net/web_server/query_router.h @@ -322,36 +322,13 @@ struct query_router { opentelemetry::trace::StartSpanOptions{ .parent = opentelemetry::trace::GetSpan(new_ctx)->GetContext(), .kind = opentelemetry::trace::SpanKind::kServer}); - auto const scope = get_otel_tracer()->WithActiveSpan(span); if (auto const user_agent = req[boost::beast::http::field::user_agent]; !user_agent.empty()) { span->SetAttribute(semconv::kUserAgentOriginal, user_agent); } - // auto const set_otlp_status = [&](reply const& rep, bool is_error) { - // auto const field = - // is_error ? semconv::kErrorType : semconv::kHttpResponseStatusCode; - // std::visit(utl::overloaded{[&](web_server::string_res_t rep) { - // span->SetAttribute(field, - // rep.result_int()); - // }, - // [&](web_server::buffer_res_t rep) { - // span->SetAttribute(field, - // rep.result_int()); - // }, - // [&](web_server::file_res_t rep) { - // span->SetAttribute(field, - // rep.result_int()); - // }, - // [&](web_server::empty_res_t rep) { - // span->SetAttribute(field, - // rep.result_int()); - // }}, - // rep); - // }; - - auto const set_otlp_status = [&span](reply const& rep, bool is_error) { + auto const set_otlp_status = [span](reply const& rep, bool is_error) { auto const field = is_error ? semconv::kErrorType : semconv::kHttpResponseStatusCode; std::visit( @@ -438,8 +415,10 @@ struct query_router { if (reply_hook_) { reply_hook_(rep); } + span->End(); return cb(std::move(rep)); } + span->End(); } void reply_hook(std::function reply_hook) { From e739df29b9c1f8a3391c8f349bfcedd1be2113c2 Mon Sep 17 00:00:00 2001 From: Jurek Elliesen <91612967+aff3npirat@users.noreply.github.com> Date: Wed, 27 May 2026 15:28:47 +0200 Subject: [PATCH 7/7] Add OTLP fields --- include/net/web_server/query_router.h | 32 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/include/net/web_server/query_router.h b/include/net/web_server/query_router.h index fdbdcc8..eb27784 100644 --- a/include/net/web_server/query_router.h +++ b/include/net/web_server/query_router.h @@ -310,14 +310,14 @@ struct query_router { auto const path = url.path(); auto span = get_otel_tracer()->StartSpan( - // req.method_string() + " "sv + req.target(), - req.method_string(), + fmt::format("{} {}", req.method_string(), req.target()), { - // TODO {semconv::kHttpRequestMethod, req.method_string()}, {semconv::kUrlPath, path}, {semconv::kUrlQuery, url.query()}, - {semconv::kUrlScheme, "http"}, + {semconv::kUrlScheme, url.scheme()}, + {semconv::kServerAddress, url.host_address()}, + {semconv::kServerPort, url.port()}, }, opentelemetry::trace::StartSpanOptions{ .parent = opentelemetry::trace::GetSpan(new_ctx)->GetContext(), @@ -328,6 +328,16 @@ struct query_router { span->SetAttribute(semconv::kUserAgentOriginal, user_agent); } + // for (auto it = req.cbegin(); it != req.cend(); it++) { + // span->SetAttribute(fmt::format("http.request.header.{}", *it), + // req[*it]); + // } + for (auto const& field : req) { + span->SetAttribute( + fmt::format("http.request.header.{}", field.name_string()), + field.value()); + } + auto const set_otlp_status = [span](reply const& rep, bool is_error) { auto const field = is_error ? semconv::kErrorType : semconv::kHttpResponseStatusCode; @@ -357,6 +367,13 @@ struct query_router { set_credentials(route_req); decode_content(route_req); + span->UpdateName(fmt::format("{} {}", route->method_, route->prefix_)); + span->SetAttribute(semconv::kHttpRoute, route->prefix_); + if (route->method_ == "POST") { + span->SetAttribute(semconv::kHttpRequestBodySize, req.body().size()); + span->SetAttribute("query_router.http.request.body", req.body()); + } + return exec_.exec( [this, route, is_ssl, span, set_otlp_status, r = std::move(route_req)]() { @@ -366,6 +383,13 @@ struct query_router { span->AddEvent("Processing Request"); rep = route->request_handler_(r, is_ssl); set_otlp_status(rep, false); + std::visit( + [&span](auto& rep) { + span->SetAttribute(semconv::kHttpResponseBodySize, + rep.payload_size().value_or(0)); + }, + rep); + } catch (openapi::bad_request_exception const& e) { rep = bad_request_response(r, serialize(value{{"error", e.what()}}));