Skip to content

Commit e6ec244

Browse files
committed
WIP: OpenTelemetry integration refactoring
1 parent 47e8847 commit e6ec244

File tree

14 files changed

+394
-232
lines changed

14 files changed

+394
-232
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ set(couchbase_cxx_client_FILES
402402
core/search_query_options.cxx
403403
core/seed_config.cxx
404404
core/topology/configuration.cxx
405+
core/tracing/otel_tracer.cxx
405406
core/tracing/threshold_logging_tracer.cxx
406407
core/tracing/tracer_wrapper.cxx
407408
core/transactions/active_transaction_record.cxx
@@ -528,6 +529,7 @@ foreach(TARGET ${couchbase_cxx_client_LIBRARIES})
528529
snappy
529530
jsonsl
530531
couchbase_backtrace
532+
opentelemetry_api
531533
hdr_histogram_static)
532534

533535
if(WIN32)

cmake/ThirdPartyDependencies.cmake

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,29 @@ if(NOT TARGET spdlog::spdlog)
4040
"SPDLOG_FMT_EXTERNAL OFF")
4141
endif()
4242

43+
if(NOT TARGET opentelemetry_api)
44+
# https://github.com/open-telemetry/opentelemetry-cpp/releases
45+
cpmaddpackage(
46+
NAME
47+
opentelemetry_api
48+
VERSION
49+
1.17.0
50+
GITHUB_REPOSITORY
51+
"open-telemetry/opentelemetry-cpp"
52+
EXCLUDE_FROM_ALL ON
53+
OPTIONS
54+
"OPENTELEMETRY_INSTALL OFF"
55+
"WITH_ABSEIL OFF"
56+
"WITH_OTLP_HTTP ON"
57+
"WITH_OTLP_GRPC OFF"
58+
"WITH_BENCHMARK OFF"
59+
"BUILD_TESTING OFF"
60+
"BUILD_SHARED_LIBS OFF"
61+
"CMAKE_C_VISIBILITY_PRESET hidden"
62+
"CMAKE_CXX_VISIBILITY_PRESET hidden"
63+
"CMAKE_POSITION_INDEPENDENT_CODE ON")
64+
endif()
65+
4366
if(NOT TARGET Microsoft.GSL::GSL)
4467
# https://github.com/microsoft/GSL/releases
4568
cpmaddpackage(

core/meta/version.cxx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <llhttp.h>
3232
#include <openssl/crypto.h>
3333
#include <openssl/x509.h>
34+
#include <opentelemetry/version.h>
3435
#include <snappy-stubs-public.h>
3536
#include <spdlog/fmt/bundled/core.h>
3637
#include <spdlog/version.h>
@@ -168,6 +169,7 @@ sdk_build_info() -> std::map<std::string, std::string>
168169
#if defined(__GLIBC__)
169170
info["libc"] = fmt::format("glibc {}.{}", __GLIBC__, __GLIBC_MINOR__);
170171
#endif
172+
info["opentelemetry_api"] = OPENTELEMETRY_VERSION;
171173

172174
return info;
173175
}

