diff --git a/CMakeLists.txt b/CMakeLists.txt index d0b2557078..79fbe62cd2 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,73 @@ 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() + +# Поиск дополнительных зависимостей +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 + 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 new file mode 100644 index 0000000000..c76123da8a --- /dev/null +++ b/docs/monitoring.md @@ -0,0 +1,273 @@ +# Мониторинг в 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 +#include +#include + +// Создаем систему мониторинга +auto monitoringSystem = TMonitoringSystemFactory::CreatePrometheus("localhost:9090"); +TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + +// Создаем драйвер 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 + +```cpp +#include +#include +#include +#include + +// Создаем систему мониторинга +auto monitoringSystem = TMonitoringSystemFactory::CreateOpenTelemetry("localhost:4317"); +TMetricsContext::Instance().SetMonitoringSystem(std::move(monitoringSystem)); + +// Создаем драйвер 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 + +```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 собирает следующие метрики: + +- `ydb_query_count` - количество запросов +- `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 с реальными запросами к 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/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/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..6f61516d32 --- /dev/null +++ b/examples/monitoring/prometheus_example.cpp @@ -0,0 +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)); + + // Создаем драйвер 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(); + + 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/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/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/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/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..5424848797 --- /dev/null +++ b/include/ydb-cpp-sdk/client/monitoring/impl/prometheus.h @@ -0,0 +1,48 @@ +#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_; } + std::string GetType() const override { return "counter"; } + +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/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 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