Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
multi_service_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
19#include <iostream>
20#include <memory>
21#include <string>
22#include <chrono>
23#include <thread>
24#include <iomanip>
25
27
28using namespace kcenon::monitoring;
29using namespace std::chrono_literals;
30
31// Forward declarations
32void display_span(const trace_span& span, int depth);
33void display_children(const std::vector<trace_span>& spans,
34 const std::string& parent_id,
35 int depth);
36
41private:
42 std::string service_name_ = "api_gateway";
43
44public:
48 trace_span handle_request(const std::string& endpoint, const std::string& method) {
49 std::cout << "\n[" << service_name_ << "] Processing " << method << " " << endpoint << std::endl;
50
51 // Create root span for this request
52 trace_span span;
55 span.operation_name = "http_request";
57 span.start_time = std::chrono::system_clock::now();
58
59 // Add HTTP-specific tags
60 span.tags["http.method"] = method;
61 span.tags["http.url"] = endpoint;
62 span.tags["http.target"] = endpoint;
63 span.tags["component"] = "http_server";
64
65 // Add baggage for cross-cutting concerns
66 span.baggage["user.id"] = "user-12345";
67 span.baggage["session.id"] = "sess-67890";
68 span.baggage["request.priority"] = "high";
69
70 std::cout << " โ†’ Created root span: " << span.span_id << std::endl;
71 std::cout << " Trace ID: " << span.trace_id << std::endl;
72 std::cout << " Baggage: user.id=" << span.baggage["user.id"] << std::endl;
73
74 return span;
75 }
76
81 trace_context ctx;
82 ctx.trace_id = span.trace_id;
83 ctx.span_id = span.span_id;
84 ctx.trace_flags = "01"; // Sampled
85 ctx.baggage = span.baggage;
86
87 std::cout << " โ†’ Context for propagation: "
88 << ctx.to_w3c_traceparent() << std::endl;
89
90 return ctx;
91 }
92
93private:
94 static std::string generate_trace_id() {
95 // Generate 32-character hex string (16 bytes)
96 std::string id;
97 for (int i = 0; i < 32; ++i) {
98 id += "0123456789abcdef"[rand() % 16];
99 }
100 return id;
101 }
102
103 static std::string generate_span_id() {
104 // Generate 16-character hex string (8 bytes)
105 std::string id;
106 for (int i = 0; i < 16; ++i) {
107 id += "0123456789abcdef"[rand() % 16];
108 }
109 return id;
110 }
111};
112
117private:
118 std::string service_name_ = "auth_service";
119
120public:
124 trace_span verify_token(const trace_context& parent_ctx, const std::string& /* token */) {
125 std::cout << "\n[" << service_name_ << "] Verifying authentication token" << std::endl;
126 std::cout << " Received context: " << parent_ctx.to_w3c_traceparent() << std::endl;
127
128 // Create child span
129 trace_span span;
130 span.trace_id = parent_ctx.trace_id; // Same trace ID
131 span.span_id = generate_span_id(); // New span ID
132 span.parent_span_id = parent_ctx.span_id; // Link to parent
133 span.operation_name = "verify_token";
135 span.start_time = std::chrono::system_clock::now();
136
137 // Inherit baggage from parent
138 span.baggage = parent_ctx.baggage;
139
140 // Add service-specific tags
141 span.tags["auth.token_type"] = "bearer";
142 span.tags["auth.method"] = "jwt";
143
144 std::cout << " โ†’ Created child span: " << span.span_id << std::endl;
145 std::cout << " Parent span: " << span.parent_span_id << std::endl;
146 std::cout << " Inherited baggage: user.id=" << span.baggage["user.id"] << std::endl;
147
148 // Simulate authentication work
149 std::this_thread::sleep_for(5ms);
150
151 span.end_time = std::chrono::system_clock::now();
152 span.calculate_duration();
153 span.status = trace_span::status_code::ok;
154 span.tags["auth.result"] = "success";
155
156 std::cout << " โœ“ Authentication successful (duration: "
157 << span.duration.count() << "ยตs)" << std::endl;
158
159 return span;
160 }
161
162private:
163 static std::string generate_span_id() {
164 std::string id;
165 for (int i = 0; i < 16; ++i) {
166 id += "0123456789abcdef"[rand() % 16];
167 }
168 return id;
169 }
170};
171
176private:
177 std::string service_name_ = "user_service";
178
179public:
183 trace_span get_user_profile(const trace_context& parent_ctx, const std::string& user_id) {
184 std::cout << "\n[" << service_name_ << "] Fetching user profile" << std::endl;
185 std::cout << " User ID from baggage: " << parent_ctx.baggage.at("user.id") << std::endl;
186
187 // Create child span
188 trace_span span;
189 span.trace_id = parent_ctx.trace_id;
190 span.span_id = generate_span_id();
191 span.parent_span_id = parent_ctx.span_id;
192 span.operation_name = "get_user_profile";
194 span.start_time = std::chrono::system_clock::now();
195
196 // Inherit baggage
197 span.baggage = parent_ctx.baggage;
198
199 // Add database query tags
200 span.tags["db.system"] = "postgresql";
201 span.tags["db.name"] = "users_db";
202 span.tags["db.statement"] = "SELECT * FROM users WHERE id = ?";
203 span.tags["db.user_id"] = user_id;
204
205 std::cout << " โ†’ Created child span: " << span.span_id << std::endl;
206 std::cout << " Parent span: " << span.parent_span_id << std::endl;
207
208 // Simulate database query
209 std::this_thread::sleep_for(15ms);
210
211 span.end_time = std::chrono::system_clock::now();
212 span.calculate_duration();
213 span.status = trace_span::status_code::ok;
214 span.tags["db.rows_returned"] = "1";
215
216 std::cout << " โœ“ User profile fetched (duration: "
217 << span.duration.count() << "ยตs)" << std::endl;
218
219 return span;
220 }
221
222private:
223 static std::string generate_span_id() {
224 std::string id;
225 for (int i = 0; i < 16; ++i) {
226 id += "0123456789abcdef"[rand() % 16];
227 }
228 return id;
229 }
230};
231
236private:
237 std::string service_name_ = "cache_service";
238
239public:
243 trace_span cache_lookup(const trace_context& parent_ctx, const std::string& key) {
244 std::cout << "\n[" << service_name_ << "] Cache lookup" << std::endl;
245
246 // Create child span
247 trace_span span;
248 span.trace_id = parent_ctx.trace_id;
249 span.span_id = generate_span_id();
250 span.parent_span_id = parent_ctx.span_id;
251 span.operation_name = "cache_get";
253 span.start_time = std::chrono::system_clock::now();
254
255 // Inherit baggage
256 span.baggage = parent_ctx.baggage;
257
258 // Add cache-specific tags
259 span.tags["cache.type"] = "redis";
260 span.tags["cache.key"] = key;
261
262 std::cout << " โ†’ Created child span: " << span.span_id << std::endl;
263
264 // Simulate cache miss
265 std::this_thread::sleep_for(2ms);
266
267 span.end_time = std::chrono::system_clock::now();
268 span.calculate_duration();
269 span.status = trace_span::status_code::ok;
270 span.tags["cache.hit"] = "false";
271
272 std::cout << " โ—‹ Cache miss (duration: "
273 << span.duration.count() << "ยตs)" << std::endl;
274
275 return span;
276 }
277
278private:
279 static std::string generate_span_id() {
280 std::string id;
281 for (int i = 0; i < 16; ++i) {
282 id += "0123456789abcdef"[rand() % 16];
283 }
284 return id;
285 }
286};
287
291void display_trace_tree(const std::vector<trace_span>& spans) {
292 std::cout << "\n=== Trace Tree Structure ===" << std::endl;
293
294 // Find root span
295 const trace_span* root = nullptr;
296 for (const auto& span : spans) {
297 if (span.parent_span_id.empty()) {
298 root = &span;
299 break;
300 }
301 }
302
303 if (!root) {
304 std::cout << "No root span found" << std::endl;
305 return;
306 }
307
308 std::cout << "\nTrace ID: " << root->trace_id << "\n" << std::endl;
309
310 // Display root
311 display_span(*root, 0);
312
313 // Display children
314 display_children(spans, root->span_id, 1);
315
316 std::cout << "\n=== Total Spans: " << spans.size() << " ===" << std::endl;
317 std::cout << "Total trace duration: " << root->duration.count() << "ยตs" << std::endl;
318}
319
323void display_span(const trace_span& span, int depth) {
324 std::string indent(depth * 2, ' ');
325 std::string prefix = depth == 0 ? "โ”Œโ”€" : "โ”œโ”€";
326
327 std::cout << indent << prefix << " [" << span.service_name << "] "
328 << span.operation_name << std::endl;
329 std::cout << indent << " Span ID: " << span.span_id << std::endl;
330 std::cout << indent << " Duration: " << span.duration.count() << "ยตs" << std::endl;
331
332 if (!span.tags.empty()) {
333 std::cout << indent << " Tags:";
334 int count = 0;
335 for (const auto& [key, value] : span.tags) {
336 if (count++ < 3) { // Show first 3 tags
337 std::cout << " " << key << "=" << value;
338 }
339 }
340 if (span.tags.size() > 3) {
341 std::cout << " +" << (span.tags.size() - 3) << " more";
342 }
343 std::cout << std::endl;
344 }
345}
346
350void display_children(const std::vector<trace_span>& spans,
351 const std::string& parent_id,
352 int depth) {
353 for (const auto& span : spans) {
354 if (span.parent_span_id == parent_id) {
355 display_span(span, depth);
356 display_children(spans, span.span_id, depth + 1);
357 }
358 }
359}
360
365 std::cout << "=== Multi-Service Distributed Tracing Example ===" << std::endl;
366
367 std::vector<trace_span> collected_spans;
368
369 // Step 1: API Gateway receives request
370 std::cout << "\n--- Step 1: API Gateway ---" << std::endl;
371 ApiGatewayService gateway;
372 auto gateway_span = gateway.handle_request("/api/user/profile", "GET");
373 auto gateway_ctx = gateway.create_context(gateway_span);
374
375 // Step 2: Check cache (parallel to auth)
376 std::cout << "\n--- Step 2: Cache Lookup ---" << std::endl;
377 CacheService cache;
378 auto cache_span = cache.cache_lookup(gateway_ctx, "user:user-12345");
379 collected_spans.push_back(cache_span);
380
381 // Step 3: Authenticate request
382 std::cout << "\n--- Step 3: Authentication ---" << std::endl;
383 AuthService auth;
384 auto auth_span = auth.verify_token(gateway_ctx, "eyJhbGc...");
385 collected_spans.push_back(auth_span);
386
387 // Create context for user service call
388 trace_context user_ctx;
389 user_ctx.trace_id = gateway_ctx.trace_id;
390 user_ctx.span_id = gateway_span.span_id;
391 user_ctx.baggage = gateway_ctx.baggage;
392
393 // Step 4: Fetch user profile (after cache miss)
394 std::cout << "\n--- Step 4: User Profile Service ---" << std::endl;
395 UserService user_service;
396 auto user_span = user_service.get_user_profile(user_ctx, "user-12345");
397 collected_spans.push_back(user_span);
398
399 // Complete gateway span
400 std::this_thread::sleep_for(5ms);
401 gateway_span.end_time = std::chrono::system_clock::now();
402 gateway_span.calculate_duration();
403 gateway_span.status = trace_span::status_code::ok;
404 gateway_span.tags["http.status_code"] = "200";
405 collected_spans.insert(collected_spans.begin(), gateway_span);
406
407 // Display complete trace
408 display_trace_tree(collected_spans);
409
410 // Show baggage propagation
411 std::cout << "\n=== Baggage Propagation ===" << std::endl;
412 std::cout << "Baggage items propagated across all services:" << std::endl;
413 for (const auto& [key, value] : gateway_span.baggage) {
414 std::cout << " " << key << " = " << value << std::endl;
415 }
416
417 std::cout << "\n=== Example completed successfully ===" << std::endl;
418}
419
420int main() {
421 std::cout << "Multi-Service Distributed Tracing Example\n" << std::endl;
422
424
425 std::cout << "\n" << std::string(60, '=') << std::endl;
426 std::cout << "\nKey Concepts Demonstrated:" << std::endl;
427 std::cout << "1. Trace context propagation across services" << std::endl;
428 std::cout << "2. Parent-child span relationships" << std::endl;
429 std::cout << "3. Baggage propagation for cross-cutting data" << std::endl;
430 std::cout << "4. W3C Trace Context standard" << std::endl;
431 std::cout << "5. Service-specific span tags and metadata" << std::endl;
432
433 return 0;
434}
Simulated API Gateway service.
static std::string generate_trace_id()
trace_span handle_request(const std::string &endpoint, const std::string &method)
Handle incoming HTTP request.
trace_context create_context(const trace_span &span)
Create trace context for propagation.
static std::string generate_span_id()
Simulated Authentication service.
trace_span verify_token(const trace_context &parent_ctx, const std::string &)
Verify user authentication.
static std::string generate_span_id()
Simulated Cache service.
static std::string generate_span_id()
trace_span cache_lookup(const trace_context &parent_ctx, const std::string &key)
Check cache for user data.
Simulated User service.
trace_span get_user_profile(const trace_context &parent_ctx, const std::string &user_id)
Fetch user profile.
static std::string generate_span_id()
Distributed tracing implementation for monitoring system.
void display_children(const std::vector< trace_span > &spans, const std::string &parent_id, int depth)
Display children recursively.
void simulate_multi_service_request()
Simulate complete multi-service request flow.
void display_trace_tree(const std::vector< trace_span > &spans)
Display trace tree structure.
void display_span(const trace_span &span, int depth)
Display single span.
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
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
std::chrono::system_clock::time_point start_time