From f7ceebd718bccfa833b8b7e412565e25872519f3 Mon Sep 17 00:00:00 2001 From: Dimitris Christodoulou Date: Fri, 21 Nov 2025 13:45:20 +0000 Subject: [PATCH 1/2] CXXCBC-756: Move metric instrumentation points to Public API --- CMakeLists.txt | 1 + core/bucket.cxx | 2 + core/cluster.cxx | 11 + core/cluster.hxx | 6 + core/impl/analytics_index_manager.cxx | 272 ++++++------- core/impl/binary_collection.cxx | 134 +++---- core/impl/bucket_manager.cxx | 144 ++++--- core/impl/collection.cxx | 360 ++++++++---------- core/impl/collection_manager.cxx | 140 +++---- core/impl/observability_recorder.cxx | 161 ++++++++ core/impl/observability_recorder.hxx | 77 ++++ core/impl/public_bucket.cxx | 37 +- core/impl/public_cluster.cxx | 94 ++--- core/impl/query_index_manager.cxx | 298 ++++++++------- core/impl/scope.cxx | 94 ++--- core/impl/search_index_manager.cxx | 284 +++++++------- core/io/http_command.hxx | 2 + core/io/mcbp_command.hxx | 2 + core/metrics/constants.hxx | 23 ++ core/metrics/logging_meter.cxx | 3 +- core/metrics/meter_wrapper.cxx | 57 ++- core/metrics/meter_wrapper.hxx | 2 +- core/operations/document_get_all_replicas.hxx | 4 +- core/operations/document_get_any_replica.hxx | 4 +- .../document_lookup_in_all_replicas.hxx | 4 +- .../document_lookup_in_any_replica.hxx | 4 +- core/tracing/constants.hxx | 10 +- test/test_integration_meter.cxx | 214 ++++++----- test/test_integration_tracer.cxx | 4 +- test/test_integration_wrapper_tracer.cxx | 2 +- test/test_unit_metrics.cxx | 19 +- 31 files changed, 1305 insertions(+), 1164 deletions(-) create mode 100644 core/impl/observability_recorder.cxx create mode 100644 core/impl/observability_recorder.hxx create mode 100644 core/metrics/constants.hxx diff --git a/CMakeLists.txt b/CMakeLists.txt index b366ea464..39f020c15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,6 +213,7 @@ set(couchbase_cxx_client_FILES core/impl/view_error_category.cxx core/impl/wildcard_query.cxx core/impl/crypto.cxx + core/impl/observability_recorder.cxx core/io/config_tracker.cxx core/io/dns_client.cxx core/io/dns_config.cxx diff --git a/core/bucket.cxx b/core/bucket.cxx index f363b553d..bd5c0f8e4 100644 --- a/core/bucket.cxx +++ b/core/bucket.cxx @@ -119,11 +119,13 @@ class bucket_impl std::optional error_info) { // TODO(SA): copy from mcbp_command, subject to refactor later +#ifdef COUCHBASE_CXX_CLIENT_CREATE_OPERATION_SPAN_IN_CORE metrics::metric_attributes attrs{ service_type::key_value, fmt::format("{}", req->command_), ec, name_, req->scope_name_, req->collection_name_, }; meter_->record_value(std::move(attrs), req->dispatched_time_); +#endif if (ec == asio::error::operation_aborted) { // TODO(SA): fix tracing diff --git a/core/cluster.cxx b/core/cluster.cxx index 595c46693..9d734a2f2 100644 --- a/core/cluster.cxx +++ b/core/cluster.cxx @@ -1266,6 +1266,11 @@ class cluster_impl : public std::enable_shared_from_this return tracer_; } + auto meter() const -> const std::shared_ptr& + { + return meter_; + } + private: void setup_observability() { @@ -2474,4 +2479,10 @@ cluster::tracer() const -> const std::shared_ptr& return impl_->tracer(); } +auto +cluster::meter() const -> const std::shared_ptr& +{ + return impl_->meter(); +} + } // namespace couchbase::core diff --git a/core/cluster.hxx b/core/cluster.hxx index a2a471eb6..730120ff8 100644 --- a/core/cluster.hxx +++ b/core/cluster.hxx @@ -44,6 +44,11 @@ namespace tracing class tracer_wrapper; } // namespace tracing +namespace metrics +{ +class meter_wrapper; +} // namespace metrics + namespace mcbp { class queue_request; @@ -331,6 +336,7 @@ public: [[nodiscard]] auto to_string() const -> std::string; [[nodiscard]] auto tracer() const -> const std::shared_ptr&; + [[nodiscard]] auto meter() const -> const std::shared_ptr&; private: std::shared_ptr impl_; diff --git a/core/impl/analytics_index_manager.cxx b/core/impl/analytics_index_manager.cxx index 7a47c2287..07e913597 100644 --- a/core/impl/analytics_index_manager.cxx +++ b/core/impl/analytics_index_manager.cxx @@ -17,6 +17,7 @@ #include "core/cluster.hxx" #include "core/impl/error.hxx" +#include "core/impl/observability_recorder.hxx" #include "core/logger/logger.hxx" #include "core/management/analytics_link_azure_blob_external.hxx" #include "core/management/analytics_link_couchbase_remote.hxx" @@ -153,39 +154,35 @@ class analytics_index_manager_impl const create_dataverse_analytics_options::built& options, create_dataverse_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_create_dataverse, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_create_dataverse, options.parent_span); core::operations::management::analytics_dataverse_create_request request{ - dataverse_name, options.ignore_if_exists, {}, options.timeout, span, + dataverse_name, options.ignore_if_exists, {}, options.timeout, obs_rec->operation_span(), }; - return core_.execute( - std::move(request), - [dataverse_name, span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - handler(core::impl::make_error(resp.ctx)); - }); + return core_.execute(std::move(request), + [dataverse_name, + obs_rec = std::move(obs_rec), + handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + handler(core::impl::make_error(resp.ctx)); + }); } void drop_dataverse(const std::string& dataverse_name, const drop_dataverse_analytics_options::built& options, drop_dataverse_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_drop_dataverse, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_drop_dataverse, options.parent_span); core::operations::management::analytics_dataverse_drop_request request{ - dataverse_name, options.ignore_if_not_exists, {}, options.timeout, span, + dataverse_name, options.ignore_if_not_exists, {}, options.timeout, obs_rec->operation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -195,8 +192,8 @@ class analytics_index_manager_impl const create_dataset_analytics_options::built& options, create_dataset_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_create_dataset, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_create_dataset, options.parent_span); core::operations::management::analytics_dataset_create_request request{ options.dataverse_name.value_or(DEFAULT_DATAVERSE_NAME), @@ -206,14 +203,12 @@ class analytics_index_manager_impl {}, options.timeout, options.ignore_if_exists, - span, + obs_rec->operation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -222,8 +217,8 @@ class analytics_index_manager_impl const drop_dataset_analytics_options::built& options, drop_dataset_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_drop_dataset, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_drop_dataset, options.parent_span); core::operations::management::analytics_dataset_drop_request request{ options.dataverse_name.value_or(DEFAULT_DATAVERSE_NAME), @@ -231,14 +226,12 @@ class analytics_index_manager_impl options.ignore_if_not_exists, {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -246,23 +239,20 @@ class analytics_index_manager_impl void get_all_datasets(const get_all_datasets_analytics_options::built& options, get_all_datasets_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_get_all_datasets, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_get_all_datasets, options.parent_span); core::operations::management::analytics_dataset_get_all_request request{ {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)]( + [obs_rec = std::move(obs_rec), handler = std::move(handler)]( const core::operations::management::analytics_dataset_get_all_response& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec) { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx), {}); } std::vector datasets{}; @@ -275,7 +265,7 @@ class analytics_index_manager_impl d.bucket_name, }); } - span->end(); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx), datasets); }); } @@ -286,8 +276,8 @@ class analytics_index_manager_impl const create_index_analytics_options::built& options, create_index_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_create_index, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_create_index, options.parent_span); core::operations::management::analytics_index_create_request request{ options.dataverse_name.value_or(DEFAULT_DATAVERSE_NAME), @@ -297,14 +287,12 @@ class analytics_index_manager_impl options.ignore_if_exists, {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -314,8 +302,8 @@ class analytics_index_manager_impl const drop_index_analytics_options::built& options, drop_index_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_drop_index, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mgr_analytics_drop_index, + options.parent_span); core::operations::management::analytics_index_drop_request request{ options.dataverse_name.value_or(DEFAULT_DATAVERSE_NAME), @@ -324,14 +312,12 @@ class analytics_index_manager_impl options.ignore_if_not_exists, {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -339,23 +325,20 @@ class analytics_index_manager_impl void get_all_indexes(const get_all_indexes_analytics_options::built& options, get_all_indexes_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_get_all_indexes, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_get_all_indexes, options.parent_span); core::operations::management::analytics_index_get_all_request request{ {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)]( + [obs_rec = std::move(obs_rec), handler = std::move(handler)]( const core::operations::management::analytics_index_get_all_response& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec) { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx), {}); } std::vector indexes{}; @@ -368,7 +351,7 @@ class analytics_index_manager_impl idx.is_primary, }); } - span->end(); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx), indexes); }); } @@ -376,8 +359,8 @@ class analytics_index_manager_impl void connect_link(const connect_link_analytics_options::built& options, connect_link_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_connect_link, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_connect_link, options.parent_span); core::operations::management::analytics_link_connect_request request{ options.dataverse_name.value_or(DEFAULT_DATAVERSE_NAME), @@ -385,14 +368,12 @@ class analytics_index_manager_impl options.force, {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -400,22 +381,20 @@ class analytics_index_manager_impl void disconnect_link(const disconnect_link_analytics_options::built& options, disconnect_link_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_disconnect_link, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_disconnect_link, options.parent_span); core::operations::management::analytics_link_disconnect_request request{ options.dataverse_name.value_or(DEFAULT_DATAVERSE_NAME), options.link_name.value_or(DEFAULT_LINK_NAME), {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -423,23 +402,20 @@ class analytics_index_manager_impl void get_pending_mutations(const get_pending_mutations_analytics_options::built& options, get_pending_mutations_analytics_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_analytics_get_pending_mutations, - options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_get_pending_mutations, options.parent_span); core::operations::management::analytics_get_pending_mutations_request request{ {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)]( + [obs_rec = std::move(obs_rec), handler = std::move(handler)]( const core::operations::management::analytics_get_pending_mutations_response& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec) { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx), {}); } std::map> pending_mutations{}; @@ -454,7 +430,7 @@ class analytics_index_manager_impl } pending_mutations.at(dataverse_name).insert({ dataset_name, mutation_count }); } - span->end(); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx), pending_mutations); }); } @@ -463,8 +439,8 @@ class analytics_index_manager_impl const create_link_analytics_options::built& options, create_link_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_create_link, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_create_link, options.parent_span); switch (link.link_type()) { case management::s3_external: { @@ -474,15 +450,12 @@ class analytics_index_manager_impl to_core_s3_external_link(link), {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -493,15 +466,12 @@ class analytics_index_manager_impl to_core_azure_blob_external_link(link), {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -512,15 +482,12 @@ class analytics_index_manager_impl to_core_couchbase_remote_link(link), {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -531,8 +498,8 @@ class analytics_index_manager_impl const replace_link_analytics_options::built& options, replace_link_analytics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_analytics_replace_link, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_analytics_replace_link, options.parent_span); switch (link.link_type()) { case management::s3_external: { @@ -542,15 +509,12 @@ class analytics_index_manager_impl to_core_s3_external_link(link), {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -561,15 +525,12 @@ class analytics_index_manager_impl to_core_azure_blob_external_link(link), {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -580,15 +541,12 @@ class analytics_index_manager_impl to_core_couchbase_remote_link(link), {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -600,17 +558,16 @@ class analytics_index_manager_impl const drop_link_analytics_options::built& options, drop_link_analytics_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_analytics_drop_link, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mgr_analytics_drop_link, + options.parent_span); core::operations::management::analytics_link_drop_request request{ - link_name, dataverse_name, {}, options.timeout, span, + link_name, dataverse_name, {}, options.timeout, obs_rec->operation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -618,10 +575,11 @@ class analytics_index_manager_impl void get_links(const get_links_analytics_options::built& options, get_links_analytics_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_analytics_get_links, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mgr_analytics_get_links, + options.parent_span); core::operations::management::analytics_link_get_all_request req{ - {}, {}, {}, {}, options.timeout, span, + {}, {}, {}, {}, options.timeout, obs_rec->operation_span(), }; if (options.name.has_value()) { req.link_name = options.name.value(); @@ -644,13 +602,10 @@ class analytics_index_manager_impl } return core_.execute( std::move(req), - [span = std::move(span), handler = std::move(handler)]( + [obs_rec = std::move(obs_rec), handler = std::move(handler)]( const core::operations::management::analytics_link_get_all_response& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec) { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx), {}); } std::vector> links{}; @@ -697,22 +652,21 @@ class analytics_index_manager_impl links.push_back(std::move(azure_link)); } - span->end(); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx), std::move(links)); }); } private: - [[nodiscard]] auto create_span(const std::string& operation_name, - const std::shared_ptr& parent_span) const - -> std::shared_ptr + [[nodiscard]] auto create_observability_recorder( + const std::string& operation_name, + const std::shared_ptr& parent_span) const + -> std::unique_ptr { - auto span = core_.tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - span->add_tag(core::tracing::attributes::op::service, core::tracing::service::analytics); - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); - } - return span; + auto rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); + rec->with_service(core::tracing::service::analytics); + return rec; } core::cluster core_; diff --git a/core/impl/binary_collection.cxx b/core/impl/binary_collection.cxx index bedcd0292..1c3dc6be0 100644 --- a/core/impl/binary_collection.cxx +++ b/core/impl/binary_collection.cxx @@ -19,6 +19,7 @@ #include "core/cluster.hxx" #include "core/impl/error.hxx" +#include "core/impl/observability_recorder.hxx" #include "core/operations/document_append.hxx" #include "core/operations/document_decrement.hxx" #include "core/operations/document_increment.hxx" @@ -86,7 +87,7 @@ class binary_collection_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto&& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); if (resp.ctx.ec()) { return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{}); } @@ -131,20 +129,16 @@ class binary_collection_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute(std::move(request), [core = core_, id = std::move(id), options, - span = std::move(span), + obs_rec = std::move(obs_rec), handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); - span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec()) { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{ resp.cas, std::move(resp.token) }); } @@ -157,10 +151,10 @@ class binary_collection_impl : public std::enable_shared_from_this(resp), handler = std::move(handler)](std::error_code ec) mutable { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), ec); if (ec) { resp.ctx.override_ec(ec); return handler(core::impl::make_error(std::move(resp.ctx)), @@ -177,7 +171,7 @@ class binary_collection_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto&& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); if (resp.ctx.ec()) { return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{}); } @@ -222,20 +213,16 @@ class binary_collection_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute(std::move(request), - [span = std::move(span), + [obs_rec = std::move(obs_rec), core = core_, id = std::move(id), options, handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); - span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec()) { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{ resp.cas, std::move(resp.token) }); } @@ -248,10 +235,10 @@ class binary_collection_impl : public std::enable_shared_from_this(resp), handler = std::move(handler)](std::error_code ec) mutable { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), ec); if (ec) { resp.ctx.override_ec(ec); return handler(core::impl::make_error(std::move(resp.ctx)), @@ -267,7 +254,7 @@ class binary_collection_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto&& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); if (resp.ctx.ec()) { return handler(core::impl::make_error(std::move(resp.ctx)), counter_result{}); @@ -315,20 +299,17 @@ class binary_collection_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), + [obs_rec = std::move(obs_rec), core = core_, id = std::move(id), options, handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec()) { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), counter_result{ resp.cas, std::move(resp.token), resp.content }); } @@ -341,10 +322,10 @@ class binary_collection_impl : public std::enable_shared_from_this(resp), handler = std::move(handler)](std::error_code ec) mutable { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), ec); if (ec) { resp.ctx.override_ec(ec); return handler(core::impl::make_error(std::move(resp.ctx)), counter_result{}); @@ -359,7 +340,7 @@ class binary_collection_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto&& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); if (resp.ctx.ec()) { return handler(core::impl::make_error(std::move(resp.ctx)), counter_result{}); } @@ -406,20 +384,17 @@ class binary_collection_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), + [obs_rec = std::move(obs_rec), core = core_, id = std::move(id), options, handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec()) { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), counter_result{ resp.cas, std::move(resp.token), resp.content }); } @@ -432,10 +407,10 @@ class binary_collection_impl : public std::enable_shared_from_this(resp), handler = std::move(handler)](std::error_code ec) mutable { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), ec); if (ec) { resp.ctx.override_ec(ec); return handler(core::impl::make_error(std::move(resp.ctx)), counter_result{}); @@ -447,28 +422,23 @@ class binary_collection_impl : public std::enable_shared_from_this const std::shared_ptr& - { - return core_.tracer(); - } - - auto create_kv_span(const std::string& operation_name, - const std::shared_ptr& parent_span, - const std::optional durability = {}) const - -> std::shared_ptr + auto create_observability_recorder(const std::string& operation_name, + const std::shared_ptr& parent_span, + const std::optional durability = {}) const + -> std::unique_ptr { - auto span = tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - span->add_tag(core::tracing::attributes::op::service, core::tracing::service::key_value); - span->add_tag(core::tracing::attributes::op::bucket_name, bucket_name_); - span->add_tag(core::tracing::attributes::op::scope_name, scope_name_); - span->add_tag(core::tracing::attributes::op::collection_name, name_); - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); - if (durability.has_value()) { - core::tracing::set_durability_level_attribute(span, durability.value()); - } + auto rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); + + rec->with_service(core::tracing::service::key_value); + rec->with_bucket_name(bucket_name_); + rec->with_scope_name(scope_name_); + rec->with_collection_name(name_); + if (durability.has_value()) { + rec->with_durability(durability.value()); } - return span; + + return rec; } core::cluster core_; diff --git a/core/impl/bucket_manager.cxx b/core/impl/bucket_manager.cxx index 989783332..a4e2c304e 100644 --- a/core/impl/bucket_manager.cxx +++ b/core/impl/bucket_manager.cxx @@ -19,6 +19,7 @@ #include "core/cluster.hxx" #include "core/impl/error.hxx" +#include "core/impl/observability_recorder.hxx" #include "core/management/bucket_settings.hxx" #include "core/operations/management/bucket_create.hxx" #include "core/operations/management/bucket_drop.hxx" @@ -267,151 +268,138 @@ class bucket_manager_impl const get_bucket_options::built& options, get_bucket_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mgr_buckets_get_bucket, bucket_name, options.parent_span); core::operations::management::bucket_get_request request{ std::move(bucket_name), {}, options.timeout, - span, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx), - map_bucket_settings(resp.bucket)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx), map_bucket_settings(resp.bucket)); + }); } void get_all_buckets(const get_all_buckets_options::built& options, get_all_buckets_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mgr_buckets_get_all_buckets, std::nullopt, options.parent_span); core::operations::management::bucket_get_all_request request{ {}, options.timeout, - span, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx), - map_all_bucket_settings(resp.buckets)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx), map_all_bucket_settings(resp.buckets)); + }); } void create_bucket(const management::cluster::bucket_settings& bucket_settings, const create_bucket_options::built& options, create_bucket_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_buckets_create_bucket, - bucket_settings.name, - options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mgr_buckets_create_bucket, + bucket_settings.name, + options.parent_span); core::operations::management::bucket_create_request request{ map_bucket_settings(bucket_settings), {}, options.timeout, - span, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void update_bucket(const management::cluster::bucket_settings& bucket_settings, const update_bucket_options::built& options, update_bucket_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_buckets_update_bucket, - bucket_settings.name, - options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mgr_buckets_update_bucket, + bucket_settings.name, + options.parent_span); core::operations::management::bucket_update_request request{ map_bucket_settings(bucket_settings), {}, options.timeout, - span, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void drop_bucket(std::string bucket_name, const drop_bucket_options::built& options, drop_bucket_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mgr_buckets_drop_bucket, bucket_name, options.parent_span); core::operations::management::bucket_drop_request request{ std::move(bucket_name), {}, options.timeout, - span, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void flush_bucket(std::string bucket_name, const flush_bucket_options::built& options, flush_bucket_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mgr_buckets_flush_bucket, bucket_name, options.parent_span); core::operations::management::bucket_flush_request request{ std::move(bucket_name), {}, options.timeout, - span, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } private: - [[nodiscard]] auto create_span(const std::string& operation_name, - const std::optional& bucket_name, - const std::shared_ptr& parent_span) const - -> std::shared_ptr + [[nodiscard]] auto create_observability_recorder( + const std::string& operation_name, + const std::optional& bucket_name, + const std::shared_ptr& parent_span) const + -> std::unique_ptr { - auto span = core_.tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - span->add_tag(core::tracing::attributes::op::service, core::tracing::service::management); - if (bucket_name.has_value()) { - span->add_tag(core::tracing::attributes::op::bucket_name, bucket_name.value()); - } - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); + auto rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); + rec->with_service(core::tracing::service::management); + if (bucket_name.has_value()) { + rec->with_bucket_name(bucket_name.value()); } - return span; + return rec; } core::cluster core_; diff --git a/core/impl/collection.cxx b/core/impl/collection.cxx index 70835d36b..fab73f824 100644 --- a/core/impl/collection.cxx +++ b/core/impl/collection.cxx @@ -21,6 +21,7 @@ #include "get_all_replicas.hxx" #include "get_any_replica.hxx" #include "internal_scan_result.hxx" +#include "observability_recorder.hxx" #include "observe_poll.hxx" #include "core/agent_group.hxx" @@ -28,6 +29,7 @@ #include "core/cluster.hxx" #include "core/impl/subdoc/command.hxx" #include "core/logger/logger.hxx" +#include "core/metrics/meter_wrapper.hxx" #include "core/operations/document_append.hxx" #include "core/operations/document_decrement.hxx" #include "core/operations/document_exists.hxx" @@ -150,14 +152,10 @@ class collection_impl : public std::enable_shared_from_this return crypto_manager_; } - [[nodiscard]] auto tracer() const -> const std::shared_ptr& - { - return core_.tracer(); - } - void get(std::string document_key, get_options::built options, get_handler&& handler) const { - auto span = create_kv_span(core::tracing::operation::mcbp_get, options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mcbp_get, options.parent_span); if (!options.with_expiry && options.projections.empty()) { core::operations::get_request request{ @@ -171,16 +169,14 @@ class collection_impl : public std::enable_shared_from_this {}, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), crypto_manager = crypto_manager_, handler = std::move(handler)]( - auto resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), + crypto_manager = crypto_manager_, + handler = std::move(handler)](auto resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler( core::impl::make_error(std::move(resp.ctx)), get_result{ @@ -202,20 +198,18 @@ class collection_impl : public std::enable_shared_from_this false, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), crypto_manager = crypto_manager_, handler = std::move(handler)]( - auto resp) mutable { + [obs_rec = std::move(obs_rec), + crypto_manager = crypto_manager_, + handler = std::move(handler)](auto resp) mutable { std::optional expiry_time{}; if (resp.expiry && resp.expiry.value() > 0) { expiry_time.emplace(std::chrono::seconds{ resp.expiry.value() }); } - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), get_result{ resp.cas, { std::move(resp.value), resp.flags }, @@ -229,7 +223,8 @@ class collection_impl : public std::enable_shared_from_this get_and_touch_options::built options, get_and_touch_handler&& handler) const { - auto span = create_kv_span(core::tracing::operation::mcbp_get_and_touch, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mcbp_get_and_touch, + options.parent_span); core::operations::get_and_touch_request request{ core::document_id{ @@ -243,17 +238,15 @@ class collection_impl : public std::enable_shared_from_this expiry, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), crypto_manager = crypto_manager_, handler = std::move(handler)]( - auto resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), + crypto_manager = crypto_manager_, + handler = std::move(handler)](auto resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler( core::impl::make_error(std::move(resp.ctx)), get_result{ @@ -266,7 +259,8 @@ class collection_impl : public std::enable_shared_from_this touch_options::built options, touch_handler&& handler) const { - auto span = create_kv_span(core::tracing::operation::mcbp_touch, options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mcbp_touch, options.parent_span); core::operations::touch_request request{ core::document_id{ @@ -280,15 +274,12 @@ class collection_impl : public std::enable_shared_from_this expiry, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), result{ resp.cas }); }); } @@ -297,7 +288,8 @@ class collection_impl : public std::enable_shared_from_this const get_any_replica_options::built& options, core::impl::movable_get_any_replica_handler&& handler) const { - auto span = create_kv_span(core::tracing::operation::mcbp_get_any_replica, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mcbp_get_any_replica, + options.parent_span); core::operations::get_any_replica_request request{ core::document_id{ @@ -308,13 +300,14 @@ class collection_impl : public std::enable_shared_from_this }, options.timeout, options.read_preference, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), crypto_manager = crypto_manager_, handler = std::move(handler)]( - auto resp) mutable { - span->end(); + [obs_rec = std::move(obs_rec), + crypto_manager = crypto_manager_, + handler = std::move(handler)](auto resp) mutable { + obs_rec->finish(resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), get_replica_result{ resp.cas, @@ -329,8 +322,8 @@ class collection_impl : public std::enable_shared_from_this const get_all_replicas_options::built& options, core::impl::movable_get_all_replicas_handler&& handler) const { - auto span = - create_kv_span(core::tracing::operation::mcbp_get_all_replicas, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mcbp_get_all_replicas, + options.parent_span); core::operations::get_all_replicas_request request{ core::document_id{ @@ -341,12 +334,13 @@ class collection_impl : public std::enable_shared_from_this }, options.timeout, options.read_preference, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), crypto_manager = crypto_manager_, handler = std::move(handler)]( - auto resp) mutable { + [obs_rec = std::move(obs_rec), + crypto_manager = crypto_manager_, + handler = std::move(handler)](auto resp) mutable { get_all_replicas_result result{}; for (auto& entry : resp.entries) { result.emplace_back(get_replica_result{ @@ -356,7 +350,7 @@ class collection_impl : public std::enable_shared_from_this crypto_manager, }); } - span->end(); + obs_rec->finish(resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), std::move(result)); }); } @@ -365,7 +359,8 @@ class collection_impl : public std::enable_shared_from_this remove_options::built options, remove_handler&& handler) const { - auto span = create_kv_span(core::tracing::operation::mcbp_remove, options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mcbp_remove, options.parent_span); auto id = core::document_id{ bucket_name_, @@ -382,15 +377,12 @@ class collection_impl : public std::enable_shared_from_this options.durability_level, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); if (resp.ctx.ec()) { return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{}); } @@ -407,20 +399,17 @@ class collection_impl : public std::enable_shared_from_this durability_level::none, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), + [obs_rec = std::move(obs_rec), core = core_, id = std::move(id), options, handler = std::move(handler)](auto&& resp) mutable { if (resp.ctx.ec()) { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{ resp.cas, std::move(resp.token) }); } @@ -432,11 +421,9 @@ class collection_impl : public std::enable_shared_from_this options.timeout, options.persist_to, options.replicate_to, - [span = std::move(span), resp, handler = std::move(handler)](std::error_code ec) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), resp, handler = std::move(handler)]( + std::error_code ec) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), ec); if (ec) { resp.ctx.override_ec(ec); return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{}); @@ -452,7 +439,8 @@ class collection_impl : public std::enable_shared_from_this get_and_lock_options::built options, get_and_lock_handler&& handler) const { - auto span = create_kv_span(core::tracing::operation::mcbp_get_and_lock, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mcbp_get_and_lock, + options.parent_span); core::operations::get_and_lock_request request{ core::document_id{ @@ -466,16 +454,14 @@ class collection_impl : public std::enable_shared_from_this static_cast(lock_duration.count()), options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; core_.execute( std::move(request), - [span = std::move(span), crypto_manager = crypto_manager_, handler = std::move(handler)]( - auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), + crypto_manager = crypto_manager_, + handler = std::move(handler)](auto&& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler( core::impl::make_error(std::move(resp.ctx)), get_result{ @@ -488,7 +474,8 @@ class collection_impl : public std::enable_shared_from_this unlock_options::built options, unlock_handler&& handler) const { - auto span = create_kv_span(core::tracing::operation::mcbp_unlock, options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mcbp_unlock, options.parent_span); core::operations::unlock_request request{ core::document_id{ @@ -502,24 +489,22 @@ class collection_impl : public std::enable_shared_from_this cas, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); - span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(std::move(resp.ctx))); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto&& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); + return handler(core::impl::make_error(std::move(resp.ctx))); + }); } void exists(std::string document_key, exists_options::built options, exists_handler&& handler) const { - auto span = create_kv_span(core::tracing::operation::mcbp_exists, options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mcbp_exists, options.parent_span); core::operations::exists_request request{ core::document_id{ @@ -532,15 +517,12 @@ class collection_impl : public std::enable_shared_from_this {}, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto&& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), exists_result{ resp.cas, resp.exists() }); }); @@ -551,7 +533,8 @@ class collection_impl : public std::enable_shared_from_this lookup_in_options::built options, lookup_in_handler&& handler) const { - auto span = create_kv_span(core::tracing::operation::mcbp_lookup_in, options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mcbp_lookup_in, options.parent_span); core::operations::lookup_in_request request{ core::document_id{ @@ -566,15 +549,12 @@ class collection_impl : public std::enable_shared_from_this specs, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); if (resp.ctx.ec()) { return handler(core::impl::make_error(std::move(resp.ctx)), lookup_in_result{}); @@ -601,8 +581,8 @@ class collection_impl : public std::enable_shared_from_this const lookup_in_all_replicas_options::built& options, lookup_in_all_replicas_handler&& handler) const { - auto span = - create_kv_span(core::tracing::operation::mcbp_lookup_in_all_replicas, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mcbp_lookup_in_all_replicas, options.parent_span); core::operations::lookup_in_all_replicas_request request{ core::document_id{ @@ -613,12 +593,12 @@ class collection_impl : public std::enable_shared_from_this }, specs, options.timeout, - span, + obs_rec->operation_span(), options.read_preference, }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto resp) mutable { + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) mutable { lookup_in_all_replicas_result result{}; for (auto& res : resp.entries) { std::vector entries; @@ -639,7 +619,7 @@ class collection_impl : public std::enable_shared_from_this res.is_replica, }); } - span->end(); + obs_rec->finish(resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), result); }); } @@ -649,8 +629,8 @@ class collection_impl : public std::enable_shared_from_this const lookup_in_any_replica_options::built& options, lookup_in_any_replica_handler&& handler) const { - auto span = - create_kv_span(core::tracing::operation::mcbp_lookup_in_any_replica, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mcbp_lookup_in_any_replica, options.parent_span); core::operations::lookup_in_any_replica_request request{ core::document_id{ @@ -661,12 +641,12 @@ class collection_impl : public std::enable_shared_from_this }, specs, options.timeout, - span, + obs_rec->operation_span(), options.read_preference, }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto resp) mutable { + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) mutable { std::vector entries; entries.reserve(resp.fields.size()); for (auto& field : resp.fields) { @@ -678,7 +658,7 @@ class collection_impl : public std::enable_shared_from_this field.ec, }); } - span->end(); + obs_rec->finish(resp.ctx.ec()); return handler( core::impl::make_error(std::move(resp.ctx)), lookup_in_replica_result{ resp.cas, std::move(entries), resp.deleted, resp.is_replica }); @@ -690,7 +670,7 @@ class collection_impl : public std::enable_shared_from_this mutate_in_options::built options, mutate_in_handler&& handler) const { - auto span = create_kv_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mcbp_mutate_in, options.parent_span, options.durability_level); auto id = core::document_id{ @@ -715,15 +695,12 @@ class collection_impl : public std::enable_shared_from_this options.timeout, { options.retry_strategy }, options.preserve_expiry, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); if (resp.ctx.ec()) { return handler(core::impl::make_error(std::move(resp.ctx)), mutate_in_result{}); } @@ -757,20 +734,17 @@ class collection_impl : public std::enable_shared_from_this options.timeout, { options.retry_strategy }, options.preserve_expiry, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), + [obs_rec = std::move(obs_rec), core = core_, id = std::move(id), options, handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec()) { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), mutate_in_result{}); } @@ -782,8 +756,9 @@ class collection_impl : public std::enable_shared_from_this options.timeout, options.persist_to, options.replicate_to, - [span = std::move(span), resp, handler = std::move(handler)](std::error_code ec) mutable { - span->end(); + [obs_rec = std::move(obs_rec), resp, handler = std::move(handler)]( + std::error_code ec) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), ec); if (ec) { resp.ctx.override_ec(ec); return handler(core::impl::make_error(std::move(resp.ctx)), mutate_in_result{}); @@ -809,10 +784,10 @@ class collection_impl : public std::enable_shared_from_this upsert_options::built options, upsert_handler&& handler) const { - auto span = create_kv_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mcbp_upsert, options.parent_span, options.durability_level); - auto [data, flags] = get_encoded_value(std::move(value), span); + auto [data, flags] = get_encoded_value(std::move(value), obs_rec); auto id = core::document_id{ bucket_name_, scope_name_, @@ -831,15 +806,12 @@ class collection_impl : public std::enable_shared_from_this options.timeout, { options.retry_strategy }, options.preserve_expiry, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{ resp.cas, std::move(resp.token) }); }); @@ -856,20 +828,17 @@ class collection_impl : public std::enable_shared_from_this options.timeout, { options.retry_strategy }, options.preserve_expiry, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), + [obs_rec = std::move(obs_rec), core = core_, id = std::move(id), options, handler = std::move(handler)](auto resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec()) { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{ resp.cas, std::move(resp.token) }); } @@ -882,8 +851,9 @@ class collection_impl : public std::enable_shared_from_this options.timeout, options.persist_to, options.replicate_to, - [span = std::move(span), resp, handler = std::move(handler)](std::error_code ec) mutable { - span->end(); + [obs_rec = std::move(obs_rec), resp, handler = std::move(handler)]( + std::error_code ec) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), ec); if (ec) { resp.ctx.override_ec(ec); return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{}); @@ -899,10 +869,10 @@ class collection_impl : public std::enable_shared_from_this insert_options::built options, insert_handler&& handler) const { - auto span = create_kv_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mcbp_insert, options.parent_span, options.durability_level); - auto [data, flags] = get_encoded_value(std::move(value), span); + auto [data, flags] = get_encoded_value(std::move(value), obs_rec); auto id = core::document_id{ bucket_name_, scope_name_, @@ -920,15 +890,12 @@ class collection_impl : public std::enable_shared_from_this options.durability_level, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto&& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); if (resp.ctx.ec()) { return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{}); } @@ -947,20 +914,17 @@ class collection_impl : public std::enable_shared_from_this durability_level::none, options.timeout, { options.retry_strategy }, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), + [obs_rec = std::move(obs_rec), core = core_, id = std::move(id), options, handler = std::move(handler)](auto resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - if (resp.ctx.ec()) { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{ resp.cas, std::move(resp.token) }); } @@ -973,8 +937,9 @@ class collection_impl : public std::enable_shared_from_this options.timeout, options.persist_to, options.replicate_to, - [span = std::move(span), resp, handler = std::move(handler)](std::error_code ec) mutable { - span->end(); + [obs_rec = std::move(obs_rec), resp, handler = std::move(handler)]( + std::error_code ec) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), ec); if (ec) { resp.ctx.override_ec(ec); return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{}); @@ -990,10 +955,10 @@ class collection_impl : public std::enable_shared_from_this replace_options::built options, replace_handler&& handler) const { - auto span = create_kv_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mcbp_replace, options.parent_span, options.durability_level); - auto [data, flags] = get_encoded_value(std::move(value), span); + auto [data, flags] = get_encoded_value(std::move(value), obs_rec); auto id = core::document_id{ bucket_name_, @@ -1014,15 +979,12 @@ class collection_impl : public std::enable_shared_from_this options.timeout, { options.retry_strategy }, options.preserve_expiry, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); if (resp.ctx.ec()) { return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{}); } @@ -1043,19 +1005,16 @@ class collection_impl : public std::enable_shared_from_this options.timeout, { options.retry_strategy }, options.preserve_expiry, - span, + obs_rec->operation_span(), }; return core_.execute(std::move(request), - [span = std::move(span), + [obs_rec = std::move(obs_rec), core = core_, id = std::move(id), options, handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts(); - span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } if (resp.ctx.ec()) { + obs_rec->finish(resp.ctx.retry_attempts(), resp.ctx.ec()); return handler(core::impl::make_error(std::move(resp.ctx)), mutation_result{ resp.cas, std::move(resp.token) }); } @@ -1068,9 +1027,9 @@ class collection_impl : public std::enable_shared_from_this options.timeout, options.persist_to, options.replicate_to, - [span = std::move(span), resp, handler = std::move(handler)]( + [obs_rec = std::move(obs_rec), resp, handler = std::move(handler)]( std::error_code ec) mutable { - span->end(); + obs_rec->finish(resp.ctx.retry_attempts(), ec); if (ec) { resp.ctx.override_ec(ec); return handler(core::impl::make_error(std::move(resp.ctx)), @@ -1084,7 +1043,8 @@ class collection_impl : public std::enable_shared_from_this void scan(scan_type::built scan_type, scan_options::built options, scan_handler&& handler) const { - auto span = create_kv_span(core::tracing::operation::mcbp_scan, options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mcbp_scan, options.parent_span); core::range_scan_orchestrator_options orchestrator_opts{ options.ids_only }; if (!options.mutation_state.empty()) { @@ -1139,30 +1099,30 @@ class collection_impl : public std::enable_shared_from_this return core_.open_bucket( bucket_name_, [this, - span = std::move(span), + obs_rec = std::move(obs_rec), handler = std::move(handler), orchestrator_opts, core_scan_type](std::error_code ec) mutable { if (ec) { - span->end(); + obs_rec->finish(ec); return handler(error(ec), {}); } return core_.with_bucket_configuration( bucket_name_, [this, - span = std::move(span), + obs_rec = std::move(obs_rec), handler = std::move(handler), orchestrator_opts, core_scan_type](std::error_code ec, const std::shared_ptr& config) mutable { if (ec) { - span->end(); + obs_rec->finish(ec); return handler( error(ec, "An error occurred when attempting to fetch the bucket configuration."), {}); } if (!config->capabilities.supports_range_scan()) { - span->end(); + obs_rec->finish(ec); return handler(error(errc::common::feature_not_available, "This bucket does not support range scan."), {}); @@ -1171,7 +1131,7 @@ class collection_impl : public std::enable_shared_from_this core::agent_group(core_.io_context(), core::agent_group_config{ { core_ } }); ec = agent_group.open_bucket(bucket_name_); if (ec) { - span->end(); + obs_rec->finish(ec); return handler(error(ec, fmt::format("An error occurred while opening the `{}` bucket.", bucket_name_)), @@ -1179,7 +1139,7 @@ class collection_impl : public std::enable_shared_from_this } auto agent = agent_group.get_agent(bucket_name_); if (!agent.has_value()) { - span->end(); + obs_rec->finish(ec); return handler( error(agent.error(), fmt::format( @@ -1190,7 +1150,7 @@ class collection_impl : public std::enable_shared_from_this if (!config->vbmap.has_value() || config->vbmap->empty()) { CB_LOG_WARNING("Unable to get vbucket map for `{}` - cannot perform scan operation", bucket_name_); - span->end(); + obs_rec->finish(ec); return handler(error(errc::common::request_canceled, "No vbucket map included with the bucket config"), {}); @@ -1204,10 +1164,10 @@ class collection_impl : public std::enable_shared_from_this core_scan_type, orchestrator_opts); return orchestrator.scan( - [span = std::move(span), + [obs_rec = std::move(obs_rec), crypto_manager = crypto_manager_, handler = std::move(handler)](auto ec, auto core_scan_result) mutable { - span->end(); + obs_rec->finish(ec); if (ec) { return handler(error(ec, "Error while starting the range scan"), {}); } @@ -1220,37 +1180,37 @@ class collection_impl : public std::enable_shared_from_this } private: - auto get_encoded_value( + static auto get_encoded_value( std::variant> value, - const std::shared_ptr& operation_span) const -> codec::encoded_value + const std::unique_ptr& obs_rec) -> codec::encoded_value { if (std::holds_alternative(value)) { return std::get(value); } - const auto request_encoding_span = - tracer()->create_span(core::tracing::operation::step_request_encoding, operation_span); + const auto request_encoding_span = obs_rec->create_request_encoding_span(); auto encoded = std::get>(value)(); request_encoding_span->end(); return encoded; } - auto create_kv_span(const std::string& operation_name, - const std::shared_ptr& parent_span, - const std::optional durability = {}) const - -> std::shared_ptr + [[nodiscard]] auto create_observability_recorder( + const std::string& operation_name, + const std::shared_ptr& parent_span, + const std::optional durability = {}) const + -> std::unique_ptr { - auto span = tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - span->add_tag(core::tracing::attributes::op::service, core::tracing::service::key_value); - span->add_tag(core::tracing::attributes::op::bucket_name, bucket_name_); - span->add_tag(core::tracing::attributes::op::scope_name, scope_name_); - span->add_tag(core::tracing::attributes::op::collection_name, name_); - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); - if (durability.has_value()) { - core::tracing::set_durability_level_attribute(span, durability.value()); - } + auto rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); + + rec->with_service(core::tracing::service::key_value); + rec->with_bucket_name(bucket_name_); + rec->with_scope_name(scope_name_); + rec->with_collection_name(name_); + if (durability.has_value()) { + rec->with_durability(durability.value()); } - return span; + + return rec; } core::cluster core_; diff --git a/core/impl/collection_manager.cxx b/core/impl/collection_manager.cxx index 1f6bbe269..dbdce0a1d 100644 --- a/core/impl/collection_manager.cxx +++ b/core/impl/collection_manager.cxx @@ -18,6 +18,7 @@ #include "core/cluster.hxx" #include "core/impl/error.hxx" +#include "core/impl/observability_recorder.hxx" #include "core/operations/management/collection_create.hxx" #include "core/operations/management/collection_drop.hxx" #include "core/operations/management/collection_update.hxx" @@ -95,20 +96,19 @@ class collection_manager_impl const couchbase::drop_collection_options::built& options, couchbase::drop_collection_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_collections_drop_collection, - scope_name, - collection_name, - options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mgr_collections_drop_collection, + scope_name, + collection_name, + options.parent_span); core::operations::management::collection_drop_request request{ - bucket_name_, std::move(scope_name), std::move(collection_name), {}, options.timeout, span, + bucket_name_, std::move(scope_name), std::move(collection_name), + {}, options.timeout, obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx)); }); } @@ -119,27 +119,20 @@ class collection_manager_impl const couchbase::update_collection_options::built& options, couchbase::update_collection_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_collections_update_collection, - scope_name, - collection_name, - options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mgr_collections_update_collection, + scope_name, + collection_name, + options.parent_span); core::operations::management::collection_update_request request{ - bucket_name_, - std::move(scope_name), - std::move(collection_name), - settings.max_expiry, - settings.history, - {}, - options.timeout, - span, + bucket_name_, std::move(scope_name), std::move(collection_name), + settings.max_expiry, settings.history, {}, + options.timeout, obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx)); }); } @@ -150,27 +143,20 @@ class collection_manager_impl const couchbase::create_collection_options::built& options, couchbase::update_collection_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_collections_create_collection, - scope_name, - collection_name, - options.parent_span); + auto obs_rec = + create_observability_recorder(core::tracing::operation::mgr_collections_create_collection, + scope_name, + collection_name, + options.parent_span); core::operations::management::collection_create_request request{ - bucket_name_, - std::move(scope_name), - std::move(collection_name), - settings.max_expiry, - settings.history, - {}, - options.timeout, - span, + bucket_name_, std::move(scope_name), std::move(collection_name), + settings.max_expiry, settings.history, {}, + options.timeout, obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx)); }); } @@ -178,21 +164,18 @@ class collection_manager_impl void get_all_scopes(const get_all_scopes_options::built& options, get_all_scopes_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mgr_collections_get_all_scopes, {}, {}, options.parent_span); core::operations::management::scope_get_all_request request{ bucket_name_, {}, options.timeout, - span, + obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](auto resp) mutable { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx), map_scope_specs(resp.manifest)); }); } @@ -201,18 +184,15 @@ class collection_manager_impl const couchbase::create_scope_options::built& options, couchbase::create_scope_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mgr_collections_create_scope, scope_name, {}, options.parent_span); core::operations::management::scope_create_request request{ - bucket_name_, std::move(scope_name), {}, options.timeout, span, + bucket_name_, std::move(scope_name), {}, options.timeout, obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx)); }); } @@ -221,42 +201,38 @@ class collection_manager_impl const couchbase::drop_scope_options::built& options, couchbase::drop_scope_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::mgr_collections_drop_scope, scope_name, {}, options.parent_span); core::operations::management::scope_drop_request request{ - bucket_name_, std::move(scope_name), {}, options.timeout, span, + bucket_name_, std::move(scope_name), {}, options.timeout, obs_rec->operation_span(), }; return core_.execute( std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx)); }); } private: - [[nodiscard]] auto create_span(const std::string& operation_name, - const std::optional& scope_name, - const std::optional& collection_name, - const std::shared_ptr& parent_span) const - -> std::shared_ptr + [[nodiscard]] auto create_observability_recorder( + const std::string& operation_name, + const std::optional& scope_name, + const std::optional& collection_name, + const std::shared_ptr& parent_span) const + -> std::unique_ptr { - auto span = core_.tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - span->add_tag(core::tracing::attributes::op::service, core::tracing::service::management); - span->add_tag(core::tracing::attributes::op::bucket_name, bucket_name_); - if (scope_name.has_value()) { - span->add_tag(core::tracing::attributes::op::scope_name, scope_name.value()); - } - if (collection_name.has_value()) { - span->add_tag(core::tracing::attributes::op::collection_name, collection_name.value()); - } - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); + auto obs_rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); + obs_rec->with_service(core::tracing::service::management); + obs_rec->with_bucket_name(bucket_name_); + if (scope_name.has_value()) { + obs_rec->with_scope_name(scope_name.value()); + } + if (collection_name.has_value()) { + obs_rec->with_collection_name(collection_name.value()); } - return span; + return obs_rec; } core::cluster core_; diff --git a/core/impl/observability_recorder.cxx b/core/impl/observability_recorder.cxx new file mode 100644 index 000000000..3f936968a --- /dev/null +++ b/core/impl/observability_recorder.cxx @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2025. Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "observability_recorder.hxx" +#include "core/tracing/attribute_helpers.hxx" + +namespace couchbase::core::impl +{ +auto +observability_recorder::create(std::string op_name, + std::shared_ptr parent_span, + std::weak_ptr tracer, + std::weak_ptr meter) + -> std::unique_ptr +{ + auto rec = std::make_unique( + std::move(op_name), std::move(parent_span), std::move(tracer), std::move(meter)); + if (rec->span_->uses_tags()) { + rec->span_->add_tag(tracing::attributes::op::operation_name, rec->op_name_); + } + rec->metric_attributes_.operation = rec->op_name_; + return rec; +} + +auto +observability_recorder::operation_span() -> const std::shared_ptr& +{ + return span_; +} + +void +observability_recorder::finish(const std::error_code ec) +{ + metric_attributes_.ec = ec; + meter_.lock()->record_value(std::move(metric_attributes_), start_time_); + span_->end(); +} + +void +observability_recorder::finish(const std::size_t retry_attempts, const std::error_code ec) +{ + if (span_->uses_tags()) { + span_->add_tag(tracing::attributes::op::retry_count, retry_attempts); + } + finish(ec); +} + +auto +observability_recorder::create_request_encoding_span() const + -> std::shared_ptr +{ + return tracer_.lock()->create_span(tracing::operation::step_request_encoding, span_); +} + +auto +observability_recorder::record_suboperation(std::string subop_name) const + -> std::unique_ptr +{ + return create(std::move(subop_name), span_, tracer_, meter_); +} + +void +observability_recorder::with_service(const std::string& service) +{ + metric_attributes_.service = service; + if (span_->uses_tags()) { + span_->add_tag(tracing::attributes::op::service, service); + } +} + +void +observability_recorder::with_collection_name(const std::string& collection_name) + +{ + metric_attributes_.collection_name = collection_name; + if (span_->uses_tags()) { + span_->add_tag(tracing::attributes::op::collection_name, collection_name); + } +} + +void +observability_recorder::with_scope_name(const std::string& scope_name) +{ + metric_attributes_.scope_name = scope_name; + if (span_->uses_tags()) { + span_->add_tag(tracing::attributes::op::scope_name, scope_name); + } +} + +void +observability_recorder::with_bucket_name(const std::string& bucket_name) +{ + metric_attributes_.bucket_name = bucket_name; + if (span_->uses_tags()) { + span_->add_tag(tracing::attributes::op::bucket_name, bucket_name); + } +} + +void +observability_recorder::with_durability(const couchbase::durability_level durability) + +{ + if (span_->uses_tags()) { + tracing::set_durability_level_attribute(span_, durability); + } +} + +void +observability_recorder::with_query_statement(const std::string& statement, + const query_options::built& query_options) + +{ + if (query_options.positional_parameters.empty() && query_options.named_parameters.empty()) { + return; + } + if (span_->uses_tags()) { + span_->add_tag(tracing::attributes::op::query_statement, statement); + } +} + +void +observability_recorder::with_query_statement(const std::string& statement, + const analytics_options::built& analytics_options) + +{ + if (analytics_options.positional_parameters.empty() && + analytics_options.named_parameters.empty()) { + return; + } + if (span_->uses_tags()) { + span_->add_tag(tracing::attributes::op::query_statement, statement); + } +} + +observability_recorder::observability_recorder( + std::string op_name, + std::shared_ptr parent_span, + std::weak_ptr tracer, + std::weak_ptr meter) + : op_name_{ std::move(op_name) } + , tracer_{ std::move(tracer) } + , meter_{ std::move(meter) } + , span_{ tracer_.lock()->create_span(op_name_, std::move(parent_span)) } + , start_time_{ std::chrono::steady_clock::now() } +{ +} +} // namespace couchbase::core::impl diff --git a/core/impl/observability_recorder.hxx b/core/impl/observability_recorder.hxx new file mode 100644 index 000000000..736cbfe3c --- /dev/null +++ b/core/impl/observability_recorder.hxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2025. Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "core/metrics/meter_wrapper.hxx" +#include "core/tracing/constants.hxx" +#include "core/tracing/tracer_wrapper.hxx" + +#include +#include + +namespace couchbase::core::impl +{ +class observability_recorder +{ +public: + static auto create(std::string op_name, + std::shared_ptr parent_span, + std::weak_ptr tracer, + std::weak_ptr meter) + -> std::unique_ptr; + + [[nodiscard]] auto operation_span() -> const std::shared_ptr&; + + [[nodiscard]] auto create_request_encoding_span() const + -> std::shared_ptr; + [[nodiscard]] auto record_suboperation(std::string subop_name) const + -> std::unique_ptr; + + void with_service(const std::string& service); + void with_collection_name(const std::string& collection_name); + void with_scope_name(const std::string& scope_name); + void with_bucket_name(const std::string& bucket_name); + void with_durability(couchbase::durability_level durability); + void with_query_statement(const std::string& statement, + const query_options::built& query_options); + void with_query_statement(const std::string& statement, + const analytics_options::built& analytics_options); + + void finish(std::error_code ec); + void finish(std::size_t retry_attempts, std::error_code ec); + + observability_recorder(std::string op_name, + std::shared_ptr parent_span, + std::weak_ptr tracer, + std::weak_ptr meter); + +private: + std::string op_name_; + std::weak_ptr tracer_; + std::weak_ptr meter_; + std::shared_ptr span_; + std::chrono::time_point start_time_; + metrics::metric_attributes metric_attributes_{}; +}; +} // namespace couchbase::core::impl diff --git a/core/impl/public_bucket.cxx b/core/impl/public_bucket.cxx index 76a154188..37327b380 100644 --- a/core/impl/public_bucket.cxx +++ b/core/impl/public_bucket.cxx @@ -21,6 +21,7 @@ #include "core/tracing/constants.hxx" #include "core/tracing/tracer_wrapper.hxx" #include "diagnostics.hxx" +#include "observability_recorder.hxx" #include #include @@ -65,39 +66,35 @@ class bucket_impl : public std::enable_shared_from_this void ping(const ping_options::built& options, ping_handler&& handler) const { - auto span = create_span(core::tracing::operation::ping, std::nullopt, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::ping, std::nullopt, options.parent_span); return core_.ping( options.report_id, name_, core::impl::to_core_service_types(options.service_types), options.timeout, - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish({}); return handler({}, core::impl::build_result(resp)); }); } private: - [[nodiscard]] auto tracer() const -> const std::shared_ptr& + [[nodiscard]] auto create_observability_recorder( + const std::string& operation_name, + const std::optional service, + const std::shared_ptr& parent_span) const + -> std::unique_ptr { - return core_.tracer(); - } + auto rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); - [[nodiscard]] auto create_span(const std::string& operation_name, - const std::optional service, - const std::shared_ptr& parent_span) const - -> std::shared_ptr - { - auto span = tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - if (service.has_value()) { - span->add_tag(core::tracing::attributes::op::service, - core::tracing::service_name_for_http_service(service.value())); - } - span->add_tag(core::tracing::attributes::op::bucket_name, name_); - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); + rec->with_bucket_name(name_); + if (service.has_value()) { + rec->with_service(core::tracing::service_name_for_http_service(service.value())); } - return span; + + return rec; } core::cluster core_; diff --git a/core/impl/public_cluster.cxx b/core/impl/public_cluster.cxx index 2e8f09035..bb811dc25 100644 --- a/core/impl/public_cluster.cxx +++ b/core/impl/public_cluster.cxx @@ -33,6 +33,7 @@ #include "diagnostics.hxx" #include "error.hxx" #include "internal_search_result.hxx" +#include "observability_recorder.hxx" #include "query.hxx" #include "search.hxx" @@ -322,23 +323,16 @@ class cluster_impl : public std::enable_shared_from_this void query(std::string statement, query_options::built options, query_handler&& handler) const { - auto span = - create_span(core::tracing::operation::query, core::service_type::query, options.parent_span); - if (span->uses_tags() && - !(options.positional_parameters.empty() && options.named_parameters.empty())) { - span->add_tag(core::tracing::attributes::op::query_statement, statement); - } + auto obs_rec = create_observability_recorder( + core::tracing::operation::query, core::service_type::query, options.parent_span); + obs_rec->with_query_statement(statement, options); - auto request = - core::impl::build_query_request(std::move(statement), {}, std::move(options), span); + auto request = core::impl::build_query_request( + std::move(statement), {}, std::move(options), obs_rec->operation_span()); return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](auto resp) { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - + std::move(request), [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx), core::impl::build_result(resp)); }); } @@ -347,49 +341,43 @@ class cluster_impl : public std::enable_shared_from_this analytics_options::built options, analytics_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::analytics, core::service_type::analytics, options.parent_span); - if (span->uses_tags() && - !(options.positional_parameters.empty() && options.named_parameters.empty())) { - span->add_tag(core::tracing::attributes::op::query_statement, statement); - } + obs_rec->with_query_statement(statement, options); - auto request = - core::impl::build_analytics_request(std::move(statement), std::move(options), {}, {}, span); + auto request = core::impl::build_analytics_request( + std::move(statement), std::move(options), {}, {}, obs_rec->operation_span()); return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](auto resp) { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - + std::move(request), [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx), core::impl::build_result(resp)); }); } void ping(const ping_options::built& options, ping_handler&& handler) const { - auto span = create_span(core::tracing::operation::ping, std::nullopt, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::ping, std::nullopt, options.parent_span); return core_.ping( options.report_id, {}, core::impl::to_core_service_types(options.service_types), options.timeout, - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish({}); return handler({}, core::impl::build_result(resp)); }); }; void diagnostics(const diagnostics_options::built& options, diagnostics_handler&& handler) const { - auto span = - create_span(core::tracing::operation::diagnostics, std::nullopt, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::diagnostics, std::nullopt, options.parent_span); return core_.diagnostics( options.report_id, - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish({}); return handler({}, core::impl::build_result(resp)); }); } @@ -399,18 +387,15 @@ class cluster_impl : public std::enable_shared_from_this const search_options::built& options, search_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::search, core::service_type::search, options.parent_span); auto core_req = core::impl::build_search_request( - std::move(index_name), std::move(request), options, {}, {}, span); + std::move(index_name), std::move(request), options, {}, {}, obs_rec->operation_span()); return core_.execute( std::move(core_req), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx), search_result{ internal_search_result{ resp } }); }); @@ -482,25 +467,18 @@ class cluster_impl : public std::enable_shared_from_this } } - [[nodiscard]] auto tracer() const -> const std::shared_ptr& + [[nodiscard]] auto create_observability_recorder( + const std::string& operation_name, + const std::optional service, + const std::shared_ptr& parent_span) const + -> std::unique_ptr { - return core_.tracer(); - } - - [[nodiscard]] auto create_span(const std::string& operation_name, - const std::optional service, - const std::shared_ptr& parent_span) const - -> std::shared_ptr - { - auto span = tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - if (service.has_value()) { - span->add_tag(core::tracing::attributes::op::service, - core::tracing::service_name_for_http_service(service.value())); - } - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); + auto rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); + if (service.has_value()) { + rec->with_service(core::tracing::service_name_for_http_service(service.value())); } - return span; + return rec; } std::string connection_string_; diff --git a/core/impl/query_index_manager.cxx b/core/impl/query_index_manager.cxx index 04289104e..7005beed3 100644 --- a/core/impl/query_index_manager.cxx +++ b/core/impl/query_index_manager.cxx @@ -18,6 +18,7 @@ #include "core/cluster.hxx" #include "core/impl/error.hxx" +#include "core/impl/observability_recorder.hxx" #include "core/logger/logger.hxx" #include "core/operations/management/query_index_build_deferred.hxx" #include "core/operations/management/query_index_create.hxx" @@ -58,7 +59,9 @@ class watch_context : public std::enable_shared_from_this , scope_name_(std::move(scope_name)) , collection_name_(std::move(collection_name)) , handler_(std::move(handler)) - , span_(create_span(core::tracing::operation::mgr_query_watch_indexes, options_.parent_span)) + , observability_recorder_( + create_observability_recorder(core::tracing::operation::mgr_query_watch_indexes, + options_.parent_span)) { } @@ -70,7 +73,7 @@ class watch_context : public std::enable_shared_from_this , scope_name_(std::move(other.scope_name_)) , collection_name_(std::move(other.collection_name_)) , handler_(std::move(other.handler_)) - , span_(std::move(other.span_)) + , observability_recorder_(std::move(other.observability_recorder_)) , timer_(std::move(other.timer_)) , start_time_(other.start_time_) , timeout_(other.timeout_) @@ -85,17 +88,21 @@ class watch_context : public std::enable_shared_from_this void execute() { - auto get_all_span = create_span(core::tracing::operation::mgr_query_get_all_indexes, span_); + auto get_all_obs_rec = create_suboperation_observability_recorder( + core::tracing::operation::mgr_query_get_all_indexes, observability_recorder_); core::operations::management::query_index_get_all_request get_all_request{ - bucket_name_, scope_name_, collection_name_, {}, {}, remaining(), get_all_span, + bucket_name_, + scope_name_, + collection_name_, + {}, + {}, + remaining(), + get_all_obs_rec->operation_span(), }; return core_.execute( std::move(get_all_request), - [ctx = shared_from_this(), get_all_span = std::move(get_all_span)](auto resp) { - if (auto retries = resp.ctx.retry_attempts; get_all_span->uses_tags() && retries > 0) { - get_all_span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - get_all_span->end(); + [ctx = shared_from_this(), get_all_obs_rec = std::move(get_all_obs_rec)](auto resp) { + get_all_obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); if (ctx->check(resp)) { ctx->finish(resp, {}); } else if (ctx->remaining().count() <= 0) { @@ -116,7 +123,7 @@ class watch_context : public std::enable_shared_from_this if (ec.has_value()) { resp.ctx.ec = ec.value(); } - span_->end(); + observability_recorder_->finish(resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); timer_.cancel(); } @@ -166,21 +173,35 @@ class watch_context : public std::enable_shared_from_this }); } - [[nodiscard]] auto create_span(const std::string& operation_name, - const std::shared_ptr& parent_span) const - -> std::shared_ptr + [[nodiscard]] auto create_observability_recorder( + const std::string& operation_name, + const std::shared_ptr& parent_span) const + -> std::unique_ptr { - auto span = core_.tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - span->add_tag(core::tracing::attributes::op::service, core::tracing::service::query); - span->add_tag(core::tracing::attributes::op::bucket_name, bucket_name_); - if (!collection_name_.empty()) { - span->add_tag(core::tracing::attributes::op::scope_name, scope_name_); - span->add_tag(core::tracing::attributes::op::collection_name, collection_name_); - } - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); + auto rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); + rec->with_service(core::tracing::service::query); + rec->with_bucket_name(bucket_name_); + if (!collection_name_.empty()) { + rec->with_scope_name(scope_name_); + rec->with_collection_name(collection_name_); + } + return rec; + } + + [[nodiscard]] auto create_suboperation_observability_recorder( + const std::string& subop_name, + const std::unique_ptr& parent_recorder) const + -> std::unique_ptr + { + auto rec = parent_recorder->record_suboperation(subop_name); + rec->with_service(core::tracing::service::query); + rec->with_bucket_name(bucket_name_); + if (!collection_name_.empty()) { + rec->with_scope_name(scope_name_); + rec->with_collection_name(collection_name_); } - return span; + return rec; } couchbase::core::cluster core_; @@ -190,7 +211,7 @@ class watch_context : public std::enable_shared_from_this std::string scope_name_; std::string collection_name_; watch_query_indexes_handler handler_; - std::shared_ptr span_; + std::unique_ptr observability_recorder_; asio::steady_timer timer_{ core_.io_context() }; std::chrono::steady_clock::time_point start_time_{ std::chrono::steady_clock::now() }; std::chrono::milliseconds timeout_{ options_.timeout.value_or( @@ -214,23 +235,20 @@ class query_index_manager_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)]( + [obs_rec = std::move(obs_rec), handler = std::move(handler)]( const core::operations::management::query_index_get_all_response& resp) { - if (const auto retries = resp.ctx.retry_attempts; - span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); if (resp.ctx.ec) { return handler(core::impl::make_error(resp.ctx), {}); } @@ -246,11 +264,11 @@ class query_index_manager_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -284,11 +300,12 @@ class query_index_manager_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -323,11 +338,11 @@ class query_index_manager_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](const auto& resp) { - if (const auto retries = resp.ctx.retry_attempts; resp.ctx.ec && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -357,11 +370,12 @@ class query_index_manager_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)]( + [obs_rec = std::move(obs_rec), handler = std::move(handler)]( const core::operations::management::query_index_drop_response& resp) { - if (const auto retries = resp.ctx.retry_attempts; - resp.ctx.ec && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); handler(core::impl::make_error(resp.ctx)); }); } @@ -395,21 +405,28 @@ class query_index_manager_impl : public std::enable_shared_from_thisoperation_span(), }; return core_.execute( @@ -419,39 +436,41 @@ class query_index_manager_impl : public std::enable_shared_from_thisuses_tags() && retries > 0) { - get_all_deferred_span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - get_all_deferred_span->end(); + get_all_deferred_obs_rec->finish(list_resp.ctx.retry_attempts, list_resp.ctx.ec); if (list_resp.ctx.ec) { - top_span->end(); + top_obs_rec->finish(list_resp.ctx.ec); return handler(core::impl::make_error(list_resp.ctx)); } if (list_resp.index_names.empty()) { - top_span->end(); + top_obs_rec->finish(list_resp.ctx.ec); return handler(core::impl::make_error(list_resp.ctx)); } - auto build_span = self->create_span( - core::tracing::operation::mgr_query_build_indexes, bucket, scope, collection, top_span); + auto build_obs_rec = self->create_suboperation_observability_recorder( + core::tracing::operation::mgr_query_build_indexes, + bucket, + scope, + collection, + top_obs_rec); core::operations::management::query_index_build_request build_request{ - std::move(bucket), scope, collection, {}, std::move(list_resp.index_names), {}, - timeout, build_span, + std::move(bucket), + scope, + collection, + {}, + std::move(list_resp.index_names), + {}, + timeout, + build_obs_rec->operation_span(), }; self->core_.execute(std::move(build_request), - [top_span = std::move(top_span), - build_span = std::move(build_span), + [top_obs_rec = std::move(top_obs_rec), + build_obs_rec = std::move(build_obs_rec), handler = std::move(handler)](const auto& build_resp) { - if (const auto retries = build_resp.ctx.retry_attempts; - build_span->uses_tags() && retries > 0) { - build_span->add_tag(core::tracing::attributes::op::retry_count, - retries); - } - build_span->end(); - top_span->end(); + build_obs_rec->finish(build_resp.ctx.retry_attempts, + build_resp.ctx.ec); + top_obs_rec->finish(build_resp.ctx.ec); return handler(core::impl::make_error(build_resp.ctx)); }); }); @@ -475,24 +494,41 @@ class query_index_manager_impl : public std::enable_shared_from_this& parent_span) const - -> std::shared_ptr + [[nodiscard]] auto create_observability_recorder( + const std::string& operation_name, + const std::string& bucket_name, + const std::string& scope_name, + const std::string& collection_name, + const std::shared_ptr& parent_span) const + -> std::unique_ptr { - auto span = core_.tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - span->add_tag(core::tracing::attributes::op::service, core::tracing::service::query); - span->add_tag(core::tracing::attributes::op::bucket_name, bucket_name); - if (!collection_name.empty()) { - span->add_tag(core::tracing::attributes::op::scope_name, scope_name); - span->add_tag(core::tracing::attributes::op::collection_name, collection_name); - } - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); + auto rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); + rec->with_service(core::tracing::service::query); + rec->with_bucket_name(bucket_name); + if (!collection_name.empty()) { + rec->with_scope_name(scope_name); + rec->with_collection_name(collection_name); + } + return rec; + } + + [[nodiscard]] auto create_suboperation_observability_recorder( + const std::string& subop_name, + const std::string& bucket_name, + const std::string& scope_name, + const std::string& collection_name, + const std::unique_ptr& parent_recorder) const + -> std::unique_ptr + { + auto rec = parent_recorder->record_suboperation(subop_name); + rec->with_service(core::tracing::service::query); + rec->with_bucket_name(bucket_name); + if (!collection_name.empty()) { + rec->with_scope_name(scope_name); + rec->with_collection_name(collection_name); } - return span; + return rec; } core::cluster core_; diff --git a/core/impl/scope.cxx b/core/impl/scope.cxx index e97081bf1..f8ca3e142 100644 --- a/core/impl/scope.cxx +++ b/core/impl/scope.cxx @@ -26,6 +26,7 @@ #include "internal_search_row.hxx" #include "internal_search_row_location.hxx" #include "internal_search_row_locations.hxx" +#include "observability_recorder.hxx" #include "query.hxx" #include "search.hxx" @@ -77,23 +78,16 @@ class scope_impl void query(std::string statement, query_options::built options, query_handler&& handler) const { - auto span = - create_span(core::tracing::operation::query, core::service_type::query, options.parent_span); - if (span->uses_tags() && - !(options.positional_parameters.empty() && options.named_parameters.empty())) { - span->add_tag(core::tracing::attributes::op::query_statement, statement); - } + auto obs_rec = create_observability_recorder( + core::tracing::operation::query, core::service_type::query, options.parent_span); + obs_rec->with_query_statement(statement, options); auto request = core::impl::build_query_request( - std::move(statement), query_context_, std::move(options), span); + std::move(statement), query_context_, std::move(options), obs_rec->operation_span()); return core_.execute( - std::move(request), [span = std::move(span), handler = std::move(handler)](auto resp) { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - + std::move(request), [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx), core::impl::build_result(resp)); }); } @@ -102,27 +96,19 @@ class scope_impl analytics_options::built options, analytics_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::analytics, core::service_type::analytics, options.parent_span); - if (span->uses_tags() && - !(options.positional_parameters.empty() && options.named_parameters.empty())) { - span->add_tag(core::tracing::attributes::op::query_statement, statement); - } + obs_rec->with_query_statement(statement, options); auto request = core::impl::build_analytics_request( - std::move(statement), std::move(options), bucket_name_, name_, span); - - return core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](auto resp) mutable { - if (auto retries = resp.ctx.retry_attempts; - span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - - return handler(core::impl::make_error(resp.ctx), - core::impl::build_result(resp)); - }); + std::move(statement), std::move(options), bucket_name_, name_, obs_rec->operation_span()); + + return core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx), core::impl::build_result(resp)); + }); } void search(std::string index_name, @@ -130,43 +116,39 @@ class scope_impl search_options::built options, search_handler&& handler) const { - auto span = create_span( + auto obs_rec = create_observability_recorder( core::tracing::operation::search, core::service_type::search, options.parent_span); - auto core_req = core::impl::build_search_request( - std::move(index_name), std::move(request), std::move(options), bucket_name_, name_, span); + auto core_req = core::impl::build_search_request(std::move(index_name), + std::move(request), + std::move(options), + bucket_name_, + name_, + obs_rec->operation_span()); return core_.execute( std::move(core_req), - [span = std::move(span), handler = std::move(handler)](auto&& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); + [obs_rec = std::move(obs_rec), handler = std::move(handler)](auto&& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); return handler(core::impl::make_error(resp.ctx), search_result{ internal_search_result{ resp } }); }); } private: - [[nodiscard]] auto tracer() const -> const std::shared_ptr& + [[nodiscard]] auto create_observability_recorder( + const std::string& operation_name, + const core::service_type service, + const std::shared_ptr& parent_span) const + -> std::unique_ptr { - return core_.tracer(); - } + auto rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); - [[nodiscard]] auto create_span(const std::string& operation_name, - const core::service_type service, - const std::shared_ptr& parent_span) const - -> std::shared_ptr - { - auto span = tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - span->add_tag(core::tracing::attributes::op::service, - core::tracing::service_name_for_http_service(service)); - span->add_tag(core::tracing::attributes::op::bucket_name, bucket_name_); - span->add_tag(core::tracing::attributes::op::scope_name, name_); - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); - } - return span; + rec->with_service(core::tracing::service_name_for_http_service(service)); + rec->with_bucket_name(bucket_name_); + rec->with_scope_name(name_); + + return rec; } core::cluster core_; diff --git a/core/impl/search_index_manager.cxx b/core/impl/search_index_manager.cxx index e26c231f8..a77b8a80b 100644 --- a/core/impl/search_index_manager.cxx +++ b/core/impl/search_index_manager.cxx @@ -18,6 +18,7 @@ #include "core/cluster.hxx" #include "core/impl/error.hxx" +#include "core/impl/observability_recorder.hxx" #include "core/operations/management/search_index_analyze_document.hxx" #include "core/operations/management/search_index_control_ingest.hxx" #include "core/operations/management/search_index_control_plan_freeze.hxx" @@ -149,205 +150,197 @@ class search_index_manager_impl const couchbase::get_search_index_options::built& options, get_search_index_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_search_get_index, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mgr_search_get_index, + options.parent_span); core::operations::management::search_index_get_request request{ - std::move(index_name), bucket_name_, scope_name_, {}, options.timeout, span, + std::move(index_name), bucket_name_, scope_name_, {}, options.timeout, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx), map_search_index(resp.index)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx), map_search_index(resp.index)); + }); } void get_all_indexes(const get_all_search_indexes_options::built& options, get_all_search_indexes_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_search_get_all_indexes, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_search_get_all_indexes, options.parent_span); core::operations::management::search_index_get_all_request request{ - bucket_name_, scope_name_, {}, options.timeout, span, + bucket_name_, scope_name_, {}, options.timeout, obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx), - map_all_search_indexes(resp.indexes)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx), map_all_search_indexes(resp.indexes)); + }); } void upsert_index(const management::search::index& search_index, const upsert_search_index_options::built& options, upsert_search_index_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_search_upsert_index, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mgr_search_upsert_index, + options.parent_span); core::operations::management::search_index_upsert_request request{ - map_search_index(search_index), bucket_name_, scope_name_, {}, options.timeout, span, + map_search_index(search_index), bucket_name_, scope_name_, {}, options.timeout, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void drop_index(std::string index_name, const drop_search_index_options::built& options, drop_search_index_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_search_drop_index, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mgr_search_drop_index, + options.parent_span); core::operations::management::search_index_drop_request request{ - std::move(index_name), bucket_name_, scope_name_, {}, options.timeout, span, + std::move(index_name), bucket_name_, scope_name_, {}, options.timeout, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void get_indexed_documents_count(std::string index_name, const get_indexed_search_index_options::built& options, get_indexed_search_index_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_search_get_indexed_documents_count, - options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_search_get_indexed_documents_count, options.parent_span); core::operations::management::search_index_get_documents_count_request request{ - std::move(index_name), bucket_name_, scope_name_, {}, options.timeout, span, + std::move(index_name), bucket_name_, scope_name_, {}, options.timeout, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx), resp.count); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx), resp.count); + }); } void pause_ingest(std::string index_name, const pause_ingest_search_index_options::built& options, pause_ingest_search_index_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_search_pause_ingest, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mgr_search_pause_ingest, + options.parent_span); core::operations::management::search_index_control_ingest_request request{ - std::move(index_name), true, bucket_name_, scope_name_, {}, options.timeout, span, + std::move(index_name), true, bucket_name_, scope_name_, {}, options.timeout, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void resume_ingest(std::string index_name, const resume_ingest_search_index_options::built& options, resume_ingest_search_index_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_search_resume_ingest, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mgr_search_resume_ingest, + options.parent_span); core::operations::management::search_index_control_ingest_request request{ - std::move(index_name), false, bucket_name_, scope_name_, {}, options.timeout, span, + std::move(index_name), false, bucket_name_, scope_name_, {}, options.timeout, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void allow_querying(std::string index_name, const allow_querying_search_index_options::built& options, allow_querying_search_index_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_search_allow_querying, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_search_allow_querying, options.parent_span); core::operations::management::search_index_control_query_request request{ - std::move(index_name), true, bucket_name_, scope_name_, {}, options.timeout, span, + std::move(index_name), true, bucket_name_, scope_name_, {}, options.timeout, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void disallow_querying(std::string index_name, const disallow_querying_search_index_options::built& options, disallow_querying_search_index_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_search_disallow_querying, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_search_disallow_querying, options.parent_span); core::operations::management::search_index_control_query_request request{ - std::move(index_name), false, bucket_name_, scope_name_, {}, options.timeout, span, + std::move(index_name), false, bucket_name_, scope_name_, {}, options.timeout, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void freeze_plan(std::string index_name, const freeze_plan_search_index_options::built& options, freeze_plan_search_index_handler&& handler) const { - auto span = create_span(core::tracing::operation::mgr_search_freeze_plan, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mgr_search_freeze_plan, + options.parent_span); core::operations::management::search_index_control_plan_freeze_request request{ - std::move(index_name), true, bucket_name_, scope_name_, {}, options.timeout, span, + std::move(index_name), true, bucket_name_, scope_name_, {}, options.timeout, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void unfreeze_plan(std::string index_name, const unfreeze_plan_search_index_options::built& options, unfreeze_plan_search_index_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_search_unfreeze_plan, options.parent_span); + auto obs_rec = create_observability_recorder(core::tracing::operation::mgr_search_unfreeze_plan, + options.parent_span); core::operations::management::search_index_control_plan_freeze_request request{ - std::move(index_name), false, bucket_name_, scope_name_, {}, options.timeout, span, + std::move(index_name), false, bucket_name_, scope_name_, {}, options.timeout, + obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - span->end(); - return handler(core::impl::make_error(resp.ctx)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx)); + }); } void analyze_document(std::string index_name, @@ -355,45 +348,38 @@ class search_index_manager_impl const analyze_document_options::built& options, analyze_document_handler&& handler) const { - auto span = - create_span(core::tracing::operation::mgr_search_analyze_document, options.parent_span); + auto obs_rec = create_observability_recorder( + core::tracing::operation::mgr_search_analyze_document, options.parent_span); core::operations::management::search_index_analyze_document_request request{ - std::move(index_name), - std::move(document), - bucket_name_, - scope_name_, - {}, - options.timeout, - span, + std::move(index_name), std::move(document), bucket_name_, scope_name_, {}, + options.timeout, obs_rec->operation_span(), }; - core_.execute(std::move(request), - [span = std::move(span), handler = std::move(handler)](const auto& resp) mutable { - if (auto retries = resp.ctx.retry_attempts; span->uses_tags() && retries > 0) { - span->add_tag(core::tracing::attributes::op::retry_count, retries); - } - auto analysis = convert_analysis(resp.analysis); - span->end(); - return handler(core::impl::make_error(resp.ctx), std::move(analysis)); - }); + core_.execute( + std::move(request), + [obs_rec = std::move(obs_rec), handler = std::move(handler)](const auto& resp) mutable { + auto analysis = convert_analysis(resp.analysis); + obs_rec->finish(resp.ctx.retry_attempts, resp.ctx.ec); + return handler(core::impl::make_error(resp.ctx), std::move(analysis)); + }); } private: - [[nodiscard]] auto create_span(const std::string& operation_name, - const std::shared_ptr& parent_span) const - -> std::shared_ptr + [[nodiscard]] auto create_observability_recorder( + const std::string& operation_name, + const std::shared_ptr& parent_span) const + -> std::unique_ptr { - auto span = core_.tracer()->create_span(operation_name, parent_span); - if (span->uses_tags()) { - span->add_tag(core::tracing::attributes::op::service, core::tracing::service::search); - span->add_tag(core::tracing::attributes::op::operation_name, operation_name); - if (bucket_name_.has_value()) { - span->add_tag(core::tracing::attributes::op::bucket_name, bucket_name_.value()); - } - if (scope_name_.has_value()) { - span->add_tag(core::tracing::attributes::op::scope_name, scope_name_.value()); - } + auto rec = core::impl::observability_recorder::create( + operation_name, parent_span, core_.tracer(), core_.meter()); + + rec->with_service(core::tracing::service::search); + if (bucket_name_.has_value()) { + rec->with_bucket_name(bucket_name_.value()); + } + if (scope_name_.has_value()) { + rec->with_scope_name(scope_name_.value()); } - return span; + return rec; } core::cluster core_; diff --git a/core/io/http_command.hxx b/core/io/http_command.hxx index de3b938b8..1835bc980 100644 --- a/core/io/http_command.hxx +++ b/core/io/http_command.hxx @@ -288,6 +288,7 @@ private: ->record_latency(latency_for_service_type(self->request.type), latency); } +#ifdef COUCHBASE_CXX_CLIENT_CREATE_OPERATION_SPAN_IN_CORE if (self->meter_) { metrics::metric_attributes attrs{ self->request.type, @@ -296,6 +297,7 @@ private: }; self->meter_->record_value(std::move(attrs), start); } +#endif self->deadline.cancel(); CB_LOG_TRACE(R"({} HTTP response: {}, client_context_id="{}", ec={}, status={}, body={})", diff --git a/core/io/mcbp_command.hxx b/core/io/mcbp_command.hxx index e67f66a5b..afc561320 100644 --- a/core/io/mcbp_command.hxx +++ b/core/io/mcbp_command.hxx @@ -325,6 +325,7 @@ struct mcbp_command : public std::enable_shared_from_thisrecord_latency(category, latency); } +#ifdef COUCHBASE_CXX_CLIENT_CREATE_OPERATION_SPAN_IN_CORE { metrics::metric_attributes attrs{ service_type::key_value, @@ -336,6 +337,7 @@ struct mcbp_command : public std::enable_shared_from_thismanager_->meter()->record_value(std::move(attrs), start); } +#endif self->retry_backoff.cancel(); if (ec == asio::error::operation_aborted) { diff --git a/core/metrics/constants.hxx b/core/metrics/constants.hxx new file mode 100644 index 000000000..aaafd77cb --- /dev/null +++ b/core/metrics/constants.hxx @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2025-Present Couchbase, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace couchbase::core::metrics +{ +constexpr auto operation_meter_name = "db.client.operation.duration"; +} // namespace couchbase::core::metrics diff --git a/core/metrics/logging_meter.cxx b/core/metrics/logging_meter.cxx index 4b32e5fea..9d5c73087 100644 --- a/core/metrics/logging_meter.cxx +++ b/core/metrics/logging_meter.cxx @@ -19,6 +19,7 @@ #include "couchbase/build_info.hxx" +#include "constants.hxx" #include "core/logger/logger.hxx" #include "core/tracing/constants.hxx" #include "core/utils/json.hxx" @@ -210,7 +211,7 @@ logging_meter::get_value_recorder(const std::string& name, std::make_shared() }; - if (static const std::string meter_name = "db.couchbase.operations"; name != meter_name) { + if (name != operation_meter_name) { return noop_recorder; } diff --git a/core/metrics/meter_wrapper.cxx b/core/metrics/meter_wrapper.cxx index 9d73f4064..27d277b55 100644 --- a/core/metrics/meter_wrapper.cxx +++ b/core/metrics/meter_wrapper.cxx @@ -19,6 +19,7 @@ #include +#include "core/metrics/constants.hxx" #include "core/tracing/constants.hxx" #include @@ -59,32 +60,10 @@ extract_error_name(std::error_code ec) -> std::string } auto -service_to_string(service_type s) -> std::string -{ - switch (s) { - case service_type::analytics: - return "analytics"; - case service_type::search: - return "search"; - case service_type::key_value: - return "kv"; - case service_type::management: - return "management"; - case service_type::eventing: - return "eventing"; - case service_type::query: - return "query"; - case service_type::view: - return "views"; - } - return {}; -} - -auto -get_standardized_outcome(std::error_code ec) -> std::string +get_standardized_error_type(std::error_code ec) -> std::string { if (!ec) { - return "Success"; + return {}; } // SDK-specific errors @@ -97,7 +76,23 @@ get_standardized_outcome(std::error_code ec) -> std::string return "CryptoError"; } - return snake_case_to_camel_case(extract_error_name(ec)); + static const std::array cb_categories = { + &impl::common_category(), &impl::key_value_category(), + &impl::query_category(), &impl::analytics_category(), + &impl::search_category(), &impl::view_category(), + &impl::management_category(), &impl::field_level_encryption_category(), + &impl::network_category(), &impl::streaming_json_lexer_category(), + &impl::transaction_category(), &impl::transaction_op_category(), + }; + + if (std::any_of( + cb_categories.begin(), cb_categories.end(), [&ec](const std::error_category* cat) { + return &ec.category() == cat; + })) { + return snake_case_to_camel_case(extract_error_name(ec)); + } + + return "_OTHER"; } } // namespace @@ -105,9 +100,9 @@ auto metric_attributes::encode() const -> std::map { std::map tags = { - { tracing::attributes::op::service, service_to_string(service) }, + { tracing::attributes::common::system, "couchbase" }, + { tracing::attributes::op::service, service }, { tracing::attributes::op::operation_name, operation }, - { "outcome", get_standardized_outcome(ec) } }; if (internal.cluster_name.has_value()) { @@ -125,6 +120,9 @@ metric_attributes::encode() const -> std::map if (collection_name) { tags.emplace(tracing::attributes::op::collection_name, collection_name.value()); } + if (ec) { + tags.emplace(tracing::attributes::op::error_type, get_standardized_error_type(ec)); + } return tags; } @@ -150,8 +148,6 @@ void meter_wrapper::record_value(metric_attributes attrs, std::chrono::steady_clock::time_point start_time) { - static const std::string meter_name{ "db.couchbase.operations" }; - { const std::shared_lock lock{ cluster_labels_mutex_ }; @@ -163,8 +159,7 @@ meter_wrapper::record_value(metric_attributes attrs, } } - auto tags = attrs.encode(); - meter_->get_value_recorder(meter_name, tags) + meter_->get_value_recorder(operation_meter_name, attrs.encode()) ->record_value(std::chrono::duration_cast( std::chrono::steady_clock::now() - start_time) .count()); diff --git a/core/metrics/meter_wrapper.hxx b/core/metrics/meter_wrapper.hxx index 85ca114c8..9553e7d0b 100644 --- a/core/metrics/meter_wrapper.hxx +++ b/core/metrics/meter_wrapper.hxx @@ -33,7 +33,7 @@ namespace couchbase::core::metrics { struct metric_attributes { - couchbase::core::service_type service; + std::string service; std::string operation; std::error_code ec; std::optional bucket_name{}; diff --git a/core/operations/document_get_all_replicas.hxx b/core/operations/document_get_all_replicas.hxx index 065f55f27..2971fabba 100644 --- a/core/operations/document_get_all_replicas.hxx +++ b/core/operations/document_get_all_replicas.hxx @@ -137,7 +137,7 @@ struct get_all_replicas_request { }, [ctx, subop_span](auto&& resp) { { - if (subop_span->uses_tags() && resp.ctx.retry_attempts() > 0) { + if (subop_span->uses_tags()) { subop_span->add_tag(tracing::attributes::op::retry_count, resp.ctx.retry_attempts()); } @@ -184,7 +184,7 @@ struct get_all_replicas_request { }, [ctx, subop_span](auto&& resp) { { - if (subop_span->uses_tags() && resp.ctx.retry_attempts() > 0) { + if (subop_span->uses_tags()) { subop_span->add_tag(tracing::attributes::op::retry_count, resp.ctx.retry_attempts()); } diff --git a/core/operations/document_get_any_replica.hxx b/core/operations/document_get_any_replica.hxx index 7e5d7a369..4499768a0 100644 --- a/core/operations/document_get_any_replica.hxx +++ b/core/operations/document_get_any_replica.hxx @@ -151,7 +151,7 @@ struct get_any_replica_request { core->execute( std::move(req), [ctx, subop_span](auto&& resp) { { - if (subop_span->uses_tags() && resp.ctx.retry_attempts() > 0) { + if (subop_span->uses_tags()) { subop_span->add_tag(tracing::attributes::op::retry_count, resp.ctx.retry_attempts()); } @@ -199,7 +199,7 @@ struct get_any_replica_request { ctx->add_cancellation_token(req.cancel_token); core->execute(std::move(req), [ctx, subop_span](auto&& resp) { { - if (subop_span->uses_tags() && resp.ctx.retry_attempts() > 0) { + if (subop_span->uses_tags()) { subop_span->add_tag(tracing::attributes::op::retry_count, resp.ctx.retry_attempts()); } diff --git a/core/operations/document_lookup_in_all_replicas.hxx b/core/operations/document_lookup_in_all_replicas.hxx index 589034ac1..662dae70b 100644 --- a/core/operations/document_lookup_in_all_replicas.hxx +++ b/core/operations/document_lookup_in_all_replicas.hxx @@ -180,7 +180,7 @@ struct lookup_in_all_replicas_request { replica_req.access_deleted = access_deleted; core->execute(replica_req, [ctx, subop_span](auto&& resp) { { - if (subop_span->uses_tags() && resp.ctx.retry_attempts() > 0) { + if (subop_span->uses_tags()) { subop_span->add_tag(tracing::attributes::op::retry_count, resp.ctx.retry_attempts()); } @@ -244,7 +244,7 @@ struct lookup_in_all_replicas_request { }, [ctx, subop_span](auto&& resp) { { - if (subop_span->uses_tags() && resp.ctx.retry_attempts() > 0) { + if (subop_span->uses_tags()) { subop_span->add_tag(tracing::attributes::op::retry_count, resp.ctx.retry_attempts()); } diff --git a/core/operations/document_lookup_in_any_replica.hxx b/core/operations/document_lookup_in_any_replica.hxx index 994b3f868..217361924 100644 --- a/core/operations/document_lookup_in_any_replica.hxx +++ b/core/operations/document_lookup_in_any_replica.hxx @@ -198,7 +198,7 @@ struct lookup_in_any_replica_request { replica_req.access_deleted = access_deleted; core->execute(replica_req, [ctx, subop_span](auto&& resp) { { - if (subop_span->uses_tags() && resp.ctx.retry_attempts() > 0) { + if (subop_span->uses_tags()) { subop_span->add_tag(tracing::attributes::op::retry_count, resp.ctx.retry_attempts()); } @@ -263,7 +263,7 @@ struct lookup_in_any_replica_request { ctx->add_cancellation_token(req.cancel_token); core->execute(std::move(req), [subop_span, ctx](auto&& resp) { { - if (subop_span->uses_tags() && resp.ctx.retry_attempts() > 0) { + if (subop_span->uses_tags()) { subop_span->add_tag(tracing::attributes::op::retry_count, resp.ctx.retry_attempts()); } diff --git a/core/tracing/constants.hxx b/core/tracing/constants.hxx index 884c3af79..864746650 100644 --- a/core/tracing/constants.hxx +++ b/core/tracing/constants.hxx @@ -37,15 +37,6 @@ constexpr auto views = "views"; constexpr auto manager = "manager"; constexpr auto eventing = "eventing"; -// TODO(DC): Remove these -constexpr auto manager_analytics = "manager_analytics"; -constexpr auto manager_query = "manager_query"; -constexpr auto manager_buckets = "manager_buckets"; -constexpr auto manager_collections = "manager_collections"; -constexpr auto manager_search = "manager_search"; -constexpr auto manager_users = "manager_users"; -constexpr auto manager_views = "manager_views"; - // KV operations constexpr auto mcbp_get = "get"; constexpr auto mcbp_get_replica = "get_replica"; @@ -160,6 +151,7 @@ constexpr auto scope_name = "couchbase.scope.name"; constexpr auto collection_name = "couchbase.collection.name"; constexpr auto query_statement = "db.query.text"; constexpr auto operation_name = "db.operation.name"; +constexpr auto error_type = "error.type"; } // namespace op // Dispatch-level attributes diff --git a/test/test_integration_meter.cxx b/test/test_integration_meter.cxx index 77ea97ccf..aa06fb0b8 100644 --- a/test/test_integration_meter.cxx +++ b/test/test_integration_meter.cxx @@ -17,21 +17,7 @@ #include "test_helper_integration.hxx" -#include "core/operations/document_append.hxx" -#include "core/operations/document_decrement.hxx" -#include "core/operations/document_get.hxx" -#include "core/operations/document_increment.hxx" -#include "core/operations/document_insert.hxx" -#include "core/operations/document_mutate_in.hxx" -#include "core/operations/document_prepend.hxx" -#include "core/operations/document_query.hxx" -#include "core/operations/document_remove.hxx" -#include "core/operations/document_replace.hxx" -#include "core/operations/document_upsert.hxx" -#include "core/operations/management/bucket_get.hxx" -#include "core/operations/management/scope_get_all.hxx" -#include "core/platform/uuid.h" - +#include #include #include "core/logger/logger.hxx" @@ -121,8 +107,7 @@ void assert_kv_recorder_tags(test::utils::integration_test_guard& guard, std::list> recorders, const std::string& op, - const couchbase::core::document_id& id, - const std::string& expected_outcome = "Success") + const std::optional& expected_outcome = {}) { // you'd expect one of these (only one) to have a matching op REQUIRE(recorders.size() == 1); @@ -131,10 +116,14 @@ assert_kv_recorder_tags(test::utils::integration_test_guard& guard, REQUIRE(tags.at("couchbase.service") == "kv"); REQUIRE(tags.at("db.operation.name") == op); - REQUIRE(tags.at("outcome") == expected_outcome); - REQUIRE(tags.at("db.namespace") == id.bucket()); - REQUIRE(tags.at("couchbase.scope.name") == id.scope()); - REQUIRE(tags.at("couchbase.collection.name") == id.collection()); + if (expected_outcome.has_value()) { + REQUIRE(tags.at("error.type") == expected_outcome.value()); + } else { + REQUIRE(tags.find("error.type") == tags.end()); + } + REQUIRE(tags.at("db.namespace") == guard.ctx.bucket); + REQUIRE(tags.at("couchbase.scope.name") == "_default"); + REQUIRE(tags.at("couchbase.collection.name") == "_default"); if (guard.cluster_version().supports_cluster_labels()) { REQUIRE_FALSE(tags.at("couchbase.cluster.name").empty()); @@ -150,7 +139,10 @@ assert_http_recorder_tags(test::utils::integration_test_guard& guard, std::list> recorders, const std::string& op, const std::string& service, - [[maybe_unused]] const std::string& expected_outcome = "Success") + const std::optional& expected_outcome = {}, + const std::optional& bucket_name = {}, + const std::optional& scope_name = {}, + const std::optional& collection_name = {}) { REQUIRE(recorders.size() == 1); @@ -158,9 +150,26 @@ assert_http_recorder_tags(test::utils::integration_test_guard& guard, REQUIRE(tags.at("couchbase.service") == service); REQUIRE(tags.at("db.operation.name") == op); - // TODO(CXXCBC-630): Enable assertion once bug recording all HTTP operations as 'Success' is - // resolved. REQUIRE(tags.at("outcome") == expected_outcome); - + if (expected_outcome.has_value()) { + REQUIRE(tags.at("error.type") == expected_outcome.value()); + } else { + REQUIRE(tags.find("error.type") == tags.end()); + } + if (bucket_name.has_value()) { + REQUIRE(tags.at("db.namespace") == bucket_name.value()); + } else { + REQUIRE(tags.find("db.namespace") == tags.end()); + } + if (scope_name.has_value()) { + REQUIRE(tags.at("couchbase.scope.name") == scope_name.value()); + } else { + REQUIRE(tags.find("couchbase.scope.name") == tags.end()); + } + if (collection_name.has_value()) { + REQUIRE(tags.at("couchbase.collection.name") == collection_name.value()); + } else { + REQUIRE(tags.find("couchbase.collection.name") == tags.end()); + } if (guard.cluster_version().supports_cluster_labels()) { REQUIRE_FALSE(tags.at("couchbase.cluster.name").empty()); REQUIRE_FALSE(tags.at("couchbase.cluster.uuid").empty()); @@ -181,73 +190,79 @@ make_id(const test::utils::test_context& ctx, std::string key = "") TEST_CASE("integration: use external meter", "[integration]") { - couchbase::core::cluster_options opts{}; + test::utils::integration_test_guard integration; + auto meter = std::make_shared(); - opts.meter = meter; - test::utils::integration_test_guard guard(opts); - test::utils::open_bucket(guard.cluster, guard.ctx.bucket); - auto value = couchbase::core::utils::to_binary(R"({"some": "thing")"); - auto existing_id = make_id(guard.ctx, "foo"); - SECTION("add doc 'foo'") + auto cluster = integration.public_cluster([meter](couchbase::cluster_options& opts) { + opts.metrics().meter(meter); + }); + + auto value = tao::json::value{ + { "some", "thing" }, + }; + auto existing_key = test::utils::uniq_id("meter"); + auto collection = cluster.bucket(integration.ctx.bucket).default_collection(); + { - const couchbase::core::operations::upsert_request r{ existing_id, value }; - auto response = test::utils::execute(guard.cluster, r); - REQUIRE_FALSE(response.ctx.ec()); + auto [err, res] = collection.upsert(existing_key, value, {}).get(); + REQUIRE_SUCCESS(err.ec()); } + + meter->reset(); + SECTION("with KV ops") { SECTION("upsert") { - meter->reset(); - const couchbase::core::operations::upsert_request r{ existing_id, value }; - auto response = test::utils::execute(guard.cluster, r); - REQUIRE_FALSE(response.ctx.ec()); - auto recorders = meter->get_recorders("db.couchbase.operations"); + auto [err, res] = collection.upsert(existing_key, value, {}).get(); + REQUIRE_FALSE(err.ec()); + + auto recorders = meter->get_recorders("db.client.operation.duration"); REQUIRE_FALSE(recorders.empty()); - assert_kv_recorder_tags(guard, recorders, "upsert", existing_id); + assert_kv_recorder_tags(integration, recorders, "upsert"); } + SECTION("insert") { - meter->reset(); - auto new_id = make_id(guard.ctx); - const couchbase::core::operations::insert_request r{ new_id, value }; - auto response = test::utils::execute(guard.cluster, r); - REQUIRE_FALSE(response.ctx.ec()); - auto recorders = meter->get_recorders("db.couchbase.operations"); + auto new_key = test::utils::uniq_id("meter"); + auto [err, res] = collection.insert(new_key, value, {}).get(); + + auto recorders = meter->get_recorders("db.client.operation.duration"); REQUIRE_FALSE(recorders.empty()); - assert_kv_recorder_tags(guard, recorders, "insert", new_id); + assert_kv_recorder_tags(integration, recorders, "insert"); } + SECTION("replace") { - meter->reset(); - auto new_value = couchbase::core::utils::to_binary("{\"some\": \"thing else\""); - const couchbase::core::operations::replace_request r{ existing_id, new_value }; - auto response = test::utils::execute(guard.cluster, r); - REQUIRE_FALSE(response.ctx.ec()); - auto recorders = meter->get_recorders("db.couchbase.operations"); + auto new_value = tao::json::value{ + { "some", "thing else" }, + }; + auto [err, res] = collection.replace(existing_key, new_value, {}).get(); + REQUIRE_FALSE(err.ec()); + + auto recorders = meter->get_recorders("db.client.operation.duration"); REQUIRE_FALSE(recorders.empty()); - assert_kv_recorder_tags(guard, recorders, "replace", existing_id); + assert_kv_recorder_tags(integration, recorders, "replace"); } + SECTION("get") { - meter->reset(); - const couchbase::core::operations::get_request r{ existing_id }; - auto response = test::utils::execute(guard.cluster, r); - REQUIRE_FALSE(response.ctx.ec()); - auto meters = meter->get_recorders("db.couchbase.operations"); + auto [err, res] = collection.get(existing_key, {}).get(); + REQUIRE_FALSE(err.ec()); + + auto meters = meter->get_recorders("db.client.operation.duration"); REQUIRE_FALSE(meters.empty()); - assert_kv_recorder_tags(guard, meters, "get", existing_id); + assert_kv_recorder_tags(integration, meters, "get"); } + SECTION("get non-existent-document") { - meter->reset(); - auto new_id = make_id(guard.ctx); - const couchbase::core::operations::get_request r{ new_id }; - auto response = test::utils::execute(guard.cluster, r); - REQUIRE(response.ctx.ec() == couchbase::errc::key_value::document_not_found); - auto meters = meter->get_recorders("db.couchbase.operations"); + auto [err, res] = collection.get(test::utils::uniq_id("does-not-exist"), {}).get(); + REQUIRE(err.ec() == couchbase::errc::key_value::document_not_found); + + auto meters = meter->get_recorders("db.client.operation.duration"); REQUIRE_FALSE(meters.empty()); - assert_kv_recorder_tags(guard, meters, "get", new_id, "DocumentNotFound"); + assert_kv_recorder_tags(integration, meters, "get", "DocumentNotFound"); } } @@ -255,39 +270,60 @@ TEST_CASE("integration: use external meter", "[integration]") { SECTION("get_all_scopes") { - if (!guard.cluster_version().supports_collections()) { + if (!integration.cluster_version().supports_collections()) { SKIP("cluster does not support collections"); } - meter->reset(); - const couchbase::core::operations::management::scope_get_all_request r{ guard.ctx.bucket }; - auto response = test::utils::execute(guard.cluster, r); - REQUIRE_SUCCESS(response.ctx.ec); - auto meters = meter->get_recorders("db.couchbase.operations"); + auto [err, res] = cluster.bucket(integration.ctx.bucket).collections().get_all_scopes().get(); + REQUIRE_SUCCESS(err.ec()); + + auto meters = meter->get_recorders("db.client.operation.duration"); REQUIRE_FALSE(meters.empty()); - assert_http_recorder_tags(guard, meters, "manager_collections_get_all_scopes", "management"); + assert_http_recorder_tags(integration, + meters, + "manager_collections_get_all_scopes", + "management", + {}, + integration.ctx.bucket); } SECTION("query") { - meter->reset(); - const couchbase::core::operations::query_request r{ "SELECT 1=1" }; - auto response = test::utils::execute(guard.cluster, r); - REQUIRE_SUCCESS(response.ctx.ec); - auto meters = meter->get_recorders("db.couchbase.operations"); - REQUIRE_FALSE(meters.empty()); - assert_http_recorder_tags(guard, meters, "query", "query"); + SECTION("cluster-level") + { + auto [err, res] = cluster.query("SELECT 1=1", {}).get(); + REQUIRE_SUCCESS(err.ec()); + + auto meters = meter->get_recorders("db.client.operation.duration"); + REQUIRE_FALSE(meters.empty()); + assert_http_recorder_tags(integration, meters, "query", "query"); + } + + SECTION("scope-level") + { + auto [err, res] = + cluster.bucket(integration.ctx.bucket).default_scope().query("SELECT 1=1", {}).get(); + REQUIRE_SUCCESS(err.ec()); + + auto meters = meter->get_recorders("db.client.operation.duration"); + REQUIRE_FALSE(meters.empty()); + assert_http_recorder_tags( + integration, meters, "query", "query", {}, integration.ctx.bucket, "_default"); + } } SECTION("get_bucket fails with bucket_not_found") { - meter->reset(); - couchbase::core::operations::management::bucket_get_request r{ "non-existent" }; - auto response = test::utils::execute(guard.cluster, r); - REQUIRE(response.ctx.ec == couchbase::errc::common::bucket_not_found); - auto meters = meter->get_recorders("db.couchbase.operations"); + auto [err, res] = cluster.buckets().get_bucket("non-existent").get(); + REQUIRE(err.ec() == couchbase::errc::common::bucket_not_found); + + auto meters = meter->get_recorders("db.client.operation.duration"); REQUIRE_FALSE(meters.empty()); - assert_http_recorder_tags( - guard, meters, "manager_buckets_get_bucket", "management", "BucketNotFound"); + assert_http_recorder_tags(integration, + meters, + "manager_buckets_get_bucket", + "management", + "BucketNotFound", + "non-existent"); } } } diff --git a/test/test_integration_tracer.cxx b/test/test_integration_tracer.cxx index 0f6981f2f..fdc740e8b 100644 --- a/test/test_integration_tracer.cxx +++ b/test/test_integration_tracer.cxx @@ -243,7 +243,7 @@ assert_kv_op_span_ok(test::utils::integration_test_guard& guard, assert_span_ok(guard, span, is_top_level_span, parent); const std::size_t expected_tag_count = - (guard.cluster_version().supports_cluster_labels()) ? 8 : 6; + (guard.cluster_version().supports_cluster_labels()) ? 9 : 7; REQUIRE(span->string_tags().size() + span->int_tags().size() == expected_tag_count); REQUIRE(op == span->name()); @@ -252,6 +252,7 @@ assert_kv_op_span_ok(test::utils::integration_test_guard& guard, REQUIRE(span->string_tags()["couchbase.scope.name"] == "_default"); REQUIRE(span->string_tags()["couchbase.collection.name"] == "_default"); REQUIRE(span->string_tags()["db.operation.name"] == op); + REQUIRE(span->int_tags().find("couchbase.retries") != span->int_tags().end()); // There must be at least one dispatch span auto dispatch_spans = span->child_spans("dispatch_to_server"); @@ -338,6 +339,7 @@ assert_http_op_span_ok(test::utils::integration_test_guard& guard, REQUIRE(span->name().find(op) != std::string::npos); REQUIRE(span->string_tags()["db.operation.name"] == op); + REQUIRE(span->int_tags().find("couchbase.retries") != span->int_tags().end()); REQUIRE(span->duration().count() > 0); if (expected_service.has_value()) { REQUIRE(span->string_tags()["couchbase.service"] == expected_service.value()); diff --git a/test/test_integration_wrapper_tracer.cxx b/test/test_integration_wrapper_tracer.cxx index 5c1532240..d4f2b5cd7 100644 --- a/test/test_integration_wrapper_tracer.cxx +++ b/test/test_integration_wrapper_tracer.cxx @@ -37,4 +37,4 @@ TEST_CASE("integration: wrappers can get dispatch spans using a parent wrapper s REQUIRE(resp.ctx.ec() == couchbase::errc::key_value::document_not_found); REQUIRE(root_span->children().size() == 1); REQUIRE(root_span->children().front()->name() == "dispatch_to_server"); -}; +} diff --git a/test/test_unit_metrics.cxx b/test/test_unit_metrics.cxx index 5063696a4..5aece75ad 100644 --- a/test/test_unit_metrics.cxx +++ b/test/test_unit_metrics.cxx @@ -18,15 +18,17 @@ #include "test_helper.hxx" #include "core/metrics/meter_wrapper.hxx" +#include "core/tracing/constants.hxx" #include +#include TEST_CASE("unit: metric attributes encoding", "[unit]") { SECTION("all attributes set") { couchbase::core::metrics::metric_attributes attrs{ - couchbase::core::service_type::key_value, + couchbase::core::tracing::service::key_value, "get", couchbase::errc::key_value::document_not_found, "test-bucket", @@ -37,20 +39,21 @@ TEST_CASE("unit: metric attributes encoding", "[unit]") auto tags = attrs.encode(); - REQUIRE(tags.size() == 8); + REQUIRE(tags.size() == 9); REQUIRE(tags.at("couchbase.service") == "kv"); REQUIRE(tags.at("db.operation.name") == "get"); REQUIRE(tags.at("db.namespace") == "test-bucket"); REQUIRE(tags.at("couchbase.scope.name") == "test-scope"); REQUIRE(tags.at("couchbase.collection.name") == "test-collection"); - REQUIRE(tags.at("outcome") == "DocumentNotFound"); + REQUIRE(tags.at("error.type") == "DocumentNotFound"); REQUIRE(tags.at("couchbase.cluster.name") == "test-cluster"); REQUIRE(tags.at("couchbase.cluster.uuid") == "d476fe9c-1f66-4bf4-9c2b-9ee866fc5251"); + REQUIRE(tags.at("db.system.name") == "couchbase"); } SECTION("successful operation") { - couchbase::core::metrics::metric_attributes attrs{ couchbase::core::service_type::key_value, + couchbase::core::metrics::metric_attributes attrs{ couchbase::core::tracing::service::key_value, "get", {}, "test-bucket", @@ -62,13 +65,13 @@ TEST_CASE("unit: metric attributes encoding", "[unit]") auto tags = attrs.encode(); REQUIRE(tags.size() == 8); - REQUIRE(tags.at("outcome") == "Success"); + REQUIRE(tags.find("error.type") == tags.end()); } SECTION("cluster labels missing") { couchbase::core::metrics::metric_attributes attrs{ - couchbase::core::service_type::key_value, + couchbase::core::tracing::service::key_value, "get", {}, "test-bucket", @@ -86,7 +89,7 @@ TEST_CASE("unit: metric attributes encoding", "[unit]") SECTION("bucket/scope/collection names missing") { couchbase::core::metrics::metric_attributes attrs{ - couchbase::core::service_type::key_value, + couchbase::core::tracing::service::key_value, "get", couchbase::errc::key_value::document_not_found, {}, @@ -97,7 +100,7 @@ TEST_CASE("unit: metric attributes encoding", "[unit]") auto tags = attrs.encode(); - REQUIRE(tags.size() == 5); + REQUIRE(tags.size() == 6); REQUIRE(tags.find("db.namespace") == tags.end()); REQUIRE(tags.find("couchbase.scope.name") == tags.end()); REQUIRE(tags.find("couchbase.collection.name") == tags.end()); From 649343d7ce1ea98cccded74671504c15aacc26d2 Mon Sep 17 00:00:00 2001 From: Sergey Avseyev Date: Tue, 25 Nov 2025 15:36:27 -0800 Subject: [PATCH 2/2] copy tag containers to local scope to avoid temporaries test/test_integration_tracer.cxx:255:3: note: Same iterator is used with containers 'span->int_tags()' that are temporaries or defined in different scopes. REQUIRE(span->int_tags().find("couchbase.retries") != span->int_tags().end()); ^ test/test_integration_tracer.cxx:342:3: error: Same iterator is used with containers 'span->int_tags()' that are temporaries or defined in different scopes. [iterators3] REQUIRE(span->int_tags().find("couchbase.retries") != span->int_tags().end()); --- test/test_integration_tracer.cxx | 41 +++++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/test/test_integration_tracer.cxx b/test/test_integration_tracer.cxx index fdc740e8b..2e524f3ae 100644 --- a/test/test_integration_tracer.cxx +++ b/test/test_integration_tracer.cxx @@ -242,17 +242,21 @@ assert_kv_op_span_ok(test::utils::integration_test_guard& guard, { assert_span_ok(guard, span, is_top_level_span, parent); + auto string_tags = span->string_tags(); + auto int_tags = span->int_tags(); + const std::size_t expected_tag_count = (guard.cluster_version().supports_cluster_labels()) ? 9 : 7; - REQUIRE(span->string_tags().size() + span->int_tags().size() == expected_tag_count); + + REQUIRE(string_tags.size() + int_tags.size() == expected_tag_count); REQUIRE(op == span->name()); - REQUIRE(span->string_tags()["couchbase.service"] == "kv"); - REQUIRE(span->string_tags()["db.namespace"] == guard.ctx.bucket); - REQUIRE(span->string_tags()["couchbase.scope.name"] == "_default"); - REQUIRE(span->string_tags()["couchbase.collection.name"] == "_default"); - REQUIRE(span->string_tags()["db.operation.name"] == op); - REQUIRE(span->int_tags().find("couchbase.retries") != span->int_tags().end()); + REQUIRE(string_tags["couchbase.service"] == "kv"); + REQUIRE(string_tags["db.namespace"] == guard.ctx.bucket); + REQUIRE(string_tags["couchbase.scope.name"] == "_default"); + REQUIRE(string_tags["couchbase.collection.name"] == "_default"); + REQUIRE(string_tags["db.operation.name"] == op); + REQUIRE(int_tags.find("couchbase.retries") != int_tags.end()); // There must be at least one dispatch span auto dispatch_spans = span->child_spans("dispatch_to_server"); @@ -337,29 +341,32 @@ assert_http_op_span_ok(test::utils::integration_test_guard& guard, { assert_span_ok(guard, span, is_top_level_op_span, std::move(parent)); + auto string_tags = span->string_tags(); + auto int_tags = span->int_tags(); + REQUIRE(span->name().find(op) != std::string::npos); - REQUIRE(span->string_tags()["db.operation.name"] == op); - REQUIRE(span->int_tags().find("couchbase.retries") != span->int_tags().end()); + REQUIRE(string_tags["db.operation.name"] == op); + REQUIRE(int_tags.find("couchbase.retries") != int_tags.end()); REQUIRE(span->duration().count() > 0); if (expected_service.has_value()) { - REQUIRE(span->string_tags()["couchbase.service"] == expected_service.value()); + REQUIRE(string_tags["couchbase.service"] == expected_service.value()); } else { - REQUIRE(span->string_tags().count("couchbase.service") == 0); + REQUIRE(string_tags.count("couchbase.service") == 0); } if (expected_bucket_name.has_value()) { - REQUIRE(span->string_tags()["db.namespace"] == expected_bucket_name.value()); + REQUIRE(string_tags["db.namespace"] == expected_bucket_name.value()); } else { - REQUIRE(span->string_tags().count("db.namespace") == 0); + REQUIRE(string_tags.count("db.namespace") == 0); } if (expected_scope_name.has_value()) { - REQUIRE(span->string_tags()["couchbase.scope.name"] == expected_scope_name.value()); + REQUIRE(string_tags["couchbase.scope.name"] == expected_scope_name.value()); } else { - REQUIRE(span->string_tags().count("couchbase.scope.name") == 0); + REQUIRE(string_tags.count("couchbase.scope.name") == 0); } if (expected_collection_name.has_value()) { - REQUIRE(span->string_tags()["couchbase.collection.name"] == expected_collection_name.value()); + REQUIRE(string_tags["couchbase.collection.name"] == expected_collection_name.value()); } else { - REQUIRE(span->string_tags().count("couchbase.collection.name") == 0); + REQUIRE(string_tags.count("couchbase.collection.name") == 0); } // There must be at least one dispatch span