Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ add_subdirectory(include/ydb-cpp-sdk/client)
add_subdirectory(src)
add_subdirectory(util)

if (YDB_SDK_HAS_OPEN_TELEMETRY)
add_subdirectory(open_telemetry EXCLUDE_FROM_ALL)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не будет собираться если на него не сошлются

endif()

#_ydb_sdk_validate_public_headers()

if (YDB_SDK_EXAMPLES)
Expand Down
4 changes: 4 additions & 0 deletions cmake/external_libs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ find_package(base64 REQUIRED)
find_package(Brotli 1.1.0 REQUIRED)
find_package(jwt-cpp REQUIRED)
find_package(double-conversion REQUIRED)
find_package(opentelemetry-cpp QUIET)
if (opentelemetry-cpp_FOUND)
set(YDB_SDK_HAS_OPEN_TELEMETRY ON)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мне кажется это не совсем правильный подход. Мы не должны опираться на то, есть ли либа в системе или нет. Установка либ в C++ и так кошмар, а если мы еще завяжем подключение компоненты SDK на наличие либы которая фиг пойми как ищется, пользователь точно с ума сойдет.

Более user-frendly подход тут, чтобы источником правды были желания пользователя. Если ему нужен otel-metrics, значит собираем не смотря ни на что. Если нет зависимости - кидаем понятную ошибку. Сделать например флажок YDB_SDK_ENABLE_OTEL_METRICS. Можешь посмотреть как это у userver выглядит, у них каждый компонент можно включать и выключать: https://github.com/userver-framework/userver/blob/59e4c01531f478a3eac245379a3b1395475ec139/CMakeLists.txt#L145-L170

endif()
Comment on lines +16 to +19
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opentelemetry -- опциональная зависимость


# RapidJSON
if (YDB_SDK_USE_RAPID_JSON)
Expand Down
81 changes: 81 additions & 0 deletions include/ydb-cpp-sdk/client/metrics/metrics.h
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

может быть, на файлы разбить?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Как-то намешано все тут. И про метрики, и про спаны

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#pragma once

#include <ydb-cpp-sdk/client/extension_common/extension.h>

#include <map>
#include <memory>
#include <string>
#include <vector>

namespace NYdb::inline V3::NMetrics {

using TLabels = std::map<std::string, std::string>;

class ICounter {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Маловато мне кажется. Не помню точно, но вроде SDK сейчас использует более широкий список видов метрик. Я уже делал подход по поддержке otel для метрик: https://github.com/ydb-platform/ydb-cpp-sdk/pull/552/changes#diff-f1460d2c3384a36a259dbc2d72809cb435960a695af99471b058811239323a60
Но не довел до конца.

Можешь посмотреть, что там было. И посмотри, какие сейчас метрики SDK уже собирает. Нам все равно их переводить придется.

public:
virtual ~ICounter() = default;
virtual void Inc() = 0;
};

class IGauge {
public:
virtual ~IGauge() = default;
virtual void Add(double delta) = 0;
virtual void Set(double value) = 0;
};

class IHistogram {
public:
virtual ~IHistogram() = default;
virtual void Record(double value) = 0;
};

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

virtual std::shared_ptr<ICounter> Counter(const std::string& name, const TLabels& labels = {}) = 0;
virtual std::shared_ptr<IGauge> Gauge(const std::string& name, const TLabels& labels = {}) = 0;
virtual std::shared_ptr<IHistogram> Histogram(const std::string& name, const std::vector<double>& buckets, const TLabels& labels = {}) = 0;
};

enum class ESpanKind {
INTERNAL,
SERVER,
CLIENT,
PRODUCER,
CONSUMER
};

class ISpan {
public:
virtual ~ISpan() = default;
virtual void End() = 0;
virtual void SetAttribute(const std::string& key, const std::string& value) = 0;
virtual void SetAttribute(const std::string& key, int64_t value) = 0;
};

class ITracer {
public:
virtual ~ITracer() = default;
virtual std::shared_ptr<ISpan> StartSpan(const std::string& name, ESpanKind kind = ESpanKind::INTERNAL) = 0;
};

