| // Copyright 2026 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| #include "tracing/test_tracing.h" | |
| #include <atomic> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/status/status.h" | |
| #include "absl/strings/str_format.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/synchronization/mutex.h" | |
| #include "absl/time/clock.h" | |
| #include "absl/time/time.h" | |
| #include "tracing/span.h" | |
| #include "tracing/tracer.h" | |
| #include "tracing/tracer_factory.h" | |
| namespace credentio_tracing { | |
| namespace { | |
| std::string GenerateTraceId() { | |
| static std::atomic<uint64_t> counter{1}; | |
| uint64_t id = counter.fetch_add(1, std::memory_order_relaxed); | |
| return absl::StrFormat("%032x", id); | |
| } | |
| std::string GenerateSpanId() { | |
| static std::atomic<uint64_t> counter{1}; | |
| uint64_t id = counter.fetch_add(1, std::memory_order_relaxed); | |
| return absl::StrFormat("%016x", id); | |
| } | |
| } // namespace | |
| void TestRecordedSpan::SetAttribute(absl::string_view key, | |
| AttributeValue value) { | |
| absl::MutexLock lock(mutex_); | |
| for (auto& [k, v] : attributes_) { | |
| if (k == key) { | |
| v = std::move(value); | |
| return; | |
| } | |
| } | |
| attributes_.push_back({std::string(key), std::move(value)}); | |
| } | |
| void TestRecordedSpan::AddEvent(absl::string_view name) { | |
| absl::MutexLock lock(mutex_); | |
| events_.push_back({std::string(name), absl::Now()}); | |
| } | |
| void TestRecordedSpan::SetStatus(const absl::Status& status) { | |
| absl::MutexLock lock(mutex_); | |
| status_ = status; | |
| } | |
| void TestRecordedSpan::End() { | |
| absl::MutexLock lock(mutex_); | |
| ended_ = true; | |
| end_time_ = absl::Now(); | |
| } | |
| bool TestRecordedSpan::ended() const { | |
| absl::MutexLock lock(mutex_); | |
| return ended_; | |
| } | |
| absl::Status TestRecordedSpan::status() const { | |
| absl::MutexLock lock(mutex_); | |
| return status_; | |
| } | |
| absl::Time TestRecordedSpan::end_time() const { | |
| absl::MutexLock lock(mutex_); | |
| return end_time_; | |
| } | |
| std::vector<std::pair<std::string, AttributeValue>> | |
| TestRecordedSpan::attributes() const { | |
| absl::MutexLock lock(mutex_); | |
| return attributes_; | |
| } | |
| std::vector<TestRecordedEvent> TestRecordedSpan::events() const { | |
| absl::MutexLock lock(mutex_); | |
| return events_; | |
| } | |
| std::optional<AttributeValue> TestRecordedSpan::GetAttribute( | |
| absl::string_view key) const { | |
| absl::MutexLock lock(mutex_); | |
| for (const auto& [k, v] : attributes_) { | |
| if (k == key) { | |
| return v; | |
| } | |
| } | |
| return std::nullopt; | |
| } | |
| TestSpan::TestSpan(std::shared_ptr<TestRecordedSpan> recorded_span) | |
| : recorded_span_(std::move(recorded_span)) {} | |
| void TestSpan::SetAttribute(absl::string_view key, AttributeValue value) { | |
| if (recorded_span_) { | |
| recorded_span_->SetAttribute(key, std::move(value)); | |
| } | |
| } | |
| void TestSpan::AddEvent(absl::string_view name) { | |
| if (recorded_span_) { | |
| recorded_span_->AddEvent(name); | |
| } | |
| } | |
| void TestSpan::SetStatus(const absl::Status& status) { | |
| if (recorded_span_) { | |
| recorded_span_->SetStatus(status); | |
| } | |
| } | |
| void TestSpan::End() { | |
| if (recorded_span_) { | |
| recorded_span_->End(); | |
| } | |
| } | |
| std::shared_ptr<TestRecordedSpan> TestSpan::recorded_span() const { | |
| return recorded_span_; | |
| } | |
| std::unique_ptr<Span> TestTracer::StartSpanImpl( | |
| absl::string_view span_name, const StartSpanOptions& options) const { | |
| auto recorded = std::make_shared<TestRecordedSpan>(); | |
| recorded->name_ = std::string(span_name); | |
| recorded->start_time_ = absl::Now(); | |
| recorded->span_id_ = GenerateSpanId(); | |
| for (const auto& [k, v] : options.attributes) { | |
| recorded->SetAttribute(k, v); | |
| } | |
| // `Tracer::StartSpan` already resolves the implicit parent from the current | |
| // `ThreadContext`, so `options.parent` is authoritative here. | |
| recorded->parent_ = options.parent; | |
| // `dynamic_cast` yields nullptr for both a null parent and a parent that is | |
| // not a `TestSpan`, in which case the span starts a new trace. | |
| const auto* test_parent = dynamic_cast<const TestSpan*>(options.parent); | |
| if (test_parent != nullptr && test_parent->recorded_span() != nullptr) { | |
| recorded->parent_span_id_ = test_parent->recorded_span()->span_id_; | |
| recorded->trace_id_ = test_parent->recorded_span()->trace_id_; | |
| } | |
| if (recorded->trace_id_.empty()) { | |
| recorded->trace_id_ = GenerateTraceId(); | |
| } | |
| absl::MutexLock lock(mutex_); | |
| spans_.push_back(recorded); | |
| return std::make_unique<TestSpan>(recorded); | |
| } | |
| std::vector<std::shared_ptr<TestRecordedSpan>> TestTracer::GetSpans() const { | |
| absl::MutexLock lock(mutex_); | |
| return spans_; | |
| } | |
| std::shared_ptr<TestRecordedSpan> TestTracer::GetSpan( | |
| absl::string_view name) const { | |
| absl::MutexLock lock(mutex_); | |
| for (const auto& span : spans_) { | |
| if (span->name_ == name) { | |
| return span; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| std::vector<std::shared_ptr<TestRecordedSpan>> TestTracer::GetSpansByName( | |
| absl::string_view name) const { | |
| absl::MutexLock lock(mutex_); | |
| std::vector<std::shared_ptr<TestRecordedSpan>> result; | |
| for (const auto& span : spans_) { | |
| if (span->name_ == name) { | |
| result.push_back(span); | |
| } | |
| } | |
| return result; | |
| } | |
| void TestTracer::Clear() { | |
| absl::MutexLock lock(mutex_); | |
| spans_.clear(); | |
| } | |
| std::shared_ptr<TestTracerFactory> TestTracerFactory::Register() { | |
| auto factory = std::make_shared<TestTracerFactory>(); | |
| TracerFactory::RegisterGlobalFactory(factory); | |
| return factory; | |
| } | |
| void TestTracerFactory::Register(std::shared_ptr<TestTracerFactory> factory) { | |
| TracerFactory::RegisterGlobalFactory(std::move(factory)); | |
| } | |
| void TestTracerFactory::RegisterThis() { | |
| TracerFactory::RegisterGlobalFactory(shared_from_this()); | |
| } | |
| void TestTracerFactory::Unregister() { | |
| TracerFactory::RegisterGlobalFactory(nullptr); | |
| } | |
| std::shared_ptr<Tracer> TestTracerFactory::GetTracer( | |
| absl::string_view tracer_name) const { | |
| absl::MutexLock lock(mutex_); | |
| auto& tracer = tracers_[tracer_name]; | |
| if (tracer == nullptr) { | |
| tracer = std::make_shared<TestTracer>(); | |
| } | |
| return tracer; | |
| } | |
| std::shared_ptr<TestTracer> TestTracerFactory::GetTestTracer( | |
| absl::string_view tracer_name) const { | |
| absl::MutexLock lock(mutex_); | |
| auto it = tracers_.find(tracer_name); | |
| if (it != tracers_.end()) { | |
| return it->second; | |
| } | |
| return nullptr; | |
| } | |
| std::vector<std::shared_ptr<TestRecordedSpan>> TestTracerFactory::GetAllSpans() | |
| const { | |
| std::vector<std::shared_ptr<TestTracer>> tracers_copy; | |
| { | |
| absl::MutexLock lock(mutex_); | |
| tracers_copy.reserve(tracers_.size()); | |
| for (const auto& [name, tracer] : tracers_) { | |
| tracers_copy.push_back(tracer); | |
| } | |
| } | |
| std::vector<std::shared_ptr<TestRecordedSpan>> all_spans; | |
| for (const auto& tracer : tracers_copy) { | |
| auto spans = tracer->GetSpans(); | |
| all_spans.insert(all_spans.end(), spans.begin(), spans.end()); | |
| } | |
| return all_spans; | |
| } | |
| void TestTracerFactory::Clear() { | |
| absl::MutexLock lock(mutex_); | |
| tracers_.clear(); | |
| } | |
| } // namespace credentio_tracing |