core/metrics/otel_meter.cxx

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2+
/*
3+
* Copyright 2021 Couchbase, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#pragma once
19+
20+
#include "otel_meter.hxx"
21+
22+
#include <couchbase/metrics/meter.hxx>
23+
24+
#include <opentelemetry/metrics/meter_provider.h>
25+
#include <opentelemetry/metrics/provider.h>
26+
27+
#include <algorithm>
28+
#include <iostream>
29+
#include <thread>
30+
#include <utility>
31+
32+
namespace couchbase::core::metrics
33+
{
34+
namespace
35+
{
36+
class otel_sync_histogram
37+
{
38+
public:
39+
otel_sync_histogram(
40+
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Histogram<std::uint64_t>>
41+
histogram_counter)
42+
: histogram_counter_(histogram_counter)
43+
{
44+
}
45+
46+
void record(std::uint64_t value,
47+
const opentelemetry::common::KeyValueIterable& tags,
48+
opentelemetry::context::Context& ctx)
49+
{
50+
histogram_counter_->Record(value, tags, ctx);
51+
}
52+
53+
private:
54+
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Histogram<std::uint64_t>>
55+
histogram_counter_;
56+
std::mutex mutex_;
57+
};
58+
59+
class otel_value_recorder : public couchbase::metrics::value_recorder
60+
{
61+
public:
62+
explicit otel_value_recorder(
63+
nostd::shared_ptr<metrics_api::Histogram<std::uint64_t>> histogram_counter,
64+
const std::map<std::string, std::string>& tags)
65+
: histogram_counter_(histogram_counter)
66+
, tags_(tags)
67+
{
68+
}
69+
void record_value(std::int64_t value) override
70+
{
71+
value = std::max<int64_t>(value, 0);
72+
auto uvalue = static_cast<std::uint64_t>(value);
73+
histogram_counter_->Record(
74+
uvalue, opentelemetry::common::KeyValueIterableView<decltype(tags_)>{ tags_ }, context_);
75+
}
76+
77+
auto tags() -> const std::map<std::string, std::string>
78+
{
79+
return tags_;
80+
}
81+
82+
auto histogram_counter()
83+
-> opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Histogram<std::uint64_t>>
84+
{
85+
return histogram_counter_;
86+
}
87+
88+
private:
89+
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Histogram<std::uint64_t>>
90+
histogram_counter_;
91+
const std::map<std::string, std::string> tags_;
92+
opentelemetry::context::Context context_{};
93+
std::mutex mutex_;
94+
};
95+
} // namespace
96+
97+
class otel_meter_impl
98+
{
99+
friend otel_meter;
100+
101+
public:
102+
explicit otel_meter_impl(opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Meter> meter)
103+
: meter_(meter)
104+
{
105+
}
106+
107+
private:
108+
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Meter> meter_;
109+
std::mutex mutex_;
110+
std::multimap<std::string, std::shared_ptr<otel_value_recorder>> recorders_;
111+
};
112+
113+
auto
114+
otel_meter::get_value_recorder(const std::string& name,
115+
const std::map<std::string, std::string>& tags)
116+
-> std::shared_ptr<couchbase::metrics::value_recorder>
117+
{
118+
// first look up the histogram, in case we already have it...
119+
std::scoped_lock<std::mutex> lock(mutex_);
120+
auto it = recorders_.equal_range(name);
121+
if (it.first == it.second) {
122+
// this name isn't associated with any histogram, so make one and return it.
123+
// Note we'd like to make one with more buckets than default, given the range of
124+
// response times we'd like to display (queries vs kv for instance), but otel
125+
// api doesn't seem to allow this.
126+
return recorders_
127+
.insert({ name,
128+
std::make_shared<otel_value_recorder>(meter_->CreateUInt64Histogram(name, "", "us"),
129+
tags) })
130+
->second;
131+
}
132+
// so it is already, lets see if we already have one with those tags, or need
133+
// to make a new one (using the histogram we already have).
134+
for (auto itr = it.first; itr != it.second; itr++) {
135+
if (tags == itr->second->tags()) {
136+
return itr->second;
137+
}
138+
}
139+
// if you are here, we need to add one with these tags and the histogram associated with the
140+
// name.
141+
return recorders_
142+
.insert(
143+
{ name, std::make_shared<otel_value_recorder>(it.first->second->histogram_counter(), tags) })
144+
->second;
145+
}
146+
} // namespace couchbase::core::metrics

core/metrics/otel_meter.hxx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2+
/*
3+
* Copyright 2021 Couchbase, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#pragma once
19+
20+
#include <couchbase/metrics/meter.hxx>
21+
22+
namespace couchbase::core::metrics
23+
{
24+
25+
class otel_meter_impl;
26+
27+
class otel_meter : public couchbase::metrics::meter
28+
{
29+
public:
30+
auto get_value_recorder(const std::string& name, const std::map<std::string, std::string>& tags)
31+
-> std::shared_ptr<couchbase::metrics::value_recorder> override;
32+
33+
private:
34+
std::unique_ptr<otel_meter_impl> impl_;
35+
};
36+
} // namespace couchbase::core::metrics

core/tracing/noop_tracer.hxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class noop_span : public couchbase::tracing::request_span
3939
/* do nothing */
4040
}
4141

42-
auto uses_tags() const -> bool override
42+
[[nodiscard]] auto uses_tags() const -> bool override
4343
{
4444
return false;
4545
}

core/tracing/otel_tracer.cxx

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2+
/*
3+
* Copyright 2021 Couchbase, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include "otel_tracer.hxx"
19+
20+
#include "../meta/version.hxx"
21+
22+
#if defined(__GNUC__)
23+
#pragma GCC diagnostic push
24+
#pragma GCC diagnostic ignored "-Wsign-conversion"
25+
#endif
26+
#include <opentelemetry/trace/provider.h>
27+
#include <opentelemetry/trace/tracer.h>
28+
#if defined(__GNUC__)
29+
#pragma GCC diagnostic pop
30+
#endif
31+
32+
namespace couchbase::core::tracing
33+
{
34+
namespace
35+
{
36+
class otel_request_span : public couchbase::tracing::request_span
37+
{
38+
public:
39+
explicit otel_request_span(opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span)
40+
: span_(std::move(span))
41+
{
42+
}
43+
44+
void add_tag(const std::string& name, const std::string& value) override
45+
{
46+
span_->SetAttribute(name, value);
47+
}
48+
49+
void add_tag(const std::string& name, std::uint64_t value) override
50+
{
51+
span_->SetAttribute(name, value);
52+
}
53+
54+
void end() override
55+
{
56+
span_->End();
57+
}
58+
59+
auto wrapped_span() -> opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span>
60+
{
61+
return span_;
62+
}
63+
64+
private:
65+
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span_;
66+
};
67+
68+
} // namespace
69+
70+
class otel_request_tracer_impl
71+
{
72+
friend otel_request_tracer;
73+
74+
public:
75+
otel_request_tracer_impl()
76+
: tracer_{
77+
opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("couchbase_cxx_sdk",
78+
meta::sdk_semver()),
79+
}
80+
{
81+
}
82+
83+
private:
84+
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer> tracer_;
85+
};
86+
87+
otel_request_tracer::otel_request_tracer()
88+
: impl_{ std::make_unique<otel_request_tracer_impl>() }
89+
{
90+
}
91+
92+
otel_request_tracer::~otel_request_tracer() = default;
93+
94+
auto
95+
otel_request_tracer::start_span(std::string name,
96+
std::shared_ptr<couchbase::tracing::request_span> parent)
97+
-> std::shared_ptr<couchbase::tracing::request_span>
98+
{
99+
auto wrapped_parent = std::dynamic_pointer_cast<otel_request_span>(parent);
100+
if (wrapped_parent) {
101+
opentelemetry::trace::StartSpanOptions opts;
102+
opts.parent = wrapped_parent->wrapped_span()->GetContext();
103+
return std::make_shared<otel_request_span>(impl_->tracer_->StartSpan(name, opts));
104+
}
105+
return std::make_shared<otel_request_span>(impl_->tracer_->StartSpan(name));
106+
}
107+
108+
} // namespace couchbase::core::tracing

0 commit comments

Comments
 (0)