class ITraceProvider {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А зачем нам и ITracer и ITraceProvider? Нельзя сделать, чтобы ITraceProvider напрямую спаны создавал?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Про трейсы кстати тоже есть PR, от другого студента, который не доделал. Можно доже посмотреть: https://github.com/ydb-platform/ydb-cpp-sdk/pull/517/changes

Но за код не ручаюсь, там все может быть и неправильно спроектировано, так что смотреть с большим скепсисом.

public:
virtual ~ITraceProvider() = default;
virtual std::shared_ptr<ITracer> GetTracer(const std::string& name) = 0;
};

class IMetricsApi : public IExtensionApi {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IExtensionApi - большое зло. Не очень удачное решение, которое плотно так засело в SDK. Давайте закопаем его. Мне кажется, пользователю будет гораздо удобнее, если экспортер метрик и трейсов будут прям в TDriverConfig задаваться.

Типа

NYdb::TDriver driver(TDriverConfig("grpc://localhost:2136/local")
    .SetMetricExporter(CreateOtelMetricExporter(...))
    .SetTraceExporter(CreateOtelTraceExporter(...))
);
...

Лаконично и понятно пользователю.

public:
static IMetricsApi* Create(TDriver driver);
public:
virtual ~IMetricsApi() = default;
virtual void SetMetricRegistry(std::shared_ptr<IMetricRegistry> registry) = 0;
virtual void SetTraceProvider(std::shared_ptr<ITraceProvider> provider) = 0;
virtual std::shared_ptr<IMetricRegistry> GetMetricRegistry() const = 0;
virtual std::shared_ptr<ITraceProvider> GetTraceProvider() const = 0;
};

} // namespace NYdb::NMetrics
20 changes: 20 additions & 0 deletions open_telemetry/CMakeLists.txt
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А может разделим? Тащить и метрики и трейсы сразу как-то странно, все же разные сущности. Вдруг трейсы пользователю не нужны?

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
_ydb_sdk_add_library(open_telemetry)

target_sources(open_telemetry PRIVATE
src/otel.cpp
)

