Skip to content

Commit fe05347

Browse files
committed
Use OpenTelemetry in the tools
1 parent ec71f76 commit fe05347

File tree

5 files changed

+267
-18
lines changed

5 files changed

+267
-18
lines changed

couchbase/tracing/otel_tracer.hxx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@
1818
#pragma once
1919

2020
#include <couchbase/tracing/request_tracer.hxx>
21-
#include <opentelemetry/exporters/ostream/span_exporter.h>
22-
#include <opentelemetry/sdk/trace/simple_processor.h>
23-
#include <opentelemetry/sdk/trace/tracer_provider.h>
21+
2422
#include <opentelemetry/trace/tracer.h>
2523

2624
namespace nostd = opentelemetry::nostd;
@@ -45,7 +43,7 @@ public:
4543
{
4644
span_->End();
4745
}
48-
nostd::shared_ptr<opentelemetry::trace::Span> wrapped_span()
46+
auto wrapped_span() -> nostd::shared_ptr<opentelemetry::trace::Span>
4947
{
5048
return span_;
5149
}
@@ -57,12 +55,12 @@ private:
5755
class otel_request_tracer : public couchbase::tracing::request_tracer
5856
{
5957
public:
60-
otel_request_tracer(nostd::shared_ptr<opentelemetry::trace::Tracer> tracer)
58+
explicit otel_request_tracer(nostd::shared_ptr<opentelemetry::trace::Tracer> tracer)
6159
: tracer_(std::move(tracer))
6260
{
6361
}
6462

65-
auto start_span(std::string name, std::shared_ptr<couchbase::tracing::request_span> parent = {})
63+
auto start_span(std::string name, std::shared_ptr<couchbase::tracing::request_span> parent)
6664
-> std::shared_ptr<couchbase::tracing::request_span> override
6765
{
6866
auto wrapped_parent = std::dynamic_pointer_cast<otel_request_span>(parent);
@@ -77,10 +75,10 @@ public:
7775
auto wrap_span(nostd::shared_ptr<opentelemetry::trace::Span> span)
7876
-> std::shared_ptr<couchbase::tracing::otel_request_span>
7977
{
80-
return std::make_shared<couchbase::tracing::otel_request_span>(span);
78+
return std::make_shared<couchbase::tracing::otel_request_span>(std::move(span));
8179
}
8280

8381
private:
8482
nostd::shared_ptr<opentelemetry::trace::Tracer> tracer_;
8583
};
86-
} // namespace couchbase::tracing
84+
} // namespace couchbase::tracing

tools/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ target_link_libraries(
4646
$<BUILD_INTERFACE:taocpp::json>
4747
$<BUILD_INTERFACE:spdlog::spdlog>
4848
$<BUILD_INTERFACE:hdr_histogram_static>
49-
$<BUILD_INTERFACE:asio>)
49+
$<BUILD_INTERFACE:asio>
50+
$<BUILD_INTERFACE:opentelemetry_exporter_otlp_http>)
5051
propagate_public_compile_definitions(cbc spdlog::spdlog asio)
5152

5253
if(COUCHBASE_CXX_CLIENT_STATIC_BORINGSSL AND WIN32)

tools/get.cxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "utils.hxx"
2020

2121
#include <core/logger/logger.hxx>
22+
2223
#include <couchbase/cluster.hxx>
2324
#include <couchbase/codec/raw_binary_transcoder.hxx>
2425
#include <couchbase/codec/tao_json_serializer.hxx>

tools/utils.cxx

Lines changed: 231 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
*/
1717

1818
#include "utils.hxx"
19+
#include "CLI/CLI.hpp"
1920
#include "core/logger/configuration.hxx"
2021
#include "core/logger/logger.hxx"
2122
#include "core/meta/version.hxx"
2223
#include "core/utils/binary.hxx"
23-
#include "core/utils/duration_parser.hxx"
2424
#include "core/utils/json.hxx"
2525

2626
#include <couchbase/cluster_options.hxx>
@@ -30,6 +30,43 @@
3030
#include <couchbase/fmt/query_scan_consistency.hxx>
3131
#include <couchbase/scope.hxx>
3232

