From dbeb92b77bd0bc88ae71646dd468ff3981845c02 Mon Sep 17 00:00:00 2001 From: Aleksey Myasnikov Date: Fri, 21 Mar 2025 16:02:22 +0300 Subject: [PATCH 1/4] added abstract IMetric class and implementations for Solomon, Prometheus, OpenTelemetry --- CMakeLists.txt | 44 +++++++++ docs/monitoring.md | 94 +++++++++++++++++++ examples/monitoring/opentelemetry_example.cpp | 28 ++++++ examples/monitoring/prometheus_example.cpp | 28 ++++++ examples/monitoring_example.cpp | 27 ++++++ .../client/monitoring/impl/opentelemetry.h | 47 ++++++++++ .../client/monitoring/impl/prometheus.h | 47 ++++++++++ .../ydb-cpp-sdk/client/monitoring/metrics.h | 79 ++++++++++++++++ src/client/monitoring/prometheus.cpp | 81 ++++++++++++++++ 9 files changed, 475 insertions(+) create mode 100644 docs/monitoring.md create mode 100644 examples/monitoring/opentelemetry_example.cpp create mode 100644 examples/monitoring/prometheus_example.cpp create mode 100644 examples/monitoring_example.cpp create mode 100644 include/ydb-cpp-sdk/client/monitoring/impl/opentelemetry.h create mode 100644 include/ydb-cpp-sdk/client/monitoring/impl/prometheus.h create mode 100644 include/ydb-cpp-sdk/client/monitoring/metrics.h create mode 100644 src/client/monitoring/prometheus.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d0b2557078..362430d342 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,15 @@ option(YDB_SDK_EXAMPLES "Build YDB C++ SDK examples" On) set(YDB_SDK_GOOGLE_COMMON_PROTOS_TARGET "" CACHE STRING "Name of cmake target preparing google common proto library") option(YDB_SDK_USE_RAPID_JSON "Search for rapid json library in system" ON) +# Опции для систем мониторинга +option(YDB_SDK_MONITORING_PROMETHEUS "Enable Prometheus monitoring" OFF) +option(YDB_SDK_MONITORING_OPENTELEMETRY "Enable OpenTelemetry monitoring" OFF) +option(YDB_SDK_MONITORING_DATADOG "Enable Datadog monitoring" OFF) +option(YDB_SDK_MONITORING_NEWRELIC "Enable New Relic monitoring" OFF) +option(YDB_SDK_MONITORING_APPDYNAMICS "Enable AppDynamics monitoring" OFF) +option(YDB_SDK_MONITORING_VICTORIAMETRICS "Enable Victoria Metrics monitoring" OFF) +option(YDB_SDK_MONITORING_SOLOMON "Enable Yandex Solomon monitoring" ON) + set(BUILD_SHARED_LIBS Off) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED On) @@ -97,3 +106,38 @@ if (YDB_SDK_INSTALL) DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ydb-cpp-sdk/Modules ) endif() + +# Поиск зависимостей для систем мониторинга +if(YDB_SDK_MONITORING_PROMETHEUS) + find_package(Prometheus REQUIRED) +endif() + +if(YDB_SDK_MONITORING_OPENTELEMETRY) + find_package(OpenTelemetry REQUIRED) +endif() + +if(YDB_SDK_MONITORING_DATADOG) + find_package(Datadog REQUIRED) +endif() + +if(YDB_SDK_MONITORING_NEWRELIC) + find_package(NewRelic REQUIRED) +endif() + +if(YDB_SDK_MONITORING_APPDYNAMICS) + find_package(AppDynamics REQUIRED) +endif() + +if(YDB_SDK_MONITORING_VICTORIAMETRICS) + find_package(VictoriaMetrics REQUIRED) +endif() + +# Добавляем зависимости в target_link_libraries +target_link_libraries(ydb-cpp-sdk + $<$:prometheus::prometheus-cpp> + $<$:OpenTelemetry::OpenTelemetry> + $<$:Datadog::Datadog> + $<$:NewRelic::NewRelic> + $<$:AppDynamics::AppDynamics> + $<$:VictoriaMetrics::VictoriaMetrics> +) diff --git a/docs/monitoring.md b/docs/monitoring.md new file mode 100644 index 0000000000..66d1608697 --- /dev/null +++ b/docs/monitoring.md @@ -0,0 +1,94 @@ +# Мониторинг в YDB C++ SDK + +YDB C++ SDK поддерживает различные системы мониторинга для сбора метрик. Вы можете выбрать одну или несколько систем мониторинга при сборке проекта. + +## Поддерживаемые системы мониторинга + +- Prometheus +- OpenTelemetry +- Datadog +- New Relic +- AppDynamics +- Victoria Metrics +- Yandex Solomon (по умолчанию) + +## Настройка сборки + +При сборке проекта вы можете включить поддержку нужных систем мониторинга с помощью CMake опций: + +```cmake +cmake -DYDB_SDK_MONITORING_PROMETHEUS=ON \ + -DYDB_SDK_MONITORING_OPENTELEMETRY=ON \ + -DYDB_SDK_MONITORING_DATADOG=ON \ + -DYDB_SDK_MONITORING_NEWRELIC=ON \ + -DYDB_SDK_MONITORING_APPDYNAMICS=ON \ + -DYDB_SDK_MONITORING_VICTORIAMETRICS=ON \ + -DYDB_SDK_MONITORING_SOLOMON=ON \ + .. +``` + +## Использование + +### Prometheus + +```cpp +#include +#include + +// Создаем систему мониторинга +auto monitoringSystem = TMonitoringSystemFactory::CreatePrometheus("localhost:9090"); +TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + +// Создаем метрику +std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} +}; +TPrometheusMetric metric("ydb_query_count", "1", labels); + +// Записываем метрику +TMetricsContext::Instance().RecordMetric(metric); +``` + +### OpenTelemetry + +```cpp +#include +#include + +// Создаем систему мониторинга +auto monitoringSystem = TMonitoringSystemFactory::CreateOpenTelemetry("localhost:4317"); +TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + +// Создаем метрику +std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} +}; +TOpenTelemetryMetric metric("ydb_query_count", "1", labels); + +// Записываем метрику +TMetricsContext::Instance().RecordMetric(metric); +``` + +## Доступные метрики + +YDB C++ SDK собирает следующие метрики: + +- `ydb_query_count` - количество запросов +- `ydb_query_latency` - задержка запросов +- `ydb_error_count` - количество ошибок +- `ydb_connection_count` - количество активных соединений +- `ydb_session_count` - количество активных сессий + +## Примеры + +Полные примеры использования различных систем мониторинга можно найти в директории `examples/monitoring/`: + +- `prometheus_example.cpp` - пример использования Prometheus +- `opentelemetry_example.cpp` - пример использования OpenTelemetry +- `datadog_example.cpp` - пример использования Datadog +- `newrelic_example.cpp` - пример использования New Relic +- `appdynamics_example.cpp` - пример использования AppDynamics +- `victoriametrics_example.cpp` - пример использования Victoria Metrics +- `solomon_example.cpp` - пример использования Yandex Solomon \ No newline at end of file diff --git a/examples/monitoring/opentelemetry_example.cpp b/examples/monitoring/opentelemetry_example.cpp new file mode 100644 index 0000000000..5b2c2c0424 --- /dev/null +++ b/examples/monitoring/opentelemetry_example.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +using namespace NYdb; +using namespace NYdb::NMonitoring; + +int main() { + // Создаем систему мониторинга OpenTelemetry + auto monitoringSystem = TMonitoringSystemFactory::CreateOpenTelemetry("localhost:4317"); + TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + + // Создаем метрику + std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} + }; + TOpenTelemetryMetric metric("ydb_query_count", "1", labels); + + // Записываем метрику + TMetricsContext::Instance().RecordMetric(metric); + + // Принудительно отправляем метрики + TMetricsContext::Instance().Flush(); + + return 0; +} \ No newline at end of file diff --git a/examples/monitoring/prometheus_example.cpp b/examples/monitoring/prometheus_example.cpp new file mode 100644 index 0000000000..5154b121f8 --- /dev/null +++ b/examples/monitoring/prometheus_example.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +using namespace NYdb; +using namespace NYdb::NMonitoring; + +int main() { + // Создаем систему мониторинга Prometheus + auto monitoringSystem = TMonitoringSystemFactory::CreatePrometheus("localhost:9090"); + TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + + // Создаем метрику + std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} + }; + TPrometheusMetric metric("ydb_query_count", "1", labels); + + // Записываем метрику + TMetricsContext::Instance().RecordMetric(metric); + + // Принудительно отправляем метрики + TMetricsContext::Instance().Flush(); + + return 0; +} \ No newline at end of file diff --git a/examples/monitoring_example.cpp b/examples/monitoring_example.cpp new file mode 100644 index 0000000000..24ef30089b --- /dev/null +++ b/examples/monitoring_example.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +using namespace NYdb; +using namespace NYdb::NMonitoring; + +int main() { + // Создаем систему мониторинга Prometheus + auto monitoringSystem = TMonitoringSystemFactory::CreatePrometheus("localhost:9090"); + TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + + // Создаем метрику + std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} + }; + TPrometheusMetric metric("ydb_query_count", "1", labels); + + // Записываем метрику + TMetricsContext::Instance().RecordMetric(metric); + + // Принудительно отправляем метрики + TMetricsContext::Instance().Flush(); + + return 0; +} \ No newline at end of file diff --git a/include/ydb-cpp-sdk/client/monitoring/impl/opentelemetry.h b/include/ydb-cpp-sdk/client/monitoring/impl/opentelemetry.h new file mode 100644 index 0000000000..3d95d03fc3 --- /dev/null +++ b/include/ydb-cpp-sdk/client/monitoring/impl/opentelemetry.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +namespace NYdb::NMonitoring { + +class TOpenTelemetryMetric : public IMetric { +public: + TOpenTelemetryMetric(const std::string& name, const std::string& value, + const std::unordered_map& labels) + : Name_(name) + , Value_(value) + , Labels_(labels) + {} + + std::string GetName() const override { return Name_; } + std::string GetValue() const override { return Value_; } + std::unordered_map GetLabels() const override { return Labels_; } + +private: + std::string Name_; + std::string Value_; + std::unordered_map Labels_; +}; + +class TOpenTelemetryMonitoringSystem : public IMonitoringSystem { +public: + explicit TOpenTelemetryMonitoringSystem(const std::string& endpoint) + : Endpoint_(endpoint) + { + Initialize(); + } + + void RecordMetric(const IMetric& metric) override; + void Flush() override; + +private: + void Initialize(); + std::string Endpoint_; + std::shared_ptr MeterProvider_; + std::shared_ptr MetricReader_; +}; + +} // namespace NYdb::NMonitoring \ No newline at end of file diff --git a/include/ydb-cpp-sdk/client/monitoring/impl/prometheus.h b/include/ydb-cpp-sdk/client/monitoring/impl/prometheus.h new file mode 100644 index 0000000000..706c8427f7 --- /dev/null +++ b/include/ydb-cpp-sdk/client/monitoring/impl/prometheus.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace NYdb::NMonitoring { + +class TPrometheusMetric : public IMetric { +public: + TPrometheusMetric(const std::string& name, const std::string& value, + const std::unordered_map& labels) + : Name_(name) + , Value_(value) + , Labels_(labels) + {} + + std::string GetName() const override { return Name_; } + std::string GetValue() const override { return Value_; } + std::unordered_map GetLabels() const override { return Labels_; } + +private: + std::string Name_; + std::string Value_; + std::unordered_map Labels_; +}; + +class TPrometheusMonitoringSystem : public IMonitoringSystem { +public: + explicit TPrometheusMonitoringSystem(const std::string& endpoint) + : Exposer_(endpoint) + , Registry_(std::make_shared()) + { + Exposer_.RegisterCollectable(Registry_); + } + + void RecordMetric(const IMetric& metric) override; + void Flush() override; + +private: + prometheus::Exposer Exposer_; + std::shared_ptr Registry_; +}; + +} // namespace NYdb::NMonitoring \ No newline at end of file diff --git a/include/ydb-cpp-sdk/client/monitoring/metrics.h b/include/ydb-cpp-sdk/client/monitoring/metrics.h new file mode 100644 index 0000000000..82d2e44a9f --- /dev/null +++ b/include/ydb-cpp-sdk/client/monitoring/metrics.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include + +namespace NYdb::NMonitoring { + +// Базовый класс для метрики +class IMetric { +public: + virtual ~IMetric() = default; + virtual std::string GetName() const = 0; + virtual std::string GetValue() const = 0; + virtual std::unordered_map GetLabels() const = 0; + virtual std::string GetType() const = 0; // counter, gauge, histogram +}; + +// Интерфейс для системы мониторинга +class IMonitoringSystem { +public: + virtual ~IMonitoringSystem() = default; + virtual void RecordMetric(const IMetric& metric) = 0; + virtual void Flush() = 0; + virtual std::string GetSystemName() const = 0; +}; + +// Фабрика для создания систем мониторинга +class TMonitoringSystemFactory { +public: + static std::unique_ptr CreatePrometheus(const std::string& endpoint); + static std::unique_ptr CreateOpenTelemetry(const std::string& endpoint); + static std::unique_ptr CreateDatadog(const std::string& apiKey); + static std::unique_ptr CreateNewRelic(const std::string& licenseKey); + static std::unique_ptr CreateAppDynamics(const std::string& controllerUrl); + static std::unique_ptr CreateVictoriaMetrics(const std::string& endpoint); + static std::unique_ptr CreateSolomon(const std::string& endpoint); +}; + +// Контекст для хранения метрик +class TMetricsContext { +public: + static TMetricsContext& Instance(); + + void SetMonitoringSystem(std::unique_ptr system); + void RecordMetric(const IMetric& metric); + void Flush(); + +private: + TMetricsContext() = default; + std::unique_ptr MonitoringSystem_; +}; + +// Вспомогательные функции для создания метрик +inline std::unique_ptr CreateCounterMetric( + const std::string& name, + const std::string& value, + const std::unordered_map& labels = {}) +{ + return std::make_unique(name, value, labels); +} + +inline std::unique_ptr CreateGaugeMetric( + const std::string& name, + const std::string& value, + const std::unordered_map& labels = {}) +{ + return std::make_unique(name, value, labels); +} + +inline std::unique_ptr CreateHistogramMetric( + const std::string& name, + const std::string& value, + const std::unordered_map& labels = {}) +{ + return std::make_unique(name, value, labels); +} + +} // namespace NYdb::NMonitoring \ No newline at end of file diff --git a/src/client/monitoring/prometheus.cpp b/src/client/monitoring/prometheus.cpp new file mode 100644 index 0000000000..363979feae --- /dev/null +++ b/src/client/monitoring/prometheus.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include + +namespace NYdb::NMonitoring { + +class TPrometheusMetric : public IMetric { +public: + TPrometheusMetric(const std::string& name, const std::string& value, + const std::unordered_map& labels) + : Name_(name) + , Value_(value) + , Labels_(labels) + {} + + std::string GetName() const override { return Name_; } + std::string GetValue() const override { return Value_; } + std::unordered_map GetLabels() const override { return Labels_; } + +private: + std::string Name_; + std::string Value_; + std::unordered_map Labels_; +}; + +class TPrometheusMonitoringSystem : public IMonitoringSystem { +public: + explicit TPrometheusMonitoringSystem(const std::string& endpoint) + : Exposer_(endpoint) + , Registry_(std::make_shared()) + { + Exposer_.RegisterCollectable(Registry_); + } + + void RecordMetric(const IMetric& metric) override { + auto& counter = prometheus::BuildCounter() + .Name(metric.GetName()) + .Help("YDB SDK metric") + .Register(*Registry_) + .Add(metric.GetLabels()); + + counter.Increment(std::stod(metric.GetValue())); + } + + void Flush() override { + // Prometheus автоматически собирает метрики + } + +private: + prometheus::Exposer Exposer_; + std::shared_ptr Registry_; +}; + +std::unique_ptr TMonitoringSystemFactory::CreatePrometheus(const std::string& endpoint) { + return std::make_unique(endpoint); +} + +TMetricsContext& TMetricsContext::Instance() { + static TMetricsContext instance; + return instance; +} + +void TMetricsContext::SetMonitoringSystem(std::unique_ptr system) { + MonitoringSystem_ = std::move(system); +} + +void TMetricsContext::RecordMetric(const IMetric& metric) { + if (MonitoringSystem_) { + MonitoringSystem_->RecordMetric(metric); + } +} + +void TMetricsContext::Flush() { + if (MonitoringSystem_) { + MonitoringSystem_->Flush(); + } +} + +} // namespace NYdb::NMonitoring \ No newline at end of file From 8dc897cece8388dcae8de16482b67eb7c209e453 Mon Sep 17 00:00:00 2001 From: Aleksey Myasnikov Date: Fri, 21 Mar 2025 16:26:22 +0300 Subject: [PATCH 2/4] appdynamics, datadog, newrelic, victoriametrics --- docs/monitoring.md | 84 +++++++++++++++++++ examples/monitoring/appdynamics_example.cpp | 28 +++++++ examples/monitoring/datadog_example.cpp | 28 +++++++ examples/monitoring/newrelic_example.cpp | 28 +++++++ .../monitoring/victoriametrics_example.cpp | 28 +++++++ .../client/monitoring/impl/appdynamics.h | 46 ++++++++++ .../client/monitoring/impl/datadog.h | 48 +++++++++++ .../client/monitoring/impl/newrelic.h | 46 ++++++++++ .../client/monitoring/impl/victoriametrics.h | 46 ++++++++++ 9 files changed, 382 insertions(+) create mode 100644 examples/monitoring/appdynamics_example.cpp create mode 100644 examples/monitoring/datadog_example.cpp create mode 100644 examples/monitoring/newrelic_example.cpp create mode 100644 examples/monitoring/victoriametrics_example.cpp create mode 100644 include/ydb-cpp-sdk/client/monitoring/impl/appdynamics.h create mode 100644 include/ydb-cpp-sdk/client/monitoring/impl/datadog.h create mode 100644 include/ydb-cpp-sdk/client/monitoring/impl/newrelic.h create mode 100644 include/ydb-cpp-sdk/client/monitoring/impl/victoriametrics.h diff --git a/docs/monitoring.md b/docs/monitoring.md index 66d1608697..0da3b56d5d 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -71,6 +71,90 @@ TOpenTelemetryMetric metric("ydb_query_count", "1", labels); TMetricsContext::Instance().RecordMetric(metric); ``` +### Datadog + +```cpp +#include +#include + +// Создаем систему мониторинга +auto monitoringSystem = TMonitoringSystemFactory::CreateDatadog("your-api-key"); +TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + +// Создаем метрику +std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} +}; +TDatadogMetric metric("ydb.query.count", "1", labels); + +// Записываем метрику +TMetricsContext::Instance().RecordMetric(metric); +``` + +### New Relic + +```cpp +#include +#include + +// Создаем систему мониторинга +auto monitoringSystem = TMonitoringSystemFactory::CreateNewRelic("your-license-key"); +TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + +// Создаем метрику +std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} +}; +TNewRelicMetric metric("Custom/ydb/query_count", "1", labels); + +// Записываем метрику +TMetricsContext::Instance().RecordMetric(metric); +``` + +### AppDynamics + +```cpp +#include +#include + +// Создаем систему мониторинга +auto monitoringSystem = TMonitoringSystemFactory::CreateAppDynamics("http://your-controller:8090"); +TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + +// Создаем метрику +std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} +}; +TAppDynamicsMetric metric("Custom Metrics|YDB|Query Count", "1", labels); + +// Записываем метрику +TMetricsContext::Instance().RecordMetric(metric); +``` + +### Victoria Metrics + +```cpp +#include +#include + +// Создаем систему мониторинга +auto monitoringSystem = TMonitoringSystemFactory::CreateVictoriaMetrics("http://localhost:8428"); +TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + +// Создаем метрику +std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} +}; +TVictoriaMetricsMetric metric("ydb_query_count", "1", labels); + +// Записываем метрику +TMetricsContext::Instance().RecordMetric(metric); +``` + ## Доступные метрики YDB C++ SDK собирает следующие метрики: diff --git a/examples/monitoring/appdynamics_example.cpp b/examples/monitoring/appdynamics_example.cpp new file mode 100644 index 0000000000..6e3d236239 --- /dev/null +++ b/examples/monitoring/appdynamics_example.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +using namespace NYdb; +using namespace NYdb::NMonitoring; + +int main() { + // Создаем систему мониторинга AppDynamics + auto monitoringSystem = TMonitoringSystemFactory::CreateAppDynamics("http://your-controller:8090"); + TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + + // Создаем метрику + std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} + }; + TAppDynamicsMetric metric("Custom Metrics|YDB|Query Count", "1", labels); + + // Записываем метрику + TMetricsContext::Instance().RecordMetric(metric); + + // Принудительно отправляем метрики + TMetricsContext::Instance().Flush(); + + return 0; +} \ No newline at end of file diff --git a/examples/monitoring/datadog_example.cpp b/examples/monitoring/datadog_example.cpp new file mode 100644 index 0000000000..c9853d11d6 --- /dev/null +++ b/examples/monitoring/datadog_example.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +using namespace NYdb; +using namespace NYdb::NMonitoring; + +int main() { + // Создаем систему мониторинга Datadog + auto monitoringSystem = TMonitoringSystemFactory::CreateDatadog("your-api-key"); + TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + + // Создаем метрику + std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} + }; + TDatadogMetric metric("ydb.query.count", "1", labels); + + // Записываем метрику + TMetricsContext::Instance().RecordMetric(metric); + + // Принудительно отправляем метрики + TMetricsContext::Instance().Flush(); + + return 0; +} \ No newline at end of file diff --git a/examples/monitoring/newrelic_example.cpp b/examples/monitoring/newrelic_example.cpp new file mode 100644 index 0000000000..9fe43fce15 --- /dev/null +++ b/examples/monitoring/newrelic_example.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +using namespace NYdb; +using namespace NYdb::NMonitoring; + +int main() { + // Создаем систему мониторинга New Relic + auto monitoringSystem = TMonitoringSystemFactory::CreateNewRelic("your-license-key"); + TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + + // Создаем метрику + std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} + }; + TNewRelicMetric metric("Custom/ydb/query_count", "1", labels); + + // Записываем метрику + TMetricsContext::Instance().RecordMetric(metric); + + // Принудительно отправляем метрики + TMetricsContext::Instance().Flush(); + + return 0; +} \ No newline at end of file diff --git a/examples/monitoring/victoriametrics_example.cpp b/examples/monitoring/victoriametrics_example.cpp new file mode 100644 index 0000000000..537232747c --- /dev/null +++ b/examples/monitoring/victoriametrics_example.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +using namespace NYdb; +using namespace NYdb::NMonitoring; + +int main() { + // Создаем систему мониторинга Victoria Metrics + auto monitoringSystem = TMonitoringSystemFactory::CreateVictoriaMetrics("http://localhost:8428"); + TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + + // Создаем метрику + std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"} + }; + TVictoriaMetricsMetric metric("ydb_query_count", "1", labels); + + // Записываем метрику + TMetricsContext::Instance().RecordMetric(metric); + + // Принудительно отправляем метрики + TMetricsContext::Instance().Flush(); + + return 0; +} \ No newline at end of file diff --git a/include/ydb-cpp-sdk/client/monitoring/impl/appdynamics.h b/include/ydb-cpp-sdk/client/monitoring/impl/appdynamics.h new file mode 100644 index 0000000000..9fed1751ea --- /dev/null +++ b/include/ydb-cpp-sdk/client/monitoring/impl/appdynamics.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +namespace NYdb::NMonitoring { + +class TAppDynamicsMetric : public IMetric { +public: + TAppDynamicsMetric(const std::string& name, const std::string& value, + const std::unordered_map& labels) + : Name_(name) + , Value_(value) + , Labels_(labels) + {} + + std::string GetName() const override { return Name_; } + std::string GetValue() const override { return Value_; } + std::unordered_map GetLabels() const override { return Labels_; } + std::string GetType() const override { return "counter"; } + +private: + std::string Name_; + std::string Value_; + std::unordered_map Labels_; +}; + +class TAppDynamicsMonitoringSystem : public IMonitoringSystem { +public: + explicit TAppDynamicsMonitoringSystem(const std::string& controllerUrl) + : ControllerUrl_(controllerUrl) + { + Initialize(); + } + + void RecordMetric(const IMetric& metric) override; + void Flush() override; + std::string GetSystemName() const override { return "appdynamics"; } + +private: + void Initialize(); + std::string ControllerUrl_; + AppDynamics* Agent_; +}; + +} // namespace NYdb::NMonitoring \ No newline at end of file diff --git a/include/ydb-cpp-sdk/client/monitoring/impl/datadog.h b/include/ydb-cpp-sdk/client/monitoring/impl/datadog.h new file mode 100644 index 0000000000..6f6d867018 --- /dev/null +++ b/include/ydb-cpp-sdk/client/monitoring/impl/datadog.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include + +namespace NYdb::NMonitoring { + +class TDatadogMetric : public IMetric { +public: + TDatadogMetric(const std::string& name, const std::string& value, + const std::unordered_map& labels) + : Name_(name) + , Value_(value) + , Labels_(labels) + {} + + std::string GetName() const override { return Name_; } + std::string GetValue() const override { return Value_; } + std::unordered_map GetLabels() const override { return Labels_; } + std::string GetType() const override { return "counter"; } + +private: + std::string Name_; + std::string Value_; + std::unordered_map Labels_; +}; + +class TDatadogMonitoringSystem : public IMonitoringSystem { +public: + explicit TDatadogMonitoringSystem(const std::string& apiKey) + : ApiKey_(apiKey) + { + Initialize(); + } + + void RecordMetric(const IMetric& metric) override; + void Flush() override; + std::string GetSystemName() const override { return "datadog"; } + +private: + void Initialize(); + std::string ApiKey_; + std::shared_ptr Tracer_; + std::shared_ptr MetricsClient_; +}; + +} // namespace NYdb::NMonitoring \ No newline at end of file diff --git a/include/ydb-cpp-sdk/client/monitoring/impl/newrelic.h b/include/ydb-cpp-sdk/client/monitoring/impl/newrelic.h new file mode 100644 index 0000000000..0d5939d353 --- /dev/null +++ b/include/ydb-cpp-sdk/client/monitoring/impl/newrelic.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +namespace NYdb::NMonitoring { + +class TNewRelicMetric : public IMetric { +public: + TNewRelicMetric(const std::string& name, const std::string& value, + const std::unordered_map& labels) + : Name_(name) + , Value_(value) + , Labels_(labels) + {} + + std::string GetName() const override { return Name_; } + std::string GetValue() const override { return Value_; } + std::unordered_map GetLabels() const override { return Labels_; } + std::string GetType() const override { return "counter"; } + +private: + std::string Name_; + std::string Value_; + std::unordered_map Labels_; +}; + +class TNewRelicMonitoringSystem : public IMonitoringSystem { +public: + explicit TNewRelicMonitoringSystem(const std::string& licenseKey) + : LicenseKey_(licenseKey) + { + Initialize(); + } + + void RecordMetric(const IMetric& metric) override; + void Flush() override; + std::string GetSystemName() const override { return "newrelic"; } + +private: + void Initialize(); + std::string LicenseKey_; + NewRelic* App_; +}; + +} // namespace NYdb::NMonitoring \ No newline at end of file diff --git a/include/ydb-cpp-sdk/client/monitoring/impl/victoriametrics.h b/include/ydb-cpp-sdk/client/monitoring/impl/victoriametrics.h new file mode 100644 index 0000000000..e1df456083 --- /dev/null +++ b/include/ydb-cpp-sdk/client/monitoring/impl/victoriametrics.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +namespace NYdb::NMonitoring { + +class TVictoriaMetricsMetric : public IMetric { +public: + TVictoriaMetricsMetric(const std::string& name, const std::string& value, + const std::unordered_map& labels) + : Name_(name) + , Value_(value) + , Labels_(labels) + {} + + std::string GetName() const override { return Name_; } + std::string GetValue() const override { return Value_; } + std::unordered_map GetLabels() const override { return Labels_; } + std::string GetType() const override { return "counter"; } + +private: + std::string Name_; + std::string Value_; + std::unordered_map Labels_; +}; + +class TVictoriaMetricsMonitoringSystem : public IMonitoringSystem { +public: + explicit TVictoriaMetricsMonitoringSystem(const std::string& endpoint) + : Endpoint_(endpoint) + { + Initialize(); + } + + void RecordMetric(const IMetric& metric) override; + void Flush() override; + std::string GetSystemName() const override { return "victoriametrics"; } + +private: + void Initialize(); + std::string Endpoint_; + std::shared_ptr Client_; +}; + +} // namespace NYdb::NMonitoring \ No newline at end of file From 7fe849113efb1aaebc7559d8f8b5ab13941b3915 Mon Sep 17 00:00:00 2001 From: Aleksey Myasnikov Date: Fri, 21 Mar 2025 14:15:21 +0000 Subject: [PATCH 3/4] CMakeLists.txt --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 362430d342..3db47df44d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,12 @@ if(YDB_SDK_MONITORING_VICTORIAMETRICS) find_package(VictoriaMetrics REQUIRED) endif() +# Определяем основную библиотеку +add_library(ydb-cpp-sdk STATIC + $ + $ +) + # Добавляем зависимости в target_link_libraries target_link_libraries(ydb-cpp-sdk $<$:prometheus::prometheus-cpp> From d65c41a6b6d7e431de587f1a3dd8fc7001ec3f05 Mon Sep 17 00:00:00 2001 From: Aleksey Myasnikov Date: Fri, 21 Mar 2025 18:09:09 +0000 Subject: [PATCH 4/4] fix --- CMakeLists.txt | 35 ++++- cmake/external_libs.cmake | 5 + docs/monitoring.md | 147 ++++++++++++++---- examples/monitoring/prometheus_example.cpp | 72 +++++++-- .../client/monitoring/impl/prometheus.h | 1 + 5 files changed, 221 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3db47df44d..79fbe62cd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,18 +132,47 @@ if(YDB_SDK_MONITORING_VICTORIAMETRICS) find_package(VictoriaMetrics REQUIRED) endif() +# Поиск дополнительных зависимостей +find_package(Brotli REQUIRED) +find_package(LZ4 REQUIRED) +find_package(xxHash REQUIRED) +find_package(ZSTD REQUIRED) + # Определяем основную библиотеку add_library(ydb-cpp-sdk STATIC - $ - $ + src/client/common_client/settings.cpp + src/client/driver/driver.cpp ) # Добавляем зависимости в target_link_libraries target_link_libraries(ydb-cpp-sdk - $<$:prometheus::prometheus-cpp> + PRIVATE + client-ydb_common_client + client-ydb_types-credentials + impl-ydb_internal-common + yutil + protobuf::libprotobuf + grpc++ + grpc++_reflection + OpenSSL::SSL + OpenSSL::Crypto + ZLIB::ZLIB + brotli + lz4 + xxhash + zstd + BZip2::BZip2 + IDN::IDN + $<$:prometheus-cpp::prometheus-cpp> $<$:OpenTelemetry::OpenTelemetry> $<$:Datadog::Datadog> $<$:NewRelic::NewRelic> $<$:AppDynamics::AppDynamics> $<$:VictoriaMetrics::VictoriaMetrics> ) + +target_include_directories(ydb-cpp-sdk + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src +) diff --git a/cmake/external_libs.cmake b/cmake/external_libs.cmake index c258dea642..33f2b57f8b 100644 --- a/cmake/external_libs.cmake +++ b/cmake/external_libs.cmake @@ -15,6 +15,11 @@ find_package(jwt-cpp REQUIRED) find_package(GTest REQUIRED) find_package(double-conversion REQUIRED) +# Prometheus +if (YDB_SDK_MONITORING_PROMETHEUS) + find_package(prometheus-cpp REQUIRED) +endif() + # RapidJSON if (YDB_SDK_USE_RAPID_JSON) find_package(RapidJSON REQUIRED) diff --git a/docs/monitoring.md b/docs/monitoring.md index 0da3b56d5d..c76123da8a 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -34,20 +34,64 @@ cmake -DYDB_SDK_MONITORING_PROMETHEUS=ON \ ```cpp #include #include +#include +#include // Создаем систему мониторинга auto monitoringSystem = TMonitoringSystemFactory::CreatePrometheus("localhost:9090"); TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); -// Создаем метрику -std::unordered_map labels = { - {"operation", "query"}, - {"status", "success"} -}; -TPrometheusMetric metric("ydb_query_count", "1", labels); - -// Записываем метрику -TMetricsContext::Instance().RecordMetric(metric); +// Создаем драйвер YDB +TDriverConfig config; +config.SetEndpoint("localhost:2135"); +config.SetDatabase("/local"); +TDriver driver(config); + +// Создаем клиент таблиц +TTableClient client(driver); + +try { + // Выполняем запрос + auto result = client.ExecuteDataQuery(R"( + SELECT 1 + 1 AS result; + )", TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()); + + // Создаем метрики на основе результата запроса + std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"}, + {"database", "/local"} + }; + + // Метрика количества запросов + TPrometheusMetric queryCount("ydb_query_count", "1", labels); + TMetricsContext::Instance().RecordMetric(queryCount); + + // Метрика задержки запроса + auto latency = result.GetExecutionTime().MilliSeconds(); + TPrometheusMetric queryLatency("ydb_query_latency", std::to_string(latency), labels); + TMetricsContext::Instance().RecordMetric(queryLatency); + + // Метрика количества строк в результате + auto rowCount = result.GetResultSet(0).RowsCount(); + TPrometheusMetric resultRows("ydb_result_rows", std::to_string(rowCount), labels); + TMetricsContext::Instance().RecordMetric(resultRows); + + // Принудительно отправляем метрики + TMetricsContext::Instance().Flush(); + +} catch (const TYdbErrorException& e) { + // В случае ошибки отправляем метрику ошибки + std::unordered_map errorLabels = { + {"operation", "query"}, + {"status", "error"}, + {"database", "/local"}, + {"error_type", e.GetStatus().ToString()} + }; + TPrometheusMetric errorCount("ydb_error_count", "1", errorLabels); + TMetricsContext::Instance().RecordMetric(errorCount); + TMetricsContext::Instance().Flush(); +} ``` ### OpenTelemetry @@ -55,20 +99,64 @@ TMetricsContext::Instance().RecordMetric(metric); ```cpp #include #include +#include +#include // Создаем систему мониторинга auto monitoringSystem = TMonitoringSystemFactory::CreateOpenTelemetry("localhost:4317"); TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); -// Создаем метрику -std::unordered_map labels = { - {"operation", "query"}, - {"status", "success"} -}; -TOpenTelemetryMetric metric("ydb_query_count", "1", labels); - -// Записываем метрику -TMetricsContext::Instance().RecordMetric(metric); +// Создаем драйвер YDB +TDriverConfig config; +config.SetEndpoint("localhost:2135"); +config.SetDatabase("/local"); +TDriver driver(config); + +// Создаем клиент таблиц +TTableClient client(driver); + +try { + // Выполняем запрос + auto result = client.ExecuteDataQuery(R"( + SELECT 1 + 1 AS result; + )", TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()); + + // Создаем метрики на основе результата запроса + std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"}, + {"database", "/local"} + }; + + // Метрика количества запросов + TOpenTelemetryMetric queryCount("ydb_query_count", "1", labels); + TMetricsContext::Instance().RecordMetric(queryCount); + + // Метрика задержки запроса + auto latency = result.GetExecutionTime().MilliSeconds(); + TOpenTelemetryMetric queryLatency("ydb_query_latency", std::to_string(latency), labels); + TMetricsContext::Instance().RecordMetric(queryLatency); + + // Метрика количества строк в результате + auto rowCount = result.GetResultSet(0).RowsCount(); + TOpenTelemetryMetric resultRows("ydb_result_rows", std::to_string(rowCount), labels); + TMetricsContext::Instance().RecordMetric(resultRows); + + // Принудительно отправляем метрики + TMetricsContext::Instance().Flush(); + +} catch (const TYdbErrorException& e) { + // В случае ошибки отправляем метрику ошибки + std::unordered_map errorLabels = { + {"operation", "query"}, + {"status", "error"}, + {"database", "/local"}, + {"error_type", e.GetStatus().ToString()} + }; + TOpenTelemetryMetric errorCount("ydb_error_count", "1", errorLabels); + TMetricsContext::Instance().RecordMetric(errorCount); + TMetricsContext::Instance().Flush(); +} ``` ### Datadog @@ -160,19 +248,26 @@ TMetricsContext::Instance().RecordMetric(metric); YDB C++ SDK собирает следующие метрики: - `ydb_query_count` - количество запросов -- `ydb_query_latency` - задержка запросов +- `ydb_query_latency` - задержка запросов (в миллисекундах) - `ydb_error_count` - количество ошибок +- `ydb_result_rows` - количество строк в результате запроса - `ydb_connection_count` - количество активных соединений - `ydb_session_count` - количество активных сессий +Каждая метрика может иметь следующие метки (labels): +- `operation` - тип операции (query, transaction и т.д.) +- `status` - статус операции (success, error) +- `database` - имя базы данных +- `error_type` - тип ошибки (только для метрик ошибок) + ## Примеры Полные примеры использования различных систем мониторинга можно найти в директории `examples/monitoring/`: -- `prometheus_example.cpp` - пример использования Prometheus -- `opentelemetry_example.cpp` - пример использования OpenTelemetry -- `datadog_example.cpp` - пример использования Datadog -- `newrelic_example.cpp` - пример использования New Relic -- `appdynamics_example.cpp` - пример использования AppDynamics -- `victoriametrics_example.cpp` - пример использования Victoria Metrics -- `solomon_example.cpp` - пример использования Yandex Solomon \ No newline at end of file +- `prometheus_example.cpp` - пример использования Prometheus с реальными запросами к YDB +- `opentelemetry_example.cpp` - пример использования OpenTelemetry с реальными запросами к YDB +- `datadog_example.cpp` - пример использования Datadog с реальными запросами к YDB +- `newrelic_example.cpp` - пример использования New Relic с реальными запросами к YDB +- `appdynamics_example.cpp` - пример использования AppDynamics с реальными запросами к YDB +- `victoriametrics_example.cpp` - пример использования Victoria Metrics с реальными запросами к YDB +- `solomon_example.cpp` - пример использования Yandex Solomon с реальными запросами к YDB \ No newline at end of file diff --git a/examples/monitoring/prometheus_example.cpp b/examples/monitoring/prometheus_example.cpp index 5154b121f8..6f61516d32 100644 --- a/examples/monitoring/prometheus_example.cpp +++ b/examples/monitoring/prometheus_example.cpp @@ -1,28 +1,80 @@ #include #include #include +#include +#include #include +#include +#include using namespace NYdb; using namespace NYdb::NMonitoring; +using namespace NYdb::NTable; int main() { // Создаем систему мониторинга Prometheus auto monitoringSystem = TMonitoringSystemFactory::CreatePrometheus("localhost:9090"); TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); - // Создаем метрику - std::unordered_map labels = { - {"operation", "query"}, - {"status", "success"} - }; - TPrometheusMetric metric("ydb_query_count", "1", labels); + // Создаем драйвер YDB + TDriverConfig config; + config.SetEndpoint("localhost:2135"); + config.SetDatabase("/local"); + TDriver driver(config); - // Записываем метрику - TMetricsContext::Instance().RecordMetric(metric); + // Создаем клиент таблиц + TTableClient client(driver); - // Принудительно отправляем метрики - TMetricsContext::Instance().Flush(); + try { + // Выполняем запрос + auto result = client.ExecuteDataQuery(R"( + SELECT 1 + 1 AS result; + )", TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()); + + // Создаем метрики на основе результата запроса + std::unordered_map labels = { + {"operation", "query"}, + {"status", "success"}, + {"database", "/local"} + }; + + // Метрика количества запросов + TPrometheusMetric queryCount("ydb_query_count", "1", labels); + TMetricsContext::Instance().RecordMetric(queryCount); + + // Метрика задержки запроса (в миллисекундах) + auto latency = result.GetExecutionTime().MilliSeconds(); + TPrometheusMetric queryLatency("ydb_query_latency", std::to_string(latency), labels); + TMetricsContext::Instance().RecordMetric(queryLatency); + + // Метрика количества строк в результате + auto rowCount = result.GetResultSet(0).RowsCount(); + TPrometheusMetric resultRows("ydb_result_rows", std::to_string(rowCount), labels); + TMetricsContext::Instance().RecordMetric(resultRows); + + // Принудительно отправляем метрики + TMetricsContext::Instance().Flush(); + + std::cout << "Query executed successfully. Metrics sent to Prometheus." << std::endl; + + } catch (const TYdbErrorException& e) { + // В случае ошибки отправляем метрику ошибки + std::unordered_map errorLabels = { + {"operation", "query"}, + {"status", "error"}, + {"database", "/local"}, + {"error_type", e.GetStatus().ToString()} + }; + TPrometheusMetric errorCount("ydb_error_count", "1", errorLabels); + TMetricsContext::Instance().RecordMetric(errorCount); + TMetricsContext::Instance().Flush(); + + std::cerr << "Error executing query: " << e.GetStatus() << std::endl; + return 1; + } + + // Ждем некоторое время, чтобы метрики успели отправиться + std::this_thread::sleep_for(std::chrono::seconds(1)); return 0; } \ No newline at end of file diff --git a/include/ydb-cpp-sdk/client/monitoring/impl/prometheus.h b/include/ydb-cpp-sdk/client/monitoring/impl/prometheus.h index 706c8427f7..5424848797 100644 --- a/include/ydb-cpp-sdk/client/monitoring/impl/prometheus.h +++ b/include/ydb-cpp-sdk/client/monitoring/impl/prometheus.h @@ -20,6 +20,7 @@ class TPrometheusMetric : public IMetric { std::string GetName() const override { return Name_; } std::string GetValue() const override { return Value_; } std::unordered_map GetLabels() const override { return Labels_; } + std::string GetType() const override { return "counter"; } private: std::string Name_;