Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
distributed_tracer.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
14#pragma once
15
16#include <string>
17#include <memory>
18#include <chrono>
19#include <vector>
20#include <unordered_map>
21#include <mutex>
22#include <atomic>
23#include <optional>
24
28
29namespace kcenon { namespace monitoring {
30
31// Forward declaration for trace exporter interface
32class trace_exporter_interface;
33
39 std::size_t batch_size = 100;
40 std::chrono::milliseconds flush_interval{5000};
41 std::size_t max_queue_size = 2048;
42 bool export_on_finish = true;
43};
44
48struct trace_span {
49 std::string trace_id; // Unique trace identifier
50 std::string span_id; // Unique span identifier
51 std::string parent_span_id; // Parent span ID (empty for root span)
52 std::string operation_name; // Name of the operation
53 std::string service_name; // Service executing this span
54
55 // Timing information
56 std::chrono::system_clock::time_point start_time;
57 std::chrono::system_clock::time_point end_time;
58 std::chrono::microseconds duration{0};
59
60 // Context and metadata
61 std::unordered_map<std::string, std::string> tags;
62 std::unordered_map<std::string, std::string> baggage;
63
64 // Status information
65 enum class status_code {
66 unset,
67 ok,
68 error
69 };
71 std::string status_message;
72
76 bool is_finished() const {
77 return end_time != std::chrono::system_clock::time_point{};
78 }
79
84 if (is_finished()) {
85 duration = std::chrono::duration_cast<std::chrono::microseconds>(
87 );
88 }
89 }
90};
91
96 std::string trace_id;
97 std::string span_id;
98 std::string trace_flags;
99 std::string trace_state;
100 std::unordered_map<std::string, std::string> baggage;
101
105 std::string to_w3c_traceparent() const {
106 // Version-TraceId-SpanId-TraceFlags
107 return "00-" + trace_id + "-" + span_id + "-" + trace_flags;
108 }
109
113 static common::Result<trace_context> from_w3c_traceparent(const std::string& header) {
114 // Parse format: version-traceid-spanid-traceflags
115 if (header.length() < 55) { // Minimum valid length
117 "Invalid traceparent header length");
118 return common::Result<trace_context>::err(err.to_common_error());
119 }
120
121 trace_context ctx;
122 // Simple parsing - production code would be more robust
123 size_t pos = 0;
124 size_t dash1 = header.find('-', pos);
125 size_t dash2 = header.find('-', dash1 + 1);
126 size_t dash3 = header.find('-', dash2 + 1);
127
128 if (dash1 == std::string::npos || dash2 == std::string::npos || dash3 == std::string::npos) {
130 "Invalid traceparent header format");
131 return common::Result<trace_context>::err(err.to_common_error());
132 }
133
134 ctx.trace_id = header.substr(dash1 + 1, dash2 - dash1 - 1);
135 ctx.span_id = header.substr(dash2 + 1, dash3 - dash2 - 1);
136 ctx.trace_flags = header.substr(dash3 + 1);
137
138 return common::ok(ctx);
139 }
140};
141
146private:
148
149public:
150 span_builder& with_trace_id(const std::string& id) {
151 span_.trace_id = id;
152 return *this;
153 }
154
155 span_builder& with_parent(const std::string& parent_id) {
156 span_.parent_span_id = parent_id;
157 return *this;
158 }
159
160 span_builder& with_operation(const std::string& name) {
161 span_.operation_name = name;
162 return *this;
163 }
164
165 span_builder& with_service(const std::string& name) {
166 span_.service_name = name;
167 return *this;
168 }
169
170 span_builder& with_tag(const std::string& key, const std::string& value) {
171 span_.tags[key] = value;
172 return *this;
173 }
174
175 span_builder& with_baggage(const std::string& key, const std::string& value) {
176 span_.baggage[key] = value;
177 return *this;
178 }
179
181 if (span_.span_id.empty()) {
183 }
184 if (span_.trace_id.empty()) {
186 }
187 span_.start_time = std::chrono::system_clock::now();
188 return span_;
189 }
190};
191
196private:
197 struct tracer_impl;
198 std::unique_ptr<tracer_impl> impl_;
199
200public:
203
204 // Disable copy
207
208 // Enable move
211
215 common::Result<std::shared_ptr<trace_span>> start_span(
216 const std::string& operation_name,
217 const std::string& service_name = "monitoring_system"
218 );
219
223 common::Result<std::shared_ptr<trace_span>> start_child_span(
224 const trace_span& parent,
225 const std::string& operation_name
226 );
227
231 common::Result<std::shared_ptr<trace_span>> start_span_from_context(
232 const trace_context& context,
233 const std::string& operation_name
234 );
235
239 common::Result<bool> finish_span(std::shared_ptr<trace_span> span);
240
244 std::shared_ptr<trace_span> get_current_span() const;
245
249 void set_current_span(std::shared_ptr<trace_span> span);
250
255
259 template<typename Carrier>
260 void inject_context(const trace_context& context, Carrier& carrier) {
261 carrier["traceparent"] = context.to_w3c_traceparent();
262 if (!context.trace_state.empty()) {
263 carrier["tracestate"] = context.trace_state;
264 }
265 // Inject baggage
266 for (const auto& [key, value] : context.baggage) {
267 carrier["baggage-" + key] = value;
268 }
269 }
270
274 template<typename Carrier>
275 common::Result<trace_context> extract_context_from_carrier(const Carrier& carrier) {
276 auto traceparent_it = carrier.find("traceparent");
277 if (traceparent_it == carrier.end()) {
278 return common::Result<trace_context>::err(error_info(kcenon::monitoring::monitoring_error_code::not_found,
279 "Traceparent not found in carrier").to_common_error());
280 }
281
282 auto ctx_result = trace_context::from_w3c_traceparent(traceparent_it->second);
283 if (ctx_result.is_err()) {
284 return common::Result<trace_context>::err(ctx_result.error());
285 }
286
287 auto ctx = ctx_result.value();
288
289 // Extract tracestate if present
290 auto tracestate_it = carrier.find("tracestate");
291 if (tracestate_it != carrier.end()) {
292 ctx.trace_state = tracestate_it->second;
293 }
294
295 // Extract baggage
296 for (const auto& [key, value] : carrier) {
297 if (key.find("baggage-") == 0) { // C++17 compatible
298 ctx.baggage[key.substr(8)] = value;
299 }
300 }
301
302 return ctx;
303 }
304
308 common::Result<std::vector<trace_span>> get_trace(const std::string& trace_id) const;
309
313 common::Result<bool> export_spans(std::vector<trace_span> spans);
314
319 void set_exporter(std::shared_ptr<trace_exporter_interface> exporter);
320
325 std::shared_ptr<trace_exporter_interface> get_exporter() const;
326
332
338
343 common::VoidResult flush();
344
349 std::unordered_map<std::string, std::size_t> get_export_stats() const;
350};
351
356private:
357 std::shared_ptr<trace_span> span_;
359
360public:
361 scoped_span(std::shared_ptr<trace_span> span, distributed_tracer* tracer)
362 : span_(span), tracer_(tracer) {
363 if (tracer_) {
365 }
366 }
367
369 if (span_ && tracer_) {
371 }
372 }
373
374 // Disable copy
375 scoped_span(const scoped_span&) = delete;
377
378 // Enable move
380 : span_(std::move(other.span_)), tracer_(other.tracer_) {
381 other.tracer_ = nullptr;
382 }
383
385 if (this != &other) {
386 if (span_ && tracer_) {
388 }
389 span_ = std::move(other.span_);
390 tracer_ = other.tracer_;
391 other.tracer_ = nullptr;
392 }
393 return *this;
394 }
395
396 trace_span* operator->() { return span_.get(); }
397 const trace_span* operator->() const { return span_.get(); }
398 trace_span& operator*() { return *span_; }
399 const trace_span& operator*() const { return *span_; }
400};
401
406
410#define TRACE_SPAN(operation_name) \
411 auto _span_result = kcenon::monitoring::global_tracer().start_span(operation_name); \
412 kcenon::monitoring::scoped_span _scoped_span( \
413 _span_result.is_ok() ? _span_result.value() : nullptr, \
414 &kcenon::monitoring::global_tracer() \
415 )
416
417#define TRACE_CHILD_SPAN(parent, operation_name) \
418 auto _child_span_result = kcenon::monitoring::global_tracer().start_child_span(parent, operation_name); \
419 kcenon::monitoring::scoped_span _child_scoped_span( \
420 _child_span_result.is_ok() ? _child_span_result.value() : nullptr, \
421 &kcenon::monitoring::global_tracer() \
422 )
423
424} } // namespace kcenon::monitoring
Distributed tracer for managing spans and traces.
distributed_tracer & operator=(const distributed_tracer &)=delete
std::shared_ptr< trace_span > get_current_span() const
Get current active span for this thread.
std::unordered_map< std::string, std::size_t > get_export_stats() const
Get export statistics.
std::shared_ptr< trace_exporter_interface > get_exporter() const
Get the current trace exporter.
common::Result< std::shared_ptr< trace_span > > start_span_from_context(const trace_context &context, const std::string &operation_name)
Start a span from trace context (for incoming requests)
common::Result< trace_context > extract_context_from_carrier(const Carrier &carrier)
Extract trace context from carrier.
std::unique_ptr< tracer_impl > impl_
void configure_export(const trace_export_settings &settings)
Configure export settings.
distributed_tracer(const distributed_tracer &)=delete
trace_export_settings get_export_settings() const
Get current export settings.
trace_context extract_context(const trace_span &span) const
Extract trace context for propagation.
common::Result< bool > export_spans(std::vector< trace_span > spans)
Export spans to external system.
void inject_context(const trace_context &context, Carrier &carrier)
Inject trace context into carrier (e.g., HTTP headers)
common::Result< std::vector< trace_span > > get_trace(const std::string &trace_id) const
Get all spans for a trace.
void set_exporter(std::shared_ptr< trace_exporter_interface > exporter)
Set the trace exporter for span export.
distributed_tracer(distributed_tracer &&) noexcept
common::VoidResult flush()
Manually flush all pending spans to exporter.
void set_current_span(std::shared_ptr< trace_span > span)
Set current active span for this thread.
common::Result< std::shared_ptr< trace_span > > start_span(const std::string &operation_name, const std::string &service_name="monitoring_system")
Start a new root span.
common::Result< bool > finish_span(std::shared_ptr< trace_span > span)
Finish a span.
common::Result< std::shared_ptr< trace_span > > start_child_span(const trace_span &parent, const std::string &operation_name)
Start a child span.
Scoped span for RAII-style span management.
scoped_span(const scoped_span &)=delete
const trace_span * operator->() const
std::shared_ptr< trace_span > span_
scoped_span(scoped_span &&other) noexcept
scoped_span & operator=(const scoped_span &)=delete
const trace_span & operator*() const
scoped_span & operator=(scoped_span &&other) noexcept
scoped_span(std::shared_ptr< trace_span > span, distributed_tracer *tracer)
Span builder for creating new spans.
span_builder & with_baggage(const std::string &key, const std::string &value)
span_builder & with_trace_id(const std::string &id)
span_builder & with_parent(const std::string &parent_id)
span_builder & with_tag(const std::string &key, const std::string &value)
span_builder & with_service(const std::string &name)
span_builder & with_operation(const std::string &name)
static std::string generate_correlation_id()
Generate a unique correlation ID.
static std::string generate_request_id()
Generate a unique request ID.
Monitoring system specific error codes.
distributed_tracer & global_tracer()
Global tracer instance.
Result pattern type definitions for monitoring system.
Extended error information with context.
common::error_info to_common_error() const
Convert to common_system error_info.
Trace context for propagation across service boundaries.
std::string to_w3c_traceparent() const
Serialize to W3C Trace Context format.
std::unordered_map< std::string, std::string > baggage
static common::Result< trace_context > from_w3c_traceparent(const std::string &header)
Parse from W3C Trace Context format.
Configuration settings for trace export behavior.
std::size_t max_queue_size
Maximum spans in queue before dropping.
bool export_on_finish
Export when batch is full.
std::chrono::milliseconds flush_interval
Interval for automatic flush.
std::size_t batch_size
Number of spans to batch before export.
Trace span representing a unit of work in distributed tracing.
void calculate_duration()
Calculate duration if span is finished.
std::unordered_map< std::string, std::string > tags
std::unordered_map< std::string, std::string > baggage
std::chrono::system_clock::time_point end_time
std::chrono::microseconds duration
bool is_finished() const
Check if span has finished.
std::chrono::system_clock::time_point start_time
Thread-local context management for request tracking and distributed tracing.