33+
#if defined(__GNUC__)
34+
#pragma GCC diagnostic push
35+
#pragma GCC diagnostic ignored "-Wsign-conversion"
36+
#pragma GCC diagnostic ignored "-Wuseless-cast"
37+
#elif defined(__clang__)
38+
#pragma clang diagnostic push
39+
#pragma clang diagnostic ignored "-Wdeprecated-builtins"
40+
#endif
41+
#include <opentelemetry/sdk/common/global_log_handler.h>
42+
43+
#include <opentelemetry/trace/provider.h>
44+
#include <opentelemetry/trace/span_startoptions.h>
45+
#include <opentelemetry/trace/tracer_provider.h>
46+
47+
#include <opentelemetry/sdk/trace/batch_span_processor.h>
48+
#include <opentelemetry/sdk/trace/batch_span_processor_factory.h>
49+
#include <opentelemetry/sdk/trace/samplers/always_off.h>
50+
#include <opentelemetry/sdk/trace/samplers/always_on.h>
51+
#include <opentelemetry/sdk/trace/samplers/parent.h>
52+
#include <opentelemetry/sdk/trace/samplers/trace_id_ratio.h>
53+
#include <opentelemetry/sdk/trace/simple_processor.h>
54+
#include <opentelemetry/sdk/trace/simple_processor_factory.h>
55+
#include <opentelemetry/sdk/trace/tracer_provider.h>
56+
#include <opentelemetry/sdk/trace/tracer_provider_factory.h>
57+
58+
#include <opentelemetry/exporters/otlp/otlp_http.h>
59+
#include <opentelemetry/exporters/otlp/otlp_http_exporter_factory.h>
60+
#include <opentelemetry/exporters/otlp/otlp_http_exporter_options.h>
61+
62+
#include <couchbase/tracing/otel_tracer.hxx>
63+
#if defined(__GNUC__)
64+
#pragma GCC diagnostic pop
65+
#elif defined(__clang__)
66+
#pragma clang diagnostic pop
67+
#endif
68+
69+
#include <memory>
3370
#include <spdlog/details/os.h>
3471
#include <spdlog/fmt/bundled/chrono.h>
3572
#include <spdlog/spdlog.h>
@@ -43,13 +80,54 @@ namespace cbc
4380
{
4481
namespace
4582
{
83+
84+
class null_opentelemetry_logger : public opentelemetry::sdk::common::internal_log::LogHandler
85+
{
86+
public:
87+
void Handle(opentelemetry::sdk::common::internal_log::LogLevel /* level */,
88+
const char* /* file */,
89+
int /* line */,
90+
const char* /* msg */,
91+
const opentelemetry::sdk::common::AttributeMap& /* attributes */) noexcept override
92+
{
93+
// Use it to debug issues with OpenTelemetry
94+
// TODO(SA): maybe we should forward these messages to our logger
95+
}
96+
};
97+
4698
auto
47-
getenv_or_default(std::string_view var_name, const std::string& default_value) -> std::string
99+
safe_getenv(const std::string& name) noexcept -> std::optional<std::string>
48100
{
49-
if (auto val = spdlog::details::os::getenv(var_name.data()); !val.empty()) {
50-
return val;
101+
if (name.empty()) {
102+
return std::nullopt;
103+
}
104+
105+
#if defined(_WIN32)
106+
char* buf = nullptr;
107+
size_t len = 0;
108+
if (_dupenv_s(&buf, &len, name.c_str()) == 0 && buf != nullptr) {
109+
std::string value(buf);
110+
free(buf);
111+
if (!value.empty()) {
112+
return value;
113+
}
51114
}
52-
return default_value;
115+
return std::nullopt;
116+
117+
#else
118+
if (const char* val = std::getenv(name.c_str())) { // NOLINT(concurrency-mt-unsafe)
119+
if (val[0] != '\0') {
120+
return std::string(val);
121+
}
122+
}
123+
return std::nullopt;
124+
#endif
125+
}
126+
127+
auto
128+
getenv_or_default(const std::string& var_name, const std::string& default_value) -> std::string
129+
{
130+
return safe_getenv(var_name).value_or(default_value);
53131
}
54132

55133
auto
@@ -388,6 +466,84 @@ add_options(CLI::App* app, tracing_options& options)
388466
->add_option("--tracing-threshold-view", options.threshold_view, "Threshold for Query service.")
389467
->default_val(defaults.tracing.view_threshold)
390468
->type_name("DURATION");
469+
470+
group->add_flag("--tracing-use-opentelemetry",
471+
options.opentelemetry.use_opentelemetry,
472+
"Use OpenTelemetry and export with HTTP transport.");
473+
474+
group->add_option("--tracing-opentelemetry-endpoint",
475+
options.opentelemetry.endpoint,
476+
"The OTLP HTTP endpoint for OpenTelemetry to export to.");
477+
478+
std::vector<std::string> allowed_samplers{
479+
"always_on",
480+
"always_off",
481+
"trace_id_ratio_based",
482+
"parent_based",
483+
};
484+
group
485+
->add_option("--tracing-opentelemetry-sampler",
486+
options.opentelemetry.sampler,
487+
"Sampler type for OpenTelemetry tracing.")
488+
->transform(CLI::IsMember(allowed_samplers))
489+
->default_str("parent_based");
490+
491+
group
492+
->add_option("--tracing-opentelemetry-sampling-ratio",
493+
options.opentelemetry.sampling_ratio,
494+
"Sampling ratio for trace_id_ratio_based sampler (0.0 to 1.0).")
495+
->check(CLI::Range(0.0, 1.0))
496+
->default_str("1.0");
497+
498+
group->add_flag("--tracing-opentelemetry-use-batch-processor{true},!--tracing-opentelemetry-use-"
499+
"simple-processor{false}",
500+
options.opentelemetry.use_batch_processor,
501+
"Use batch span processor (default) or simple span processor.");
502+
503+
group
504+
->add_option("--tracing-opentelemetry-batch-schedule-delay",
505+
options.opentelemetry.batch_schedule_delay,
506+
"Batch processor schedule delay in milliseconds.")
507+
->default_val(options.opentelemetry.batch_schedule_delay)
508+
->type_name("DURATION");
509+
510+
group
511+
->add_option("--tracing-opentelemetry-batch-max-queue-size",
512+
options.opentelemetry.batch_max_queue_size,
513+
"Maximum queue size for batch processor.")
514+
->default_val(options.opentelemetry.batch_max_queue_size);
515+
516+
group
517+
->add_option("--tracing-opentelemetry-batch-max-export-batch-size",
518+
options.opentelemetry.batch_max_export_batch_size,
519+
"Maximum export batch size for batch processor.")
520+
->default_val(options.opentelemetry.batch_max_export_batch_size);
521+
522+
group
523+
->add_option("--tracing-opentelemetry-exporter-timeout",
524+
options.opentelemetry.exporter_timeout,
525+
"Exporter timeout in milliseconds.")
526+
->default_val(options.opentelemetry.exporter_timeout)
527+
->type_name("DURATION");
528+
529+
group->add_flag("--tracing-opentelemetry-use-ssl-credentials",
530+
options.opentelemetry.use_ssl_credentials,
531+
"Use SSL/TLS credentials for secure connection.");
532+
533+
group->add_option("--tracing-opentelemetry-ssl-credentials-cacert",
534+
options.opentelemetry.ssl_credentials_cacert,
535+
"Path to CA certificate file for SSL/TLS verification.");
536+
537+
group
538+
->add_option("--tracing-opentelemetry-headers",
539+
options.opentelemetry.headers,
540+
"HTTP headers (e.g. for authentication) (format: key=value).")
541+
->type_name("KEY=VALUE")
542+
->take_all();
543+
544+
group->add_option("--tracing-opentelemetry-compression",
545+
options.opentelemetry.compression,
546+
"Compression algorithm for exporter (e.g., 'gzip').");
391547
}
392548

393549
auto
@@ -559,9 +715,77 @@ apply_options(couchbase::cluster_options& options, const metrics_options& metric
559715
options.metrics().emit_interval(metrics.emit_interval);
560716
}
561717

718+
void
719+
apply_opentelemetry_tracer_options(couchbase::cluster_options& options,
720+
const opentelemetry_tracing_options& tracing)
721+
{
722+
opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogHandler(
723+
std::make_shared<null_opentelemetry_logger>());
724+
725+
opentelemetry::exporter::otlp::OtlpHttpExporterOptions exporter_options{};
726+
if (auto endpoint = tracing.endpoint; endpoint) {
727+
exporter_options.url = endpoint.value();
728+
}
729+
exporter_options.timeout = tracing.exporter_timeout;
730+
if (!tracing.headers.empty()) {
731+
for (const auto& [key, value] : tracing.headers) {
732+
exporter_options.http_headers.insert({ key, value });
733+
}
734+
}
735+
if (auto compression = tracing.compression; compression) {
736+
exporter_options.compression = compression.value();
737+
}
738+
739+
auto exporter = opentelemetry::exporter::otlp::OtlpHttpExporterFactory::Create(exporter_options);
740+
741+
std::unique_ptr<opentelemetry::sdk::trace::SpanProcessor> processor;
742+
if (tracing.use_batch_processor) {
743+
opentelemetry::sdk::trace::BatchSpanProcessorOptions batch_opts{};
744+
batch_opts.schedule_delay_millis = tracing.batch_schedule_delay;
745+
batch_opts.max_queue_size = tracing.batch_max_queue_size;
746+
batch_opts.max_export_batch_size = tracing.batch_max_export_batch_size;
747+
processor =
748+
opentelemetry::sdk::trace::BatchSpanProcessorFactory::Create(std::move(exporter), batch_opts);
749+
} else {
750+
processor = opentelemetry::sdk::trace::SimpleSpanProcessorFactory::Create(std::move(exporter));
751+
}
752+
753+
auto resource = opentelemetry::sdk::resource::Resource::Create({
754+
{ "service.name", "cbc" },
755+
{ "service.version", couchbase::core::meta::sdk_semver() },
756+
});
757+
758+
std::unique_ptr<opentelemetry::sdk::trace::Sampler> sampler;
759+
if (!tracing.sampler || tracing.sampler == "parent_based") {
760+
sampler = std::make_unique<opentelemetry::sdk::trace::ParentBasedSampler>(
761+
std::make_shared<opentelemetry::sdk::trace::AlwaysOnSampler>());
762+
} else if (tracing.sampler == "always_on") {
763+
sampler = std::make_unique<opentelemetry::sdk::trace::AlwaysOnSampler>();
764+
} else if (tracing.sampler == "always_off") {
765+
sampler = std::make_unique<opentelemetry::sdk::trace::AlwaysOffSampler>();
766+
} else if (tracing.sampler == "trace_id_ratio_based") {
767+
sampler =
768+
std::make_unique<opentelemetry::sdk::trace::TraceIdRatioBasedSampler>(tracing.sampling_ratio);
769+
}
770+
771+
auto provider = opentelemetry::sdk::trace::TracerProviderFactory::Create(
772+
std::move(processor), resource, std::move(sampler));
773+
774+
opentelemetry::trace::Provider::SetTracerProvider(
775+
std::shared_ptr<opentelemetry::trace::TracerProvider>{ std::move(provider) });
776+
777+
options.tracing().tracer(std::make_shared<couchbase::tracing::otel_request_tracer>(
778+
opentelemetry::trace::Provider::GetTracerProvider()->GetTracer(
779+
"cbc", couchbase::core::meta::sdk_semver())));
780+
}
781+
562782
void
563783
apply_options(couchbase::cluster_options& options, const tracing_options& tracing)
564784
{
785+
if (tracing.opentelemetry.use_opentelemetry) {
786+
apply_opentelemetry_tracer_options(options, tracing.opentelemetry);
787+
}
788+
565789
options.tracing().enable(!tracing.disable);
566790
options.tracing().orphaned_emit_interval(tracing.orphaned_emit_interval);
567791
options.tracing().orphaned_sample_size(tracing.orphaned_sample_size);
@@ -741,7 +965,8 @@ available_analytics_scan_consistency_modes() -> std::vector<std::string>
741965
fail(std::string_view message)
742966
{
743967
fmt::print(stderr, "ERROR: {}\n", message);
744-
exit(EXIT_FAILURE);
968+
969+
std::quick_exit(EXIT_FAILURE);
745970
}
746971

747972
auto

0 commit comments

Comments
 (0)