diff options
| author | Stefan Boberg <[email protected]> | 2026-01-19 13:39:03 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-01-19 13:39:03 +0100 |
| commit | ee0c0810b5720a01318a0115da68760b20059459 (patch) | |
| tree | bcd25a41a639b487b9375830ee5e1e0d0c0cf9b6 /src/zentelemetry/include | |
| parent | small doc updates (#715) (diff) | |
| download | zen-ee0c0810b5720a01318a0115da68760b20059459.tar.xz zen-ee0c0810b5720a01318a0115da68760b20059459.zip | |
OTLP/trace improvements (#717)
This PR brings over some changes made to avoid performing setup for otel instrumentation if we are not sending otel information anywhere anyway.
It also adds the ability to configure an OTLP endpoint on the command line using `--otlp-endpoint=<URI>`.
Bear in mind that OTLP support is still not officially supported so this should not be used in production at this stage.
Diffstat (limited to 'src/zentelemetry/include')
| -rw-r--r-- | src/zentelemetry/include/zentelemetry/otlptrace.h | 130 |
1 files changed, 119 insertions, 11 deletions
diff --git a/src/zentelemetry/include/zentelemetry/otlptrace.h b/src/zentelemetry/include/zentelemetry/otlptrace.h index f191241ed..49dd90358 100644 --- a/src/zentelemetry/include/zentelemetry/otlptrace.h +++ b/src/zentelemetry/include/zentelemetry/otlptrace.h @@ -4,6 +4,7 @@ #include <zenbase/refcount.h> #include <zencore/memcmp.h> +#include <zencore/string.h> #include <zencore/uid.h> #include <span> @@ -27,12 +28,26 @@ class MemoryArena; namespace zen::otel { -using AttributeList = std::span<std::pair<std::string, std::string>>; +using AttributeList = std::span<std::pair<std::string_view, std::string_view>>; class Tracer; -// OLTP Span ID +/** Check if OTLP tracing is enabled + * + * This can be used to avoid unnecessary work when tracing is disabled. + * + * In many cases it is preferable and more convenient to use the ScopedSpan + * constructor which takes a naming function to avoid string formatting work + * when tracing is disabled. + */ +bool IsOtlpTraceEnabled(); +/** OTLP Span ID + * + * A SpanId is an 8-byte identifier for a span within a trace. It's not something + * that should be interpreted or manipulated directly, but it can be used + * for correlating spans during analysis. + */ struct SpanId { constexpr static size_t kSize = 8; @@ -60,7 +75,12 @@ private: uint8_t Id[kSize]; }; -// OLTP Trace ID +/** OTLP Trace ID + * + * A TraceId is a 16-byte identifier for a trace. It's not something + * that should be interpreted or manipulated directly, but it can be used + * for correlating traces during analysis. + */ struct TraceId { @@ -68,23 +88,35 @@ struct TraceId std::span<const uint8_t> GetBytes() const { return std::span<const uint8_t>(Id, kSize); } - inline TraceId() noexcept : Id{0} {} + inline TraceId() noexcept { memset(Id, 0, kSize); } explicit TraceId(const std::span<const uint8_t, kSize> Bytes) noexcept { std::copy(Bytes.begin(), Bytes.end(), Id); } std::strong_ordering operator<=>(const TraceId& Rhs) const noexcept { - int cmp = MemCmpFixed<kSize>(Id, Rhs.Id); - if (cmp < 0) + if (int Diff = MemCmpFixed<kSize>(Id, Rhs.Id); Diff < 0) + { return std::strong_ordering::less; - if (cmp > 0) + } + else if (Diff > 0) + { return std::strong_ordering::greater; - return std::strong_ordering::equal; + } + else + { + return std::strong_ordering::equal; + } } + inline operator bool() const { return !(reinterpret_cast<const uint64_t*>(Id)[0] == 0 && reinterpret_cast<const uint64_t*>(Id)[1] == 0); } + // Generates a new TraceId. The current scheme uses the session ID + // as the first 12 bytes, and a counter for the last 4 bytes. This makes + // it more likely that traces from the same session can be correlated, and + // should make indexes more efficient since the identifiers will be emitted + // in a mostly (lexically) increasing order. static TraceId NewTraceId(); inline const char* GetData() const { return reinterpret_cast<const char*>(Id); } @@ -93,6 +125,17 @@ private: uint8_t Id[kSize]; }; +/** OTEL attribute key-value pair + * + * An AttributePair is a key-value pair that provides additional information + * about a span or event. This class is intended to support a variety of + * value types, but currently only supports string and integer values. + * + * This is an internal structure used by the Span class, which encodes + * the key-value pair efficiently. Each instance is allocated within + * a MemoryArena associated with the trace and will be freed without + * explicit deallocation when the arena is destroyed. + */ struct AttributePair { const char* Key = nullptr; @@ -129,6 +172,12 @@ struct AttributePair AttributePair* Next = nullptr; }; +/** OTEL event + * + * An event represents a time-stamped annotation of the span, consisting + * of a name and optional attributes. + * + */ struct Event { Event* NextEvent = nullptr; @@ -142,6 +191,10 @@ struct Event * A span represents a single operation within a trace. Spans can be nested * to form a trace tree. * + * This class is reference counted in order to support spans which may + * cross thread boundaries. A single span may be referenced by multiple threads + * and will be finalized when the last reference is released. + * */ struct Span final : public TRefCounted<Span> @@ -225,7 +278,21 @@ private: class ScopedSpan final { public: + /** Create a new scoped span + * + * @param Name Name of the span + */ ScopedSpan(std::string_view Name); + + /** Create a new scoped span with an initializer function + * + * This allows initializing the span (e.g. adding attributes etc) in a safe way + * that avoids unnecessary work when OTLP tracing is disabled. + * + * @param Name Name of the span + * @param InitializerFunction Function which is called with the newly created span + * so that it can be initialized (e.g. adding attributes etc) + */ ScopedSpan(std::string_view Name, std::invocable<Span&> auto&& InitializerFunction) : ScopedSpan(Name) { if (m_Span) @@ -233,6 +300,43 @@ public: InitializerFunction(*m_Span); } } + + /** Construct a new span with a naming function + * + * The naming function will only be called if OTLP tracing is enabled. This can be + * used to avoid unnecessary string formatting when tracing is disabled. + * + * @param NamingFunction Function which is called with a string builder to create the span name + */ + ScopedSpan(std::invocable<StringBuilderBase&> auto&& NamingFunction) + { + if (!IsOtlpTraceEnabled()) + { + return; + } + + ExtendableStringBuilder<128> NameBuilder; + NamingFunction(NameBuilder); + } + + /** Construct a new span with a naming function AND initializer function + * + * Both functions will only be called if OTLP tracing is enabled. This can be + * used to avoid unnecessary string formatting and span initialization related work + * when tracing. + * + * @param NamingFunction Function which is called with a string builder to create the span name + * @param InitializerFunction Function which is called with the newly created span + * so that it can be initialized (e.g. adding attributes etc) + */ + ScopedSpan(std::invocable<StringBuilderBase&> auto&& NamingFunction, std::invocable<Span&> auto&& InitializerFunction) + : ScopedSpan(NamingFunction) + { + if (m_Span) + { + InitializerFunction(*m_Span); + } + } ScopedSpan() = delete; ~ScopedSpan(); @@ -241,11 +345,15 @@ public: ScopedSpan(ScopedSpan&& Rhs) = default; ScopedSpan(const ScopedSpan& Rhs) = default; - operator bool() const { return !!m_Span; } - void WithSpan(auto Func) const { Func(*m_Span); } + // Check if the span is valid (i.e OTLP tracing is enabled) + inline explicit operator bool() const { return !!m_Span; } + + // Execute a function with the span pointer if valid. This can + // be used to add attributes or events to the span after creation + inline void WithSpan(auto Func) const { Func(*m_Span); } private: - ScopedSpan(Span* InSpan, Tracer* InTracer); + void Initialize(std::string_view Name); Ref<Tracer> m_Tracer; // This needs to precede the span ref to ensure proper destruction order Ref<Span> m_Span; |