Skip to content

Support tracing in SDK and add loading to OpenTelemetry client #517

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .devcontainer/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ volumes:
ydb-certs:

services:
jaeger:
image: jaegertracing/all-in-one:1.51.0
ports:
- "16686:16686" # Jaeger UI frontend
- "4317:4317" # gRPC port for accepts traces in OpenTelemetry OTLP format
- "4318:4318" # HTTP port for accepts traces in OpenTelemetry OTLP format
environment:
- COLLECTOR_OTLP_ENABLED=true

sdk:
platform: linux/amd64

Expand Down
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ add_subdirectory(pagination)
add_subdirectory(secondary_index)
add_subdirectory(secondary_index_builtin)
add_subdirectory(topic_reader)
add_subdirectory(tracing)
add_subdirectory(ttl)
add_subdirectory(vector_index)
29 changes: 29 additions & 0 deletions examples/tracing/tracing_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <ydb-cpp-sdk/client/tracing/otel_tracer.h>
#include <opentelemetry/sdk/trace/tracer_provider.h>
#include <opentelemetry/exporters/jaeger/jaeger_exporter.h>
#include <ydb-cpp-sdk/client/driver.h>

int main() {
// 1. Настройка OpenTelemetry с экспортером в Jaeger
auto exporter = opentelemetry::exporter::jaeger::JaegerExporterFactory::Create();
auto provider = opentelemetry::sdk::trace::TracerProviderFactory::Create(std::move(exporter));
auto otel_tracer = provider->GetTracer("ydb-cpp-sdk");

// 2. Создание адаптера для YDB SDK
auto ydb_tracer = std::make_shared<NYdb::NTracing::TOpenTelemetryTracer>(otel_tracer);

// 3. Инициализация драйвера YDB с трейсером
auto driver = NYdb::TDriver(
NYdb::TDriverConfig()
.SetEndpoint("grpc://localhost:2136")
.SetDatabase("/local")
.SetTracer(ydb_tracer)
);

// 4. Тестовый запрос (спан создастся автоматически внутри SDK)
auto client = NYdb::NTable::TTableClient(driver);
auto session = client.CreateSession().GetValueSync();
session.ExecuteDataQuery("SELECT 1", NYdb::NTable::TTxControl::BeginTx().CommitTx()).GetValueSync();

return 0;
}
2 changes: 2 additions & 0 deletions include/ydb-cpp-sdk/client/driver/driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ class TDriverConfig {

//! Log backend.
TDriverConfig& SetLog(std::unique_ptr<TLogBackend>&& log);

TDriverConfig& SetTracer(std::shared_ptr<NTracing::ITracer> tracer);
private:
class TImpl;
std::shared_ptr<TImpl> Impl_;
Expand Down
22 changes: 22 additions & 0 deletions include/ydb-cpp-sdk/client/tracing/noop_tracer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <ydb-cpp-sdk/client/tracing/tracer.h>

namespace NYdb::inline V3 {
namespace NTracing {

class TNoopSpan : public ISpan {
public:
void AddAttribute(const std::string&, const std::string&) override {}
void End() override {}
};

class TNoopTracer : public ITracer {
public:
std::unique_ptr<ISpan> StartSpan(const std::string&) override {
return std::make_unique<TNoopSpan>();
}
};

} // namespace NTracing
} // namespace NYdb
106 changes: 106 additions & 0 deletions include/ydb-cpp-sdk/client/tracing/otel_tracer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#pragma once

#include "tracer.h"

#include <opentelemetry/trace/tracer.h>
#include <opentelemetry/trace/span.h>
#include <opentelemetry/trace/span_context.h>
#include <opentelemetry/context/runtime_context.h>
#include <opentelemetry/common/key_value_iterable_view.h>

namespace NYdb {
namespace NTracing {

class OpenTelemetrySpan : public ISpan {
public:
OpenTelemetrySpan(opentelemetry::trace::Span span, std::shared_ptr<TTraceContext> context)
: span_(std::move(span)), context_(std::move(context)) {}

void AddAttribute(const std::string& key, const std::string& value) override {
span_.SetAttribute(key, value);
}

void AddEvent(const std::string& name, const TAttributeMap& attributes = {}) override {
// Преобразуем std::unordered_map в вектор KeyValue для OpenTelemetry
std::vector<opentelemetry::common::KeyValue> otelAttributes;
otelAttributes.reserve(attributes.size());
for (const auto& [k, v] : attributes) {
otelAttributes.emplace_back(k, v);
}
span_.AddEvent(name, otelAttributes);
}

void SetStatus(bool isError, const std::string& description = "") override {
span_.SetStatus(isError ? opentelemetry::trace::StatusCode::kError
: opentelemetry::trace::StatusCode::kOk,
description);
}

void End() override {
span_.End();
}

const TTraceContext& GetContext() const override {
return *context_;
}

private:
opentelemetry::trace::Span span_;
std::shared_ptr<TTraceContext> context_;
};


class OpenTelemetryTracer : public ITracer {
public:
explicit OpenTelemetryTracer(std::shared_ptr<opentelemetry::trace::Tracer> tracer)
: tracer_(std::move(tracer)) {}

std::unique_ptr<ISpan> StartSpan(
const std::string& name,
const TAttributeMap& attributes = {},
std::shared_ptr<TTraceContext> parentContext = nullptr
) override {
opentelemetry::context::Context otelContext;

if (parentContext) {
auto traceId = opentelemetry::trace::TraceId::FromHex(parentContext->GetTraceId());
auto spanId = opentelemetry::trace::SpanId::FromHex(parentContext->GetSpanId());

auto spanContext = opentelemetry::trace::SpanContext::Create(
traceId,
spanId,
opentelemetry::trace::TraceFlags::kIsSampled,
false // remote
);

otelContext = opentelemetry::context::Context{}.SetValue(
opentelemetry::trace::kSpanKey,
opentelemetry::trace::Span{spanContext});
}

auto span = tracer_->StartSpan(name, attributes, otelContext);

auto context = parentContext ? parentContext->CreateChild() : TTraceContext::GenerateNew();

return std::make_unique<OpenTelemetrySpan>(std::move(span), std::move(context));
}

std::shared_ptr<TTraceContext> GetCurrentContext() const override {
auto currentSpan = opentelemetry::trace::GetCurrentSpan();
if (!currentSpan.IsValid()) {
return nullptr;
}
auto spanContext = currentSpan.GetContext();

return std::make_shared<TTraceContext>(
spanContext.trace_id().ToHex(),
spanContext.span_id().ToHex(),
spanContext.IsValid() ? spanContext.trace_id().ToHex() : "");
}

private:
std::shared_ptr<opentelemetry::trace::Tracer> tracer_;
};

} // namespace NTracing
} // namespace NYdb
77 changes: 77 additions & 0 deletions include/ydb-cpp-sdk/client/tracing/tracer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once

#include <string>
#include <memory>
#include <unordered_map>

namespace NYdb {
namespace NTracing {

using TAttributeMap = std::unordered_map<std::string, std::string>;

// Контекст с идентификаторами трассировки
class TTraceContext {
public:
TTraceContext(std::string traceId, std::string spanId, std::string parentSpanId = "")
: TraceId_(std::move(traceId))
, SpanId_(std::move(spanId))
, ParentSpanId_(std::move(parentSpanId))
{}

const std::string& GetTraceId() const { return TraceId_; }
const std::string& GetSpanId() const { return SpanId_; }
const std::string& GetParentSpanId() const { return ParentSpanId_; }

// Генерация нового контекста (создает новые уникальные traceId и spanId)
static std::shared_ptr<TTraceContext> GenerateNew();

// Создание дочернего контекста (новый spanId, тот же traceId)
std::shared_ptr<TTraceContext> CreateChild() const;

// Формирование W3C traceparent ("00-traceId-spanId-01")
std::string ToTraceParent() const;

private:
std::string TraceId_;
std::string SpanId_;
std::string ParentSpanId_;
};


class ISpan {
public:
virtual ~ISpan() = default;

virtual void AddAttribute(const std::string& key, const std::string& value) = 0;
virtual void AddEvent(const std::string& name, const TAttributeMap& attributes = {}) = 0;
virtual void SetStatus(bool isError, const std::string& description = "") = 0;
virtual void End() = 0;

virtual const TTraceContext& GetContext() const = 0;
};


class ITracer {
public:
virtual ~ITracer() = default;

// Создать новый спан с именем, атрибутами и опциональным родительским контекстом
virtual std::unique_ptr<ISpan> StartSpan(
const std::string& name,
const TAttributeMap& attributes = {},
std::shared_ptr<TTraceContext> parentContext = nullptr) = 0;

// Получить текущий контекст
virtual std::shared_ptr<TTraceContext> GetCurrentContext() const = 0;

// Получить W3C traceparent текущего контекста (если есть)
virtual std::string GetCurrentTraceParent() const {
if (auto ctx = GetCurrentContext()) {
return ctx->ToTraceParent();
}
return "";
}
};

} // namespace NTracing
} // namespace NYdb
3 changes: 3 additions & 0 deletions include/ydb-cpp-sdk/client/types/request_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <vector>
#include <utility>
#include <string>

namespace NYdb::inline V3 {

Expand All @@ -20,6 +21,7 @@ struct TRequestSettings {
FLUENT_SETTING(std::string, RequestType);
FLUENT_SETTING(THeader, Header);
FLUENT_SETTING(TDuration, ClientTimeout);
FLUENT_SETTING(std::string, TraceParent);

TRequestSettings() = default;

Expand All @@ -29,6 +31,7 @@ struct TRequestSettings {
, RequestType_(other.RequestType_)
, Header_(other.Header_)
, ClientTimeout_(other.ClientTimeout_)
, TraceParent_(other.TraceParent_)
{}
};

Expand Down
15 changes: 15 additions & 0 deletions src/client/impl/ydb_internal/grpc_connections/grpc_connections.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ class TGRpcConnectionsImpl

TCallMeta meta;
meta.Timeout = requestSettings.ClientTimeout;

if (!requestSettings.TraceParent.empty()) {
meta.Aux.emplace_back({"traceparent", requestSettings.TraceParent});
}

#ifndef YDB_GRPC_UNSECURE_AUTH
meta.CallCredentials = dbState->CallCredentials;
#else
Expand Down Expand Up @@ -415,6 +420,11 @@ class TGRpcConnectionsImpl

TCallMeta meta;
meta.Timeout = requestSettings.ClientTimeout;

if (!requestSettings.TraceParent.empty()) {
meta.Aux.emplace_back({"traceparent", requestSettings.TraceParent});
}

#ifndef YDB_GRPC_UNSECURE_AUTH
meta.CallCredentials = dbState->CallCredentials;
#else
Expand Down Expand Up @@ -509,6 +519,11 @@ class TGRpcConnectionsImpl
}

TCallMeta meta;

if (!requestSettings.TraceParent.empty()) {
meta.Aux.emplace_back({"traceparent", requestSettings.TraceParent});
}

#ifndef YDB_GRPC_UNSECURE_AUTH
meta.CallCredentials = dbState->CallCredentials;
#else
Expand Down
Loading