target_include_directories(open_telemetry PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

target_link_libraries(open_telemetry PUBLIC
client-metrics
opentelemetry-cpp::api
opentelemetry-cpp::metrics
opentelemetry-cpp::trace
)

_ydb_sdk_make_client_component(OpenTelemetry open_telemetry)
_ydb_sdk_install_headers(${CMAKE_INSTALL_INCLUDEDIR} DIRECTORY include/)
34 changes: 34 additions & 0 deletions open_telemetry/include/ydb-cpp-sdk/open_telemetry/extension.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include <ydb-cpp-sdk/open_telemetry/otel.h>
#include <ydb-cpp-sdk/client/extension_common/extension.h>

namespace NYdb::inline V3::NMetrics {

class TOtelExtension : public IExtension {
public:
using IApi = IMetricsApi;

struct TParams {
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::MeterProvider> MeterProvider;
opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider> TracerProvider;
};

TOtelExtension(const TParams& params, IApi* api) {
if (params.MeterProvider) {
api->SetMetricRegistry(std::make_shared<TOtelMetricRegistry>(params.MeterProvider));
}
if (params.TracerProvider) {
api->SetTraceProvider(std::make_shared<TOtelTraceProvider>(params.TracerProvider));
}
}
};

inline void AddOpenTelemetry(TDriver& driver
, opentelemetry::nostd::shared_ptr<opentelemetry::metrics::MeterProvider> meterProvider
, opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider> tracerProvider
) {
driver.AddExtension<TOtelExtension>({meterProvider, tracerProvider});
}

} // namespace NYdb::NMetrics
33 changes: 33 additions & 0 deletions open_telemetry/include/ydb-cpp-sdk/open_telemetry/otel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

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

#include <opentelemetry/metrics/meter_provider.h>
#include <opentelemetry/trace/tracer_provider.h>

namespace NYdb::inline V3::NMetrics {

class TOtelMetricRegistry : public IMetricRegistry {
public:
TOtelMetricRegistry(opentelemetry::nostd::shared_ptr<opentelemetry::metrics::MeterProvider> meterProvider);

std::shared_ptr<ICounter> Counter(const std::string& name, const TLabels& labels = {}) override;
std::shared_ptr<IGauge> Gauge(const std::string& name, const TLabels& labels = {}) override;
std::shared_ptr<IHistogram> Histogram(const std::string& name, const std::vector<double>& buckets, const TLabels& labels = {}) override;

private:
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::MeterProvider> MeterProvider_;
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Meter> Meter_;
};

class TOtelTraceProvider : public ITraceProvider {
public:
TOtelTraceProvider(opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider> tracerProvider);

std::shared_ptr<ITracer> GetTracer(const std::string& name) override;

private:
opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider> TracerProvider_;
};

} // namespace NYdb::NMetrics
153 changes: 153 additions & 0 deletions open_telemetry/src/otel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#include <ydb-cpp-sdk/open_telemetry/otel.h>

#include <opentelemetry/metrics/meter.h>
#include <opentelemetry/metrics/sync_instruments.h>
#include <opentelemetry/common/key_value_iterable_view.h>
#include <opentelemetry/trace/tracer.h>
#include <opentelemetry/context/runtime_context.h>

namespace NYdb::inline V3::NMetrics {

namespace {

using namespace opentelemetry;

common::KeyValueIterableView<TLabels> MakeAttributes(const TLabels& labels) {
return common::KeyValueIterableView<TLabels>(labels);
}

class TOtelCounter : public ICounter {
public:
TOtelCounter(nostd::shared_ptr<metrics::Counter<uint64_t>> counter, const TLabels& labels)
: Counter_(std::move(counter))
, Labels_(labels)
{}

void Inc() override {
Counter_->Add(1, MakeAttributes(Labels_), context::RuntimeContext::GetCurrent());
}

private:
nostd::shared_ptr<metrics::Counter<uint64_t>> Counter_;
TLabels Labels_;
};

class TOtelUpDownCounterGauge : public IGauge {
public:
TOtelUpDownCounterGauge(nostd::shared_ptr<metrics::UpDownCounter<double>> counter, const TLabels& labels)
: Counter_(std::move(counter))
, Labels_(labels)
{}

void Add(double delta) override {
Counter_->Add(delta, MakeAttributes(Labels_), context::RuntimeContext::GetCurrent());
Value_ += delta;
}

void Set(double value) override {
Counter_->Add(value - Value_, MakeAttributes(Labels_), context::RuntimeContext::GetCurrent());
Value_ = value;
}

private:
nostd::shared_ptr<metrics::UpDownCounter<double>> Counter_;
TLabels Labels_;
double Value_ = 0;
};

class TOtelHistogram : public IHistogram {
public:
TOtelHistogram(nostd::shared_ptr<metrics::Histogram<double>> histogram, const TLabels& labels)
: Histogram_(std::move(histogram))
, Labels_(labels)
{}

void Record(double value) override {
Histogram_->Record(value, MakeAttributes(Labels_), context::RuntimeContext::GetCurrent());
}

private:
nostd::shared_ptr<metrics::Histogram<double>> Histogram_;
TLabels Labels_;
};

trace::SpanKind MapSpanKind(ESpanKind kind) {
switch (kind) {
case ESpanKind::INTERNAL: return trace::SpanKind::kInternal;
case ESpanKind::SERVER: return trace::SpanKind::kServer;
case ESpanKind::CLIENT: return trace::SpanKind::kClient;
case ESpanKind::PRODUCER: return trace::SpanKind::kProducer;
case ESpanKind::CONSUMER: return trace::SpanKind::kConsumer;
}
return trace::SpanKind::kInternal;
}

class TOtelSpan : public ISpan {
public:
TOtelSpan(nostd::shared_ptr<trace::Span> span)
: Span_(std::move(span))
{}

void End() override {
Span_->End();
}

void SetAttribute(const std::string& key, const std::string& value) override {
Span_->SetAttribute(key, value);
}

void SetAttribute(const std::string& key, int64_t value) override {
Span_->SetAttribute(key, value);
}

private:
nostd::shared_ptr<trace::Span> Span_;
};

class TOtelTracer : public ITracer {
public:
TOtelTracer(nostd::shared_ptr<trace::Tracer> tracer)
: Tracer_(std::move(tracer))
{}

std::shared_ptr<ISpan> StartSpan(const std::string& name, ESpanKind kind) override {
trace::StartSpanOptions options;
options.kind = MapSpanKind(kind);
return std::make_shared<TOtelSpan>(Tracer_->StartSpan(name, options));
}

private:
nostd::shared_ptr<trace::Tracer> Tracer_;
};

} // namespace

TOtelMetricRegistry::TOtelMetricRegistry(nostd::shared_ptr<metrics::MeterProvider> meterProvider)
: MeterProvider_(std::move(meterProvider))
, Meter_(MeterProvider_->GetMeter("ydb-cpp-sdk", "1.0.0"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Почему 1.0.0? У ydb-cpp-sdk есть версия, в SLO тестах даже проставляем на meter: https://github.com/ydb-platform/ydb-cpp-sdk/blob/main/tests/slo_workloads/utils/metrics.cpp#L53

{}

std::shared_ptr<ICounter> TOtelMetricRegistry::Counter(const std::string& name, const TLabels& labels) {
auto counter = Meter_->CreateUInt64Counter(name);
return std::make_shared<TOtelCounter>(std::move(counter), labels);
}

std::shared_ptr<IGauge> TOtelMetricRegistry::Gauge(const std::string& name, const TLabels& labels) {
auto counter = Meter_->CreateDoubleUpDownCounter(name);
return std::make_shared<TOtelUpDownCounterGauge>(std::move(counter), labels);
}

std::shared_ptr<IHistogram> TOtelMetricRegistry::Histogram(const std::string& name, const std::vector<double>& buckets, const TLabels& labels) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buckets потеряли

auto histogram = Meter_->CreateDoubleHistogram(name);
return std::make_shared<TOtelHistogram>(std::move(histogram), labels);
}

TOtelTraceProvider::TOtelTraceProvider(nostd::shared_ptr<trace::TracerProvider> tracerProvider)
: TracerProvider_(std::move(tracerProvider))
{}

std::shared_ptr<ITracer> TOtelTraceProvider::GetTracer(const std::string& name) {
return std::make_shared<TOtelTracer>(TracerProvider_->GetTracer(name));
}

} // namespace NYdb::NMetrics
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
add_subdirectory(api)
add_subdirectory(client)
add_subdirectory(library)
add_subdirectory(library)
1 change: 1 addition & 0 deletions src/client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ add_subdirectory(iam)
add_subdirectory(iam_private)
add_subdirectory(impl)
add_subdirectory(import)
add_subdirectory(metrics)
add_subdirectory(monitoring)
add_subdirectory(operation)
add_subdirectory(params)
Expand Down
12 changes: 12 additions & 0 deletions src/client/impl/internal/grpc_connections/grpc_connections.h
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,18 @@ class TGRpcConnectionsImpl
::NMonitoring::TMetricRegistry* GetMetricRegistry() override;
void RegisterExtension(IExtension* extension);
void RegisterExtensionApi(IExtensionApi* api);

template<typename T>
T* GetExtensionApi() {
std::lock_guard lock(ExtensionsLock_);
for (const auto& api : ExtensionApis_) {
if (auto ptr = dynamic_cast<T*>(api.get())) {
return ptr;
}
}
return nullptr;
}

void SetDiscoveryMutator(IDiscoveryMutatorApi::TMutatorCb&& cb);
const TLog& GetLog() const override;

Expand Down
16 changes: 16 additions & 0 deletions src/client/metrics/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
_ydb_sdk_add_library(client-metrics)

target_sources(client-metrics PRIVATE
metrics.cpp
)

target_include_directories(client-metrics PUBLIC
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А это не нужно, в _ydb_sdk_add_library уже должен проставляться target_include_directories

$<BUILD_INTERFACE:${YDB_SDK_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

target_link_libraries(client-metrics PUBLIC
client-extension_common
)

_ydb_sdk_make_client_component(Metrics client-metrics)
Loading
Loading