11#include <gtest/gtest.h>
45 EXPECT_TRUE(metadata.
user_id.empty());
46 EXPECT_FALSE(metadata.
empty());
53 metadata.
set_tag(
"environment",
"production");
54 metadata.
set_tag(
"version",
"1.2.3");
57 auto env = metadata.
get_tag(
"environment");
58 EXPECT_EQ(env,
"production");
60 auto ver = metadata.
get_tag(
"version");
61 EXPECT_EQ(ver,
"1.2.3");
63 auto missing = metadata.
get_tag(
"nonexistent");
64 EXPECT_EQ(missing,
"");
67 EXPECT_TRUE(metadata.
tags.find(
"environment") != metadata.
tags.end());
68 EXPECT_TRUE(metadata.
tags.find(
"version") != metadata.
tags.end());
71 EXPECT_FALSE(metadata.
empty());
81 metadata1.
set_tag(
"tag1",
"value1");
86 metadata2.
set_tag(
"tag2",
"value2");
94 EXPECT_EQ(copy.
get_tag(
"tag1"),
"value1");
109 EXPECT_EQ(ctx.request_id,
"test-request");
113 ASSERT_NE(current,
nullptr);
114 current->user_id =
"test-user";
115 current->add_tag(
"test",
"value");
120 ASSERT_TRUE(tag.has_value());
121 EXPECT_EQ(tag.value(),
"value");
134 std::set<std::string> ids;
135 for (
int i = 0; i < 100; ++i) {
140 EXPECT_EQ(ids.size(), 100);
144 EXPECT_FALSE(ctx.request_id.empty());
147 auto generated_id = ctx.request_id;
148 EXPECT_EQ(ids.find(generated_id), ids.end());
158 EXPECT_FALSE(corr_id1.empty());
159 EXPECT_FALSE(corr_id2.empty());
160 EXPECT_NE(corr_id1, corr_id2);
163 EXPECT_EQ(corr_id1.substr(0, 5),
"corr-");
164 EXPECT_EQ(corr_id2.substr(0, 5),
"corr-");
176 context_scope scope(
"scoped-request");
199 auto new_metadata = std::make_unique<context_metadata>(
"scoped-request");
200 new_metadata->user_id =
"scoped-user";
201 context_scope scope(std::move(new_metadata),
true);
224 context_propagator propagator;
225 auto capture_result = propagator.capture();
226 ASSERT_TRUE(capture_result);
227 EXPECT_TRUE(propagator.has_captured());
234 auto apply_result = propagator.apply();
235 ASSERT_TRUE(apply_result);
242 ASSERT_TRUE(tag.has_value());
243 EXPECT_EQ(tag.value(),
"main");
256 auto propagator = context_propagator::from_current();
259 std::thread worker([propagator]() {
264 auto result = propagator.apply();
273 ASSERT_TRUE(tag.has_value());
274 EXPECT_EQ(tag.value(),
"main");
284 ASSERT_TRUE(main_tag.has_value());
285 EXPECT_EQ(main_tag.value(),
"main");
293 class test_context_aware :
public context_aware_monitoring {
298 test_context_aware aware;
312 auto result = aware.enrich_with_context(data);
316 auto req_id = data.
get_tag(
"request_id");
317 ASSERT_TRUE(req_id.has_value());
318 EXPECT_EQ(req_id.value(),
"enrich-request");
320 auto corr_id = data.
get_tag(
"correlation_id");
321 ASSERT_TRUE(corr_id.has_value());
322 EXPECT_EQ(corr_id.value(),
"enrich-corr");
324 auto user_id = data.
get_tag(
"user_id");
325 ASSERT_TRUE(user_id.has_value());
326 EXPECT_EQ(user_id.value(),
"enrich-user");
329 auto custom1 = data.
get_tag(
"ctx.custom1");
330 ASSERT_TRUE(custom1.has_value());
331 EXPECT_EQ(custom1.value(),
"value1");
333 auto custom2 = data.
get_tag(
"ctx.custom2");
334 ASSERT_TRUE(custom2.has_value());
335 EXPECT_EQ(custom2.value(),
"value2");
339 ASSERT_TRUE(
metric.has_value());
351 : context_metrics_collector(name) {}
353 kcenon::common::Result<metrics_snapshot> collect()
override {
354 auto snapshot = create_snapshot_with_context();
355 snapshot.add_metric(
"test_metric", 42.0);
356 return kcenon::common::ok(std::move(snapshot));
367 collector.set_context_aware(
true);
368 auto result1 = collector.collect();
369 ASSERT_TRUE(result1);
371 auto snapshot1 = result1.value();
372 EXPECT_EQ(snapshot1.source_id,
"test-collector");
375 auto metric = snapshot1.get_metric(
"test_metric");
376 ASSERT_TRUE(
metric.has_value());
380 collector.set_context_aware(
false);
381 auto result2 = collector.collect();
382 ASSERT_TRUE(result2);
385 auto snapshot2 = result2.value();
386 EXPECT_EQ(snapshot2.source_id,
"test-collector");
393 const int thread_count = 10;
394 std::vector<std::thread> threads;
395 std::vector<std::future<std::string>> futures;
398 for (
int i = 0; i < thread_count; ++i) {
399 std::promise<std::string> promise;
400 futures.push_back(promise.get_future());
402 threads.emplace_back([i, p = std::move(promise)]()
mutable {
404 auto req_id =
"thread-" + std::to_string(i);
409 std::this_thread::sleep_for(std::chrono::milliseconds(10));
413 if (ctx && ctx->request_id == req_id &&
414 ctx->user_id ==
"user-" + std::to_string(i)) {
417 p.set_value(
"error");
423 std::set<std::string> results;
424 for (
auto& future : futures) {
425 results.insert(future.get());
429 for (
auto& t : threads) {
434 EXPECT_EQ(results.size(), thread_count);
435 for (
int i = 0; i < thread_count; ++i) {
436 auto expected =
"thread-" + std::to_string(i);
437 EXPECT_NE(results.find(expected), results.end());
448 source.
user_id =
"source-user";
449 source.add_tag(
"tag1",
"value1");
458 ASSERT_NE(current,
nullptr);
460 EXPECT_EQ(current->request_id,
"source-request");
461 EXPECT_EQ(current->correlation_id,
"source-corr");
462 EXPECT_EQ(current->user_id,
"source-user");
464 auto tag = current->get_tag(
"tag1");
465 ASSERT_TRUE(tag.has_value());
466 EXPECT_EQ(tag.value(),
"value1");
469 current->user_id =
"modified-user";
470 EXPECT_EQ(source.
user_id,
"source-user");
static std::string generate_correlation_id()
Generate a unique correlation ID.
static bool copy_from(const thread_context_data &source)
Copy context data from another source into the current thread.
static void clear()
Clear and destroy the current thread-local context.
static thread_context_data * current()
Get the current thread-local context.
static thread_context_data & create(const std::string &request_id="")
Create a new thread-local context, replacing any existing one.
static std::string generate_request_id()
Generate a unique request ID.
static bool has_context()
Check whether a context exists on the current thread.
Context metadata for thread-specific information.
std::string correlation_id
Correlation ID for tracing across services.
std::unordered_map< std::string, std::string > tags
Arbitrary key-value tags.
std::string request_id
Unique identifier for the current request.
std::string user_id
User identifier associated with the request.
std::string get_tag(const std::string &key) const
Retrieve a tag value by key.
void set_tag(const std::string &key, const std::string &value)
Set a custom tag on this context.
bool empty() const
Check if all metadata fields are empty.
Basic metric structure for interface compatibility.
std::variant< double, int64_t, std::string > value
Container for monitoring metrics from a component.
std::optional< std::string > get_tag(const std::string &key) const
Get a tag value.
std::optional< double > get_metric(const std::string &key) const
Get a metric value.
void add_metric(const std::string &key, double value)
Add a numeric metric.
void add_tag(const std::string &key, const std::string &value)
Add or update a custom tag.
std::string correlation_id
Correlation ID for cross-service tracing.
std::string get_tag(const std::string &key) const
Retrieve a tag value by key.
std::string user_id
User identifier associated with the request.
TEST_F(ThreadContextTest, ContextMetadataBasicOperations)
Thread-local context management for request tracking and distributed tracing.