Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
distributed_tracing_example.cpp
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
17#include <iostream>
18#include <thread>
19#include <map>
20#include <vector>
21
24
25using namespace kcenon::monitoring;
26using namespace std::chrono_literals;
27
28// Simulate a web service that processes requests
30private:
32 std::string service_name_;
33
34public:
35 WebService(distributed_tracer& tracer, const std::string& name)
36 : tracer_(tracer), service_name_(name) {}
37
38 // Simulate handling an HTTP request
39 void handle_request(const std::string& request_id,
40 const std::map<std::string, std::string>& headers) {
41 std::cout << "[" << service_name_ << "] Processing request: " << request_id << std::endl;
42
43 // Extract trace context from incoming headers
44 auto context_result = tracer_.extract_context_from_carrier(headers);
45
46 std::shared_ptr<trace_span> span;
47
48 if (context_result.is_ok()) {
49 // Continue existing trace
50 auto span_result = tracer_.start_span_from_context(
51 context_result.value(),
52 service_name_ + "_handler"
53 );
54 if (span_result.is_ok()) {
55 span = span_result.value();
56 std::cout << "[" << service_name_ << "] Continuing trace: "
57 << context_result.value().trace_id << std::endl;
58 }
59 } else {
60 // Start new trace
61 auto span_result = tracer_.start_span(
62 service_name_ + "_handler",
64 );
65 if (span_result.is_ok()) {
66 span = span_result.value();
67 std::cout << "[" << service_name_ << "] Started new trace: "
68 << span->trace_id << std::endl;
69 }
70 }
71
72 if (span) {
73 // Add tags to the span
74 span->tags["service"] = service_name_;
75 span->tags["request_id"] = request_id;
76 span->tags["http.method"] = "GET";
77 span->tags["http.url"] = "/api/process";
78 span->tags["user.id"] = "user123";
79
80 // Simulate processing
82
83 // Simulate calling downstream service
85
86 // Mark span as successful
87 span->status = trace_span::status_code::ok;
88
89 // Finish the span
90 tracer_.finish_span(span);
91
92 std::cout << "[" << service_name_ << "] Span completed: "
93 << span->span_id << std::endl;
94 }
95 }
96
97private:
98 void process_business_logic(std::shared_ptr<trace_span> parent_span) {
99 // Create a child span for business logic
100 auto span_result = tracer_.start_child_span(*parent_span, "business_logic");
101 if (!span_result.is_ok()) return;
102
103 auto span = span_result.value();
104 span->tags["operation"] = "data_processing";
105
106 std::cout << "[" << service_name_ << "] Processing business logic..." << std::endl;
107
108 // Simulate some work
109 std::this_thread::sleep_for(50ms);
110
111 // Simulate database query
112 query_database(span);
113
114 tracer_.finish_span(span);
115 }
116
117 void query_database(std::shared_ptr<trace_span> parent_span) {
118 // Create a child span for database query
119 auto span_result = tracer_.start_child_span(*parent_span, "database_query");
120 if (!span_result.is_ok()) return;
121
122 auto span = span_result.value();
123 span->tags["db.type"] = "postgresql";
124 span->tags["db.statement"] = "SELECT * FROM users WHERE id = ?";
125
126 std::cout << "[" << service_name_ << "] Querying database..." << std::endl;
127
128 // Simulate database query
129 std::this_thread::sleep_for(20ms);
130
131 tracer_.finish_span(span);
132 }
133
134 void call_downstream_service(std::shared_ptr<trace_span> parent_span) {
135 // Create a child span for downstream call
136 auto span_result = tracer_.start_child_span(*parent_span, "downstream_call");
137 if (!span_result.is_ok()) return;
138
139 auto span = span_result.value();
140 span->tags["peer.service"] = "downstream_service";
141 span->tags["span.kind"] = "client";
142
143 // Extract context to propagate to downstream service
144 auto context = tracer_.extract_context(*span);
145
146 // Prepare headers for downstream call
147 std::map<std::string, std::string> headers;
148 tracer_.inject_context(context, headers);
149
150 std::cout << "[" << service_name_ << "] Calling downstream service..." << std::endl;
151 std::cout << " Propagating trace: " << context.trace_id << std::endl;
152
153 // Simulate HTTP call with headers
154 std::this_thread::sleep_for(30ms);
155
156 tracer_.finish_span(span);
157 }
158};
159
160// Simulate distributed system with multiple services
162 distributed_tracer tracer;
163
164 // Create services
165 WebService frontend(tracer, "frontend");
166 WebService backend(tracer, "backend");
167 WebService database_service(tracer, "database_service");
168
169 std::cout << "\n=== Simulating distributed request flow ===" << std::endl;
170
171 // Simulate incoming request to frontend
172 std::map<std::string, std::string> initial_headers;
173 initial_headers["user-agent"] = "example-client";
174
175 // Process request through frontend
176 frontend.handle_request("req-001", initial_headers);
177
178 std::cout << "\n=== Simulating request with existing trace ===" << std::endl;
179
180 // Simulate request with existing trace context (e.g., from another service)
181 std::map<std::string, std::string> traced_headers;
182 traced_headers["traceparent"] = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01";
183 traced_headers["baggage-user-id"] = "user456";
184
185 backend.handle_request("req-002", traced_headers);
186}
187
188// Demonstrate trace analysis
190 std::cout << "\n=== Analyzing traces ===" << std::endl;
191
192 // Create a sample trace for analysis
193 auto root_span_result = tracer.start_span("analyze_operation", "analyzer");
194 if (!root_span_result.is_ok()) return;
195
196 auto root_span = root_span_result.value();
197 root_span->tags["analysis.type"] = "performance";
198
199 // Create multiple child spans to simulate complex operation
200 std::vector<std::shared_ptr<trace_span>> child_spans;
201
202 for (int i = 0; i < 5; ++i) {
203 auto child_result = tracer.start_child_span(*root_span,
204 "sub_operation_" + std::to_string(i));
205
206 if (child_result.is_ok()) {
207 auto child = child_result.value();
208 child->tags["index"] = std::to_string(i);
209 child->tags["complexity"] = (i % 2 == 0) ? "low" : "high";
210
211 // Simulate varying durations
212 std::this_thread::sleep_for(std::chrono::milliseconds(10 * (i + 1)));
213
214 child_spans.push_back(child);
215 }
216 }
217
218 // Finish all child spans
219 for (auto& child : child_spans) {
220 tracer.finish_span(child);
221 }
222
223 // Simulate an error in one operation
224 auto error_span_result = tracer.start_child_span(*root_span, "failing_operation");
225 if (error_span_result.is_ok()) {
226 auto error_span = error_span_result.value();
227 error_span->status = trace_span::status_code::error;
228 error_span->status_message = "Database connection timeout";
229 error_span->tags["error"] = "true";
230 error_span->tags["error.type"] = "timeout";
231
232 tracer.finish_span(error_span);
233 }
234
235 tracer.finish_span(root_span);
236
237 // Get all spans for the trace
238 auto trace_result = tracer.get_trace(root_span->trace_id);
239 if (trace_result.is_ok()) {
240 auto& spans = trace_result.value();
241
242 std::cout << "Trace ID: " << root_span->trace_id << std::endl;
243 std::cout << "Total spans in trace: " << spans.size() << std::endl;
244
245 // Calculate total duration
246 if (!spans.empty()) {
247 auto min_time = spans[0].start_time;
248 auto max_time = spans[0].end_time;
249
250 for (const auto& span : spans) {
251 if (span.start_time < min_time) min_time = span.start_time;
252 if (span.end_time > max_time) max_time = span.end_time;
253 }
254
255 auto total_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
256 max_time - min_time
257 );
258
259 std::cout << "Total trace duration: " << total_duration.count() << " ms" << std::endl;
260 }
261
262 // Count errors
263 int error_count = 0;
264 for (const auto& span : spans) {
265 if (span.status == trace_span::status_code::error) {
266 error_count++;
267 std::cout << "Error in span: " << span.operation_name
268 << " - " << span.status_message << std::endl;
269 }
270 }
271
272 std::cout << "Spans with errors: " << error_count << std::endl;
273 }
274}
275
276int main() {
277 std::cout << "=== Distributed Tracing Example ===" << std::endl;
278
279 try {
280 // Part 1: Basic span creation and management
281 std::cout << "\n--- Part 1: Basic Span Management ---" << std::endl;
282
283 distributed_tracer tracer;
284
285 // Create a root span
286 auto root_span_result = tracer.start_span("main_operation", "example_service");
287 if (!root_span_result.is_ok()) {
288 std::cerr << "Failed to create root span" << std::endl;
289 return 1;
290 }
291
292 auto root_span = root_span_result.value();
293
294 std::cout << "Created root span:" << std::endl;
295 std::cout << " Trace ID: " << root_span->trace_id << std::endl;
296 std::cout << " Span ID: " << root_span->span_id << std::endl;
297 std::cout << " Operation: " << root_span->operation_name << std::endl;
298
299 // Add tags
300 root_span->tags["version"] = "1.0.0";
301 root_span->tags["environment"] = "development";
302
303 // Add baggage (propagated to child spans)
304 root_span->baggage["user.id"] = "user789";
305 root_span->baggage["session.id"] = "sess123";
306
307 // Create child spans
308 std::cout << "\nCreating child spans..." << std::endl;
309
310 auto child1_result = tracer.start_child_span(*root_span, "child_operation_1");
311 auto child2_result = tracer.start_child_span(*root_span, "child_operation_2");
312
313 if (child1_result.is_ok() && child2_result.is_ok()) {
314 auto child1 = child1_result.value();
315 auto child2 = child2_result.value();
316
317 std::cout << " Child 1 span ID: " << child1->span_id << std::endl;
318 std::cout << " Child 2 span ID: " << child2->span_id << std::endl;
319
320 // Simulate some work
321 std::this_thread::sleep_for(10ms);
322
323 // Finish child spans
324 tracer.finish_span(child1);
325 tracer.finish_span(child2);
326 }
327
328 // Finish root span
329 tracer.finish_span(root_span);
330
331 std::cout << "All spans finished" << std::endl;
332
333 // Part 2: Distributed system simulation
334 std::cout << "\n--- Part 2: Distributed System Simulation ---" << std::endl;
336
337 // Part 3: Trace analysis
338 std::cout << "\n--- Part 3: Trace Analysis ---" << std::endl;
339 analyze_traces(tracer);
340
341 // Part 4: Context propagation demonstration
342 std::cout << "\n--- Part 4: Context Propagation ---" << std::endl;
343
344 auto demo_span_result = tracer.start_span("propagation_demo");
345 if (demo_span_result.is_ok()) {
346 auto demo_span = demo_span_result.value();
347
348 // Extract context for propagation
349 auto context = tracer.extract_context(*demo_span);
350
351 // Simulate HTTP headers
352 std::map<std::string, std::string> http_headers;
353 tracer.inject_context(context, http_headers);
354
355 std::cout << "Context injected into headers:" << std::endl;
356 for (const auto& [key, value] : http_headers) {
357 std::cout << " " << key << ": " << value << std::endl;
358 }
359
360 // Simulate receiving headers in another service
361 auto extracted_context = tracer.extract_context_from_carrier(http_headers);
362 if (extracted_context.is_ok()) {
363 std::cout << "\nContext extracted from headers:" << std::endl;
364 std::cout << " Trace ID: " << extracted_context.value().trace_id << std::endl;
365 std::cout << " Span ID: " << extracted_context.value().span_id << std::endl;
366
367 // Continue trace in new service
368 auto continued_span = tracer.start_span_from_context(
369 extracted_context.value(),
370 "continued_operation"
371 );
372
373 if (continued_span.is_ok()) {
374 std::cout << " Continued span ID: "
375 << continued_span.value()->span_id << std::endl;
376 tracer.finish_span(continued_span.value());
377 }
378 }
379
380 tracer.finish_span(demo_span);
381 }
382
383 } catch (const std::exception& e) {
384 std::cerr << "Exception: " << e.what() << std::endl;
385 return 1;
386 }
387
388 std::cout << "\n=== Example completed successfully ===" << std::endl;
389
390 return 0;
391}
void handle_request(const std::string &request_id, const std::map< std::string, std::string > &headers)
void process_business_logic(std::shared_ptr< trace_span > parent_span)
WebService(distributed_tracer &tracer, const std::string &name)
void call_downstream_service(std::shared_ptr< trace_span > parent_span)
distributed_tracer & tracer_
void query_database(std::shared_ptr< trace_span > parent_span)
Distributed tracer for managing spans and traces.
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.
trace_context extract_context(const trace_span &span) const
Extract trace context for propagation.
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.
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.
Distributed tracing implementation for monitoring system.
void simulate_distributed_system()
void analyze_traces(distributed_tracer &tracer)
Thread-local context management for request tracking and distributed tracing.