From: Pieter Lexis Date: Thu, 25 Sep 2025 15:38:29 +0000 (+0200) Subject: feat(dnsdist): add initial OpenTelemetry Tracing X-Git-Tag: rec-5.4.0-alpha1~187^2~26 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c71ae3cdc2e5c308abff74eefd02feb2b31f4a58;p=thirdparty%2Fpdns.git feat(dnsdist): add initial OpenTelemetry Tracing --- diff --git a/pdns/dnsdistdist/Makefile.am b/pdns/dnsdistdist/Makefile.am index 609ba5559e..ab423885d1 100644 --- a/pdns/dnsdistdist/Makefile.am +++ b/pdns/dnsdistdist/Makefile.am @@ -234,6 +234,7 @@ dnsdist_SOURCES = \ dnsdist-metrics.cc dnsdist-metrics.hh \ dnsdist-nghttp2-in.hh \ dnsdist-nghttp2.hh \ + dnsdist-opentelemetry.cc dnsdist-opentelemetry.hh \ dnsdist-prometheus.hh \ dnsdist-protobuf.cc dnsdist-protobuf.hh \ dnsdist-protocols.cc dnsdist-protocols.hh \ @@ -290,6 +291,7 @@ dnsdist_SOURCES = \ packetcache.hh \ pdnsexception.hh \ pollmplexer.cc \ + protozero-trace.cc protozero-trace.hh \ protozero.cc protozero.hh \ proxy-protocol.cc proxy-protocol.hh \ qtype.cc qtype.hh \ diff --git a/pdns/dnsdistdist/dnsdist-actions-definitions.yml b/pdns/dnsdistdist/dnsdist-actions-definitions.yml index 05e7f99c98..d7ce17c567 100644 --- a/pdns/dnsdistdist/dnsdist-actions-definitions.yml +++ b/pdns/dnsdistdist/dnsdist-actions-definitions.yml @@ -400,6 +400,12 @@ are processed after this action" - name: "ttl" type: "u32" description: "The TTL to use" +- name: "SetTrace" + description: "Enable or disable OpenTelemetry tracing for this query. Don't forget to also use a RemoteLog action to actually send the trace. Subsequent rules are processed after this action" + parameters: + - name: "value" + type: "bool" + description: "Whether or not to enable tracing" - name: "SNMPTrap" description: "Send an SNMP trap, adding the message string as the query description. Subsequent rules are processed after this action" parameters: diff --git a/pdns/dnsdistdist/dnsdist-actions-factory.cc b/pdns/dnsdistdist/dnsdist-actions-factory.cc index deb8110e16..d340744920 100644 --- a/pdns/dnsdistdist/dnsdist-actions-factory.cc +++ b/pdns/dnsdistdist/dnsdist-actions-factory.cc @@ -47,6 +47,8 @@ #include "ipcipher.hh" #include "dnsdist-ipcrypt2.hh" #include "iputils.hh" +#include "protozero-trace.hh" +#include "qtype.hh" #include "remote_logger.hh" #include "svc-records.hh" #include "threadname.hh" @@ -1670,9 +1672,41 @@ private: std::string d_ipEncryptMethod; std::optional d_ipcrypt2{std::nullopt}; }; - #endif /* DISABLE_PROTOBUF */ +class SetTraceAction : public DNSAction +{ +public: + SetTraceAction(bool value) : + d_value{value} {}; + + DNSAction::Action operator()([[maybe_unused]] DNSQuestion* dnsquestion, std::string* ruleresult) const override + { + (void)ruleresult; +#ifndef DISABLE_PROTOBUF + if (d_value) { + dnsquestion->ids.d_OTTracer->activate(); + dnsquestion->ids.d_OTTracer->setTraceAttribute("query.qname", AnyValue{dnsquestion->ids.qname.toStringNoDot()}); + dnsquestion->ids.d_OTTracer->setTraceAttribute("query.qtype", AnyValue{QType(dnsquestion->ids.qtype).toString()}); + dnsquestion->ids.d_OTTracer->setTraceAttribute("query.remote", AnyValue{dnsquestion->ids.origRemote.toLogString()}); + } + else { + dnsquestion->ids.d_OTTracer->deactivate(); + } + dnsquestion->ids.tracingEnabled = d_value; +#endif + return Action::None; + } + + [[nodiscard]] std::string toString() const override + { + return string((d_value ? "en" : "dis")) + string("able OpenTelemetry Tracing"); + } + +private: + bool d_value; +}; + class SNMPTrapAction : public DNSAction { public: diff --git a/pdns/dnsdistdist/dnsdist-idstate.hh b/pdns/dnsdistdist/dnsdist-idstate.hh index 74839e2d69..dbf700fce2 100644 --- a/pdns/dnsdistdist/dnsdist-idstate.hh +++ b/pdns/dnsdistdist/dnsdist-idstate.hh @@ -21,7 +21,12 @@ */ #pragma once +#include +#include +#include #include +#include +#include #include "config.h" #include "dnscrypt.hh" @@ -31,6 +36,7 @@ #include "gettime.hh" #include "iputils.hh" #include "noinitvector.hh" +#include "dnsdist-opentelemetry.hh" #include "uuid-utils.hh" struct ClientState; @@ -107,6 +113,13 @@ struct InternalQueryState std::string d_requestorID; }; + // Whether or not Open Telemetry tracing is enabled for this query + bool tracingEnabled = false; + + // TODO: Do we want to keep some data *without* creating a tracer for each query? + // TODO: shard_ptr to work with Tracer::Closer? + std::unique_ptr d_OTTracer{new pdns::trace::dnsdist::Tracer}; + InternalQueryState() { origDest.sin4.sin_family = 0; diff --git a/pdns/dnsdistdist/dnsdist-opentelemetry.cc b/pdns/dnsdistdist/dnsdist-opentelemetry.cc new file mode 100644 index 0000000000..9b8a061ea4 --- /dev/null +++ b/pdns/dnsdistdist/dnsdist-opentelemetry.cc @@ -0,0 +1,272 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "dnsdist-opentelemetry.hh" + +#include + +#ifndef DISABLE_PROTOBUF +#include "protozero-trace.hh" +#endif + +namespace pdns::trace::dnsdist +{ + +TracesData Tracer::getTracesData() const +{ +#ifdef DISABLE_PROTOBUF + return 0; +#else + auto otTrace = pdns::trace::TracesData{ + .resource_spans = { + pdns::trace::ResourceSpans{ + .resource = { + .attributes = std::vector{ + pdns::trace::KeyValue{ + "service.name", pdns::trace::AnyValue{"dnsdist"}}, + }}, + .scope_spans = std::vector{{}}}}}; + + otTrace.resource_spans.at(0).resource.attributes.insert( + otTrace.resource_spans.at(0).resource.attributes.end(), + d_attributes.begin(), + d_attributes.end()); + + for (auto const& preActivationTrace : d_preActivationSpans) { + otTrace.resource_spans.at(0).scope_spans.at(0).spans.push_back( + { + .trace_id = d_traceid, + .span_id = preActivationTrace.span_id, + .parent_span_id = preActivationTrace.parent_span_id, + .name = preActivationTrace.name, + .start_time_unix_nano = preActivationTrace.start_time_unix_nano, + .end_time_unix_nano = preActivationTrace.end_time_unix_nano, + }); + } + + otTrace.resource_spans.at(0).scope_spans.at(0).spans.insert( + otTrace.resource_spans.at(0).scope_spans.at(0).spans.end(), + d_postActivationSpans.begin(), + d_postActivationSpans.end()); + return otTrace; +#endif +} + +std::string Tracer::getOTProtobuf() const +{ +#ifdef DISABLE_PROTOBUF + return 0; +#else + // TODO: Should we close all spans? + return getTracesData().encode(); +#endif +} + +SpanID Tracer::addSpan([[maybe_unused]] const std::string& name) +{ +#ifdef DISABLE_PROTOBUF + return 0; +#else + return addSpan(name, SpanID{}); +#endif +} + +SpanID Tracer::addSpan([[maybe_unused]] const std::string& name, [[maybe_unused]] const SpanID& parentSpanID) +{ +#ifdef DISABLE_PROTOBUF + return 0; +#else + auto spanID = pdns::trace::randomSpanID(); + if (d_activated) { + d_postActivationSpans.push_back({ + .trace_id = d_traceid, + .span_id = spanID, + .parent_span_id = parentSpanID, + .name = name, + .start_time_unix_nano = pdns::trace::timestamp(), + }); + return spanID; + } + + // We're not activated, so we are in pre-activation. + d_preActivationSpans.push_back({ + .name = name, + .span_id = spanID, + .parent_span_id = parentSpanID, + .start_time_unix_nano = pdns::trace::timestamp(), + .end_time_unix_nano = 0, + }); + + d_lastSpanID = spanID; + + return spanID; +#endif +} + +// TODO: Figure out what to do with duplicate keys +bool Tracer::setTraceAttribute([[maybe_unused]] const std::string& key, [[maybe_unused]] const AnyValue& value) +{ +#ifdef DISABLE_PROTOBUF + // always succesfull + return true; +#else + if (!d_activated) { + return false; + } + d_attributes.push_back({key, value}); + return true; +#endif +} + +void Tracer::closeSpan([[maybe_unused]] const SpanID& spanID) +{ +#ifndef DISABLE_PROTOBUF + if (d_activated) { + auto spanIt = std::find_if( + d_postActivationSpans.rbegin(), + d_postActivationSpans.rend(), + [spanID](const pdns::trace::Span& span) { return span.span_id == spanID; }); + if (spanIt != d_postActivationSpans.rend()) { + if (spanIt->end_time_unix_nano == 0) { + spanIt->end_time_unix_nano = pdns::trace::timestamp(); + } + return; + } + } + + auto spanIt = std::find_if( + d_preActivationSpans.rbegin(), + d_preActivationSpans.rend(), + [spanID](const preActivationSpanInfo& span) { return span.span_id == spanID; }); + if (spanIt != d_preActivationSpans.rend() && spanIt->end_time_unix_nano == 0) { + spanIt->end_time_unix_nano = pdns::trace::timestamp(); + return; + } +#endif +} + +// TODO: Figure out what to do with duplicate keys +void Tracer::setSpanAttribute([[maybe_unused]] const SpanID& spanid, [[maybe_unused]] const std::string& key, [[maybe_unused]] const AnyValue& value) +{ +#ifndef DISABLE_PROTOBUF + if (d_activated) { + if (auto iter = std::find_if(d_postActivationSpans.rbegin(), + d_postActivationSpans.rend(), + [spanid](const pdns::trace::Span& span) { return span.span_id == spanid; }); + iter != d_postActivationSpans.rend()) { + iter->attributes.push_back({key, value}); + return; + } + } + // XXX: It is not possible to add attributes to d_preActivationTraces. Perhaps these should be converted on calling activate +#endif +} + +SpanID Tracer::getLastSpanID() const +{ +#ifdef DISABLE_PROTOBUF + return 0; +#else + if (d_activated && d_postActivationSpans.size() != 0) { + return d_postActivationSpans.back().span_id; + } + if (d_preActivationSpans.size() != 0) { + return d_preActivationSpans.back().span_id; + } + return SpanID{}; +#endif +} + +SpanID Tracer::getLastSpanIDForName([[maybe_unused]] const std::string& name) const +{ +#ifdef DISABLE_PROTOBUF + return 0; +#else + if (d_activated && d_postActivationSpans.size() != 0) { + if (auto iter = std::find_if(d_postActivationSpans.rbegin(), + d_postActivationSpans.rend(), + [name](const pdns::trace::Span& span) { return span.name == name; }); + iter != d_postActivationSpans.rend()) { + return iter->span_id; + } + } + + if (d_preActivationSpans.size() != 0) { + if (auto iter = std::find_if(d_preActivationSpans.rbegin(), + d_preActivationSpans.rend(), + [name](const preActivationSpanInfo& span) { return span.name == name; }); + iter != d_preActivationSpans.rend()) { + return iter->span_id; + } + } + return SpanID{}; +#endif +} + +TraceID Tracer::getTraceID() const +{ +#ifdef DISABLE_PROTOBUF + return 0; +#else + return d_traceid; +#endif +} + +Tracer::Closer Tracer::getCloser([[maybe_unused]] const SpanID& spanid) +{ +#ifdef DISABLE_PROTOBUF + return Tracer::Closer(); +#else + return {this, spanid}; +#endif +} + +Tracer::Closer Tracer::openSpan([[maybe_unused]] const std::string& name) +{ +#ifdef DISABLE_PROTOBUF + return Tracer::Closer(); +#else + auto spanid = addSpan(name); + return getCloser(spanid); +#endif +} + +Tracer::Closer Tracer::openSpan([[maybe_unused]] const std::string& name, [[maybe_unused]] const SpanID& parentSpanID) +{ +#ifdef DISABLE_PROTOBUF + return Tracer::Closer(); +#else + auto spanid = addSpan(name, parentSpanID); + return getCloser(spanid); +#endif +} + +SpanID Tracer::Closer::getSpanID() const +{ +#ifdef DISABLE_PROTOBUF + return 0; +#else + return d_spanID; +#endif +} + +} // namespace pdns::trace::dnsdist diff --git a/pdns/dnsdistdist/dnsdist-opentelemetry.hh b/pdns/dnsdistdist/dnsdist-opentelemetry.hh new file mode 100644 index 0000000000..c1316ca827 --- /dev/null +++ b/pdns/dnsdistdist/dnsdist-opentelemetry.hh @@ -0,0 +1,315 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#pragma once + +#include +#include + +#ifndef DISABLE_PROTOBUF +#include "protozero-trace.hh" +using TraceID = pdns::trace::TraceID; +using SpanID = pdns::trace::SpanID; +using AnyValue = pdns::trace::AnyValue; +using TracesData = pdns::trace::TracesData; +#else +// Define the minimal things needed +#include +using TraceID = int; +using SpanID = int; +using AnyValue = std::variant; +using TracesData = int; +#endif + +/* + * This namespace contains all the bits and pieces required to do OpenTelemetry + * traces in dnsdist. It is contained in this header and cc-file to ensure the rest + * of the code is not littered with #ifdefs for DISABLE_PROTOBUF. All functions and + * other public members can be safely called/manipulated in a non-protobuf build of + * dnsdist. + * + * The idea is inspired by the rec-eventtrace.{cc,hh} files. + * + * Although the namespace contains dnsdist, it might be general enough to be + * reused (after renaming the namespace) by auth and recursor + */ +namespace pdns::trace::dnsdist +{ + +/** + * @class Tracer + * @brief This class holds a single trace instance + * + */ +class Tracer +{ +public: + Tracer() = default; + ~Tracer() = default; + Tracer(const Tracer&) = delete; + Tracer& operator=(const Tracer) = delete; + Tracer& operator=(Tracer&&) = delete; + Tracer(Tracer&&) = delete; + + /** + * @brief Activate the Tracer + * + * Once activated, all new events are stored as actual Spans + */ + void activate() + { +#ifndef DISABLE_PROTOBUF + d_activated = true; + if (d_traceid == pdns::trace::s_emptyTraceID) { + pdns::trace::random(d_traceid); + } +#endif + } + + /** + * @brief Deactivate the tracer + */ + void deactivate() + { +#ifndef DISABLE_PROTOBUF + // Set deactivated, but don't delete any data. + // The Tracer can be activated later + d_activated = false; +#endif + } + + /** + * @brief Add an attribute to the Trace + * + * This only works when Tracer is active + * + * @param key + * @param value + * @return true on success, false when attribute was not added + */ + bool setTraceAttribute(const std::string& key, const AnyValue& value); + + /** + * @brief Set an attribute on a Span + * + * This does not work when the Tracer is not active + * + * @param spanID The SpanID of the Span to add the attribute to + * @param key + * @param value + */ + void setSpanAttribute(const SpanID& spanID, const std::string& key, const AnyValue& value); + + /** + * @brief Sets the stop timestamp for a span + * + * When a Span is already closed, the timestamp is not updated + * + * @param spanID The ID of the Span to set the end time for + */ + void closeSpan(const SpanID& spanID); + + /** + * @brief Get the last SpanID generated + * + * @return The last generated SpanID, or empty SpanID when none exist + */ + [[nodiscard]] SpanID getLastSpanID() const; + + /** + * @brief Get the SpanID for the most recently added span with a name + * + * @param name The name of the Span + * @return The SpanID, or empty SpanID when none are found + */ + [[nodiscard]] SpanID getLastSpanIDForName(const std::string& name) const; + + /** + * @brief Retrieve the TraceID for this Tracer + */ + [[nodiscard]] TraceID getTraceID() const; + + /** + * @brief Generate the TracesData from all data in this Tracer + * + * @return pdns::trace::TracesData + */ + [[nodiscard]] TracesData getTracesData() const; + + /** + * @brief Get the TracesData as protobuf encoded OpenTelemetry data + */ + [[nodiscard]] std::string getOTProtobuf() const; + + /** + * @class Closer + * @brief Automatically closes a Span when it goes out of scope + * + * This is a helper that _somewhat_ implements Go's `defer` in C++ semantics + * Basically, it stores a pointer to the Tracer and a SpanID. + * When the object goes out of scope, the closeSpan function is called + */ + class Closer + { + public: + /** + * @brief An empty Closer, not really useful + */ +#ifdef DISABLE_PROTOBUF + Closer() = default; +#else + Closer() : + d_tracer(nullptr), d_spanID(SpanID{}) {}; + /** + * @brief Create a Closer + * + * There should be no need to call this directly. Use one of these functions to get one: + * + * Tracer::getCloser + * Tracer::openSpan + * + * @param tracer A pointer to the Tracer where we want to close a Span + * @param spanid The SpanID to close in the Tracer + */ + Closer(Tracer* tracer, const SpanID& spanid) : + d_tracer(tracer), d_spanID(spanid) {}; +#endif + + /** + * @brief Closes the Span in the Tracer + */ + ~Closer() + { +#ifndef DISABLE_PROTOBUF + if (d_tracer != nullptr) { + d_tracer->closeSpan(d_spanID); + } +#endif + }; + Closer(const Closer&) = default; + Closer& operator=(const Closer&) = default; + Closer& operator=(Closer&&) noexcept = default; + Closer(Closer&&) = default; + + /** + * @brief Get the SpanID + * + * @return + */ + [[nodiscard]] SpanID getSpanID() const; + + private: +#ifndef DISABLE_PROTOBUF + // XXX: Should we make this a shared_ptr and force all consumers to Tracer to keep it as a shared_ptr as well? + Tracer* d_tracer; + SpanID d_spanID; +#endif + }; + + /** + * @brief Get a Closer for spanid in this Tracer + * + * @param spanid The SpanID that will close when the Closer is destructed + * @return Tracer::Closer + */ + Closer getCloser(const SpanID& spanid); + + /** + * @brief Add a new Span + * + * @param name The name for this span + * @return Tracer::Closer for the newly created Span + */ + Closer openSpan(const std::string& name); + + /** + * @brief Add a new Span which is a child of another Span + * + * @param name The name for this span + * @param parentSpanID The SpanID of the parent Trace + * @return Tracer::Closer for the newly created Span + */ + Closer openSpan(const std::string& name, const SpanID& parentSpanID); + +private: + /** + * @brief Create a new Span + * + * The Span's start time is set to the current time + * + * @param name The name for this span + * @return The SpanID of the created Span + */ + SpanID addSpan(const std::string& name); + + /** + * @brief Create a new Span with a parent + * + * The Span's start time is set to the current time + * + * @param name The name for this span + * @param parentSpanID The SpanID of the parent Span (not verified) + * @return The SpanID of the created Span + */ + SpanID addSpan(const std::string& name, const SpanID& parentSpanID); + +#ifndef DISABLE_PROTOBUF + /** + * @class preActivationSpanInfo + * @brief Used before the Tracer is activated to store Span information + */ + struct preActivationSpanInfo + { + std::string name; + SpanID span_id; + SpanID parent_span_id; + uint64_t start_time_unix_nano; + uint64_t end_time_unix_nano; + }; + + /** + * @brief Stores all preActivationSpanInfos. It is used until d_activated is true + */ + std::vector d_preActivationSpans; + /** + * @brief Stores all Spans. It is used when d_activated is true + */ + std::vector d_postActivationSpans; + /** + * @brief All attributes related to this Trace + */ + std::vector d_attributes; + + /** + * @brief The TraceID for this Tracer. It is stable for the lifetime of the Tracer + */ + TraceID d_traceid{}; + /** + * @brief The last SpanID that was added to this Tracer + */ + SpanID d_lastSpanID{}; + /** + * @brief Whether or not we are storing full Spans or minimal Spans + */ + bool d_activated{false}; +#endif +}; +} // namespace pdns::trace::dnsdist diff --git a/pdns/dnsdistdist/dnsdist-protobuf.cc b/pdns/dnsdistdist/dnsdist-protobuf.cc index e02b6d8755..10774465c2 100644 --- a/pdns/dnsdistdist/dnsdist-protobuf.cc +++ b/pdns/dnsdistdist/dnsdist-protobuf.cc @@ -20,6 +20,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" +#include "protozero-trace.hh" +#include #ifndef DISABLE_PROTOBUF #include "base64.hh" @@ -241,6 +243,11 @@ void DNSDistProtoBufMessage::serialize(std::string& data) const msg.setMeta(key, {std::string()}, {}); } } + + if (d_dq.ids.tracingEnabled) { + msg.setOpenTelemtryTraceID(d_dq.ids.d_OTTracer->getTraceID()); + msg.setOpenTelemetryData(d_dq.ids.d_OTTracer->getOTProtobuf()); + } } ProtoBufMetaKey::ProtoBufMetaKey(const std::string& key) diff --git a/pdns/dnsdistdist/dnsdist-protobuf.hh b/pdns/dnsdistdist/dnsdist-protobuf.hh index 13d035749e..6824c716a3 100644 --- a/pdns/dnsdistdist/dnsdist-protobuf.hh +++ b/pdns/dnsdistdist/dnsdist-protobuf.hh @@ -22,6 +22,7 @@ #pragma once #include "dnsname.hh" +#include #ifndef DISABLE_PROTOBUF #include @@ -29,6 +30,7 @@ #include #include "protozero.hh" +#include "protozero-trace.hh" struct DNSQuestion; struct DNSResponse; @@ -110,6 +112,8 @@ private: pdns::ProtoZero::Message::MessageType d_type{pdns::ProtoZero::Message::MessageType::DNSQueryType}; bool d_includeCNAME{false}; + + std::optional> d_traceSpans{std::nullopt}; }; class ProtoBufMetaKey diff --git a/pdns/dnsdistdist/dnsdist.cc b/pdns/dnsdistdist/dnsdist.cc index 3bba1fd0e8..ebf621d03b 100644 --- a/pdns/dnsdistdist/dnsdist.cc +++ b/pdns/dnsdistdist/dnsdist.cc @@ -29,12 +29,17 @@ #include #include #include +#include #include #include #include #include +#include "dns.hh" +#include "dnsdist-idstate.hh" +#include "dnsdist-opentelemetry.hh" #include "dnsdist-systemd.hh" +#include "protozero-trace.hh" #ifdef HAVE_SYSTEMD #include #endif @@ -451,6 +456,10 @@ static bool encryptResponse(PacketBuffer& response, size_t maximumSize, bool tcp bool applyRulesToResponse(const std::vector& respRuleActions, DNSResponse& dnsResponse) { + pdns::trace::dnsdist::Tracer::Closer closer; + if (dnsResponse.ids.tracingEnabled) { + closer = dnsResponse.ids.d_OTTracer->openSpan("applyRulesToResponse", dnsResponse.ids.d_OTTracer->getLastSpanID()); + } if (respRuleActions.empty()) { return true; } @@ -511,8 +520,15 @@ bool applyRulesToResponse(const std::vector& bool processResponseAfterRules(PacketBuffer& response, DNSResponse& dnsResponse, [[maybe_unused]] bool muted) { + pdns::trace::dnsdist::Tracer::Closer closer; + if (dnsResponse.ids.tracingEnabled) { + closer = dnsResponse.ids.d_OTTracer->openSpan("processResponseAfterRules"); + } bool zeroScope = false; if (!fixUpResponse(response, dnsResponse.ids.qname, dnsResponse.ids.origFlags, dnsResponse.ids.ednsAdded, dnsResponse.ids.ecsAdded, dnsResponse.ids.useZeroScope ? &zeroScope : nullptr)) { + if (dnsResponse.ids.tracingEnabled) { + dnsResponse.ids.d_OTTracer->setSpanAttribute(closer.getSpanID(), "result", AnyValue{"fixUpResponse->false"}); + } return false; } @@ -538,8 +554,13 @@ bool processResponseAfterRules(PacketBuffer& response, DNSResponse& dnsResponse, // if zeroScope, pass the pre-ECS hash-key and do not pass the subnet to the cache cacheKey = dnsResponse.ids.cacheKeyNoECS; } - dnsResponse.ids.packetCache->insert(cacheKey, zeroScope ? boost::none : dnsResponse.ids.subnet, dnsResponse.ids.cacheFlags, dnsResponse.ids.dnssecOK ? *dnsResponse.ids.dnssecOK : false, dnsResponse.ids.qname, dnsResponse.ids.qtype, dnsResponse.ids.qclass, response, dnsResponse.ids.forwardedOverUDP, dnsResponse.getHeader()->rcode, dnsResponse.ids.tempFailureTTL); - + { + pdns::trace::dnsdist::Tracer::Closer cacheInsertCloser; + if (dnsResponse.ids.tracingEnabled) { + cacheInsertCloser = dnsResponse.ids.d_OTTracer->openSpan("packetCacheInsert", closer.getSpanID()); + } + dnsResponse.ids.packetCache->insert(cacheKey, zeroScope ? boost::none : dnsResponse.ids.subnet, dnsResponse.ids.cacheFlags, dnsResponse.ids.dnssecOK ? *dnsResponse.ids.dnssecOK : false, dnsResponse.ids.qname, dnsResponse.ids.qtype, dnsResponse.ids.qclass, response, dnsResponse.ids.forwardedOverUDP, dnsResponse.getHeader()->rcode, dnsResponse.ids.tempFailureTTL); + } const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains; const auto& cacheInsertedRespRuleActions = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::CacheInsertedResponseRules); if (!applyRulesToResponse(cacheInsertedRespRuleActions, dnsResponse)) { @@ -568,6 +589,11 @@ bool processResponseAfterRules(PacketBuffer& response, DNSResponse& dnsResponse, bool processResponse(PacketBuffer& response, DNSResponse& dnsResponse, bool muted) { + pdns::trace::dnsdist::Tracer::Closer closer; + if (dnsResponse.ids.tracingEnabled) { + closer = dnsResponse.ids.d_OTTracer->openSpan("processResponse"); + } + const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains; const auto& respRuleActions = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::ResponseRules); @@ -1021,6 +1047,7 @@ static bool applyRulesChainToQuery(const std::vector static bool applyRulesToQuery(DNSQuestion& dnsQuestion, const timespec& now) { + auto closer = dnsQuestion.ids.d_OTTracer->openSpan("applyRulesToQuery", dnsQuestion.ids.d_OTTracer->getLastSpanID()); if (g_rings.shouldRecordQueries()) { g_rings.insertQuery(now, dnsQuestion.ids.origRemote, dnsQuestion.ids.qname, dnsQuestion.ids.qtype, dnsQuestion.getData().size(), *dnsQuestion.getHeader(), dnsQuestion.getProtocol()); } @@ -1426,9 +1453,18 @@ static ProcessQueryResult handleQueryTurnedIntoSelfAnsweredResponse(DNSQuestion& static ServerPolicy::SelectedBackend selectBackendForOutgoingQuery(DNSQuestion& dnsQuestion, const ServerPool& serverPool) { + auto closer = dnsQuestion.ids.d_OTTracer->openSpan("selectBackendForOutgoingQuery", dnsQuestion.ids.d_OTTracer->getLastSpanID()); + const auto& policy = serverPool.policy != nullptr ? *serverPool.policy : *dnsdist::configuration::getCurrentRuntimeConfiguration().d_lbPolicy; const auto& servers = serverPool.getServers(); - return policy.getSelectedBackend(servers, dnsQuestion); + auto selectedBackend = policy.getSelectedBackend(servers, dnsQuestion); + + if (dnsQuestion.ids.tracingEnabled) { + dnsQuestion.ids.d_OTTracer->setSpanAttribute(closer.getSpanID(), "backend.name", AnyValue{selectedBackend->getNameWithAddr()}); + dnsQuestion.ids.d_OTTracer->setSpanAttribute(closer.getSpanID(), "backend.id", AnyValue{boost::uuids::to_string(selectedBackend->getID())}); + } + + return selectedBackend; } // NOLINTNEXTLINE(readability-function-cognitive-complexity): refactoring will be done in https://github.com/PowerDNS/pdns/pull/16124 @@ -1742,8 +1778,9 @@ std::unique_ptr getUDPCrossProtocolQueryFromDQ(DNSQuestion& ProcessQueryResult processQuery(DNSQuestion& dnsQuestion, std::shared_ptr& selectedBackend) { - const uint16_t queryId = ntohs(dnsQuestion.getHeader()->id); + auto closer = dnsQuestion.ids.d_OTTracer->openSpan("processQuery", dnsQuestion.ids.d_OTTracer->getLastSpanID()); + const uint16_t queryId = ntohs(dnsQuestion.getHeader()->id); try { /* we need an accurate ("real") value for the response and to store into the IDS, but not for insertion into the @@ -1774,6 +1811,8 @@ ProcessQueryResult processQuery(DNSQuestion& dnsQuestion, std::shared_ptr& downstream, uint16_t queryID, DNSQuestion& dnsQuestion, PacketBuffer& query, bool actuallySend) { + auto closer = dnsQuestion.ids.d_OTTracer->openSpan("assignOutgoingUDPQueryToBackend", dnsQuestion.ids.d_OTTracer->getLastSpanID()); + bool doh = dnsQuestion.ids.du != nullptr; bool failed = false; @@ -1847,6 +1886,9 @@ static void processUDPQuery(ClientState& clientState, const struct msghdr* msgh, assert(responsesVect == nullptr || (queuedResponses != nullptr && respIOV != nullptr && respCBuf != nullptr)); uint16_t queryId = 0; InternalQueryState ids; + + auto closer = ids.d_OTTracer->openSpan("processUDPQuery"); + ids.cs = &clientState; ids.origRemote = remote; ids.hopRemote = remote; diff --git a/pdns/dnsdistdist/meson.build b/pdns/dnsdistdist/meson.build index 2e83040bdb..1ddd5797a4 100644 --- a/pdns/dnsdistdist/meson.build +++ b/pdns/dnsdistdist/meson.build @@ -169,6 +169,7 @@ common_sources += files( src_dir / 'dnsdist-metrics.cc', src_dir / 'dnsdist-nghttp2.cc', src_dir / 'dnsdist-nghttp2-in.cc', + src_dir / 'dnsdist-opentelemetry.cc', src_dir / 'dnsdist-protobuf.cc', src_dir / 'dnsdist-protocols.cc', src_dir / 'dnsdist-proxy-protocol.cc', @@ -202,6 +203,7 @@ common_sources += files( src_dir / 'libssl.cc', src_dir / 'misc.cc', src_dir / 'protozero.cc', + src_dir / 'protozero-trace.cc', src_dir / 'proxy-protocol.cc', src_dir / 'qtype.cc', src_dir / 'remote_logger.cc', diff --git a/pdns/dnsdistdist/protozero-trace.cc b/pdns/dnsdistdist/protozero-trace.cc new file mode 120000 index 0000000000..e55a75baee --- /dev/null +++ b/pdns/dnsdistdist/protozero-trace.cc @@ -0,0 +1 @@ +../protozero-trace.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/protozero-trace.hh b/pdns/dnsdistdist/protozero-trace.hh new file mode 120000 index 0000000000..3352190444 --- /dev/null +++ b/pdns/dnsdistdist/protozero-trace.hh @@ -0,0 +1 @@ +../protozero-trace.hh \ No newline at end of file diff --git a/pdns/protozero-trace.hh b/pdns/protozero-trace.hh index 9bcbb5ec06..6141916921 100644 --- a/pdns/protozero-trace.hh +++ b/pdns/protozero-trace.hh @@ -238,6 +238,13 @@ inline void random(SpanID& span) dns_random(span.data(), span.size()); } +inline SpanID randomSpanID() +{ + SpanID ret; + random(ret); + return ret; +} + inline void clear(TraceID& trace) { trace.fill(0);