diff --git a/CHANGELOG.md b/CHANGELOG.md index e1b10d6f58..fdc2db1207 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Increment the: ## [Unreleased] +* [SDK] Add instrumentation library and multiple tracer support ([#693](https://github.com/open-telemetry/opentelemetry-cpp/pull/693)) + ## [0.5.0] 2021-04-26 * [SDK] Support custom span-id and trace-id generator ([#681](https://github.com/open-telemetry/opentelemetry-cpp/pull/681)) diff --git a/api/include/opentelemetry/trace/tracer.h b/api/include/opentelemetry/trace/tracer.h index e358100f4f..ab9c6f1675 100644 --- a/api/include/opentelemetry/trace/tracer.h +++ b/api/include/opentelemetry/trace/tracer.h @@ -14,6 +14,7 @@ OPENTELEMETRY_BEGIN_NAMESPACE namespace trace { + /** * Handles span creation and in-process context propagation. * diff --git a/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h b/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h new file mode 100644 index 0000000000..ce9a89e9eb --- /dev/null +++ b/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h @@ -0,0 +1,85 @@ +// Copyright 2021, OpenTelemetry Authors +// +// 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 "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/unique_ptr.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE + +namespace sdk +{ +namespace instrumentationlibrary +{ + +class InstrumentationLibrary +{ +public: + InstrumentationLibrary(const InstrumentationLibrary &) = default; + + /** + * Returns a newly created InstrumentationLibrary with the specified library name and version. + * @param name name of the instrumentation library. + * @param version version of the instrumentation library. + * @returns the newly created InstrumentationLibrary. + */ + static nostd::unique_ptr create(nostd::string_view name, + nostd::string_view version = "") + { + return nostd::unique_ptr( + new InstrumentationLibrary{std::string{name}, std::string{version}}); + } + + /** + * Compare 2 instrumentation libraries. + * @param other the instrumentation library to compare to. + * @returns true if the 2 instrumentation libraries are equal, false otherwise. + */ + bool operator==(const InstrumentationLibrary &other) const + { + return equal(other.name_, other.version_); + } + + /** + * Check whether the instrumentation library has given name and version. + * This could be used to check version equality and avoid heap allocation. + * @param name name of the instrumentation library to compare. + * @param version version of the instrumentatoin library to compare. + * @returns true if name and version in this instrumentation library are equal with the given name + * and version. + */ + bool equal(const nostd::string_view name, const nostd::string_view version) const + { + return this->name_ == name && this->version_ == version; + } + + const std::string &GetName() const { return name_; } + const std::string &GetVersion() const { return version_; } + +private: + InstrumentationLibrary(nostd::string_view name, nostd::string_view version) + : name_(name), version_(version) + {} + +private: + std::string name_; + std::string version_; +}; + +} // namespace instrumentationlibrary +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h index 39af27ff7f..1e025ec89b 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -1,6 +1,7 @@ #pragma once #include "opentelemetry/sdk/common/atomic_shared_ptr.h" +#include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/samplers/always_on.h" @@ -16,11 +17,16 @@ namespace sdk { namespace trace { + +using namespace opentelemetry::sdk::instrumentationlibrary; + class Tracer final : public trace_api::Tracer, public std::enable_shared_from_this { public: /** Construct a new Tracer with the given context pipeline. */ - explicit Tracer(std::shared_ptr context) noexcept; + explicit Tracer(std::shared_ptr context, + std::unique_ptr instrumentation_library = + InstrumentationLibrary::create("")) noexcept; nostd::shared_ptr StartSpan( nostd::string_view name, @@ -33,16 +39,23 @@ class Tracer final : public trace_api::Tracer, public std::enable_shared_from_th void CloseWithMicroseconds(uint64_t timeout) noexcept override; /** Returns the currently active span processor. */ - SpanProcessor &GetActiveProcessor() noexcept { return context_->GetActiveProcessor(); } + SpanProcessor &GetActiveProcessor() const noexcept { return context_->GetActiveProcessor(); } /** Returns the configured Id generator */ - IdGenerator &GetIdGenerator() noexcept { return context_->GetIdGenerator(); } + IdGenerator &GetIdGenerator() const noexcept { return context_->GetIdGenerator(); } + + /** Returns the associated instruementation library */ + const InstrumentationLibrary &GetInstrumentationLibrary() const noexcept + { + return *instrumentation_library_; + } // Note: Test only Sampler &GetSampler() { return context_->GetSampler(); } private: std::shared_ptr context_; + std::shared_ptr instrumentation_library_; }; } // namespace trace } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h index a82e29ccef..6c1f8595fa 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -2,7 +2,9 @@ #include #include +#include #include +#include #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/sdk/resource/resource.h" @@ -73,7 +75,8 @@ class TracerProvider final : public opentelemetry::trace::TracerProvider private: std::shared_ptr context_; - std::shared_ptr tracer_; + std::vector> tracers_; + std::mutex lock_; }; } // namespace trace } // namespace sdk diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index 2d67321d83..e111357ec5 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -11,7 +11,10 @@ namespace sdk namespace trace { -Tracer::Tracer(std::shared_ptr context) noexcept : context_{context} {} +Tracer::Tracer(std::shared_ptr context, + std::unique_ptr instrumentation_library) noexcept + : context_{context}, instrumentation_library_{std::move(instrumentation_library)} +{} trace_api::SpanContext GetCurrentSpanContext(const trace_api::SpanContext &explicit_parent) { diff --git a/sdk/src/trace/tracer_context.cc b/sdk/src/trace/tracer_context.cc index 8dc5397dc9..c7bf1934e1 100644 --- a/sdk/src/trace/tracer_context.cc +++ b/sdk/src/trace/tracer_context.cc @@ -19,7 +19,7 @@ TracerContext::TracerContext(std::unique_ptr processor, Sampler &TracerContext::GetSampler() const noexcept { - return *sampler_.get(); + return *sampler_; } const opentelemetry::sdk::resource::Resource &TracerContext::GetResource() const noexcept diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index 479ba8a462..47bbc54ecb 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -6,7 +6,7 @@ namespace sdk namespace trace { TracerProvider::TracerProvider(std::shared_ptr context) noexcept - : context_{context}, tracer_(new Tracer(context)) + : context_{context} {} TracerProvider::TracerProvider(std::unique_ptr processor, @@ -19,11 +19,29 @@ TracerProvider::TracerProvider(std::unique_ptr processor, std::move(id_generator))) {} -opentelemetry::nostd::shared_ptr TracerProvider::GetTracer( +nostd::shared_ptr TracerProvider::GetTracer( nostd::string_view library_name, nostd::string_view library_version) noexcept { - return opentelemetry::nostd::shared_ptr(tracer_); + // if (library_name == "") { + // // TODO: log invalid library_name. + // } + + const std::lock_guard guard(lock_); + + for (auto &tracer : tracers_) + { + auto &tracer_lib = tracer->GetInstrumentationLibrary(); + if (tracer_lib.equal(library_name, library_version)) + { + return nostd::shared_ptr{tracer}; + } + } + + auto lib = InstrumentationLibrary::create(library_name, library_version); + tracers_.push_back(std::shared_ptr( + new sdk::trace::Tracer(context_, std::move(lib)))); + return nostd::shared_ptr{tracers_.back()}; } void TracerProvider::RegisterPipeline(std::unique_ptr processor) noexcept diff --git a/sdk/test/CMakeLists.txt b/sdk/test/CMakeLists.txt index eae1209f46..77047165d9 100644 --- a/sdk/test/CMakeLists.txt +++ b/sdk/test/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(trace) add_subdirectory(metrics) add_subdirectory(logs) add_subdirectory(resource) +add_subdirectory(instrumentationlibrary) diff --git a/sdk/test/instrumentationlibrary/BUILD b/sdk/test/instrumentationlibrary/BUILD new file mode 100644 index 0000000000..68373e42c5 --- /dev/null +++ b/sdk/test/instrumentationlibrary/BUILD @@ -0,0 +1,13 @@ +load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark") + +cc_test( + name = "instrumentationlibrary_test", + srcs = [ + "instrumentationlibrary_test.cc", + ], + deps = [ + "//api", + "//sdk:headers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/sdk/test/instrumentationlibrary/CMakeLists.txt b/sdk/test/instrumentationlibrary/CMakeLists.txt new file mode 100644 index 0000000000..50c2728a8a --- /dev/null +++ b/sdk/test/instrumentationlibrary/CMakeLists.txt @@ -0,0 +1,12 @@ +include(GoogleTest) + +foreach(testname instrumentationlibrary_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX instrumentationlibrary. + TEST_LIST ${testname}) +endforeach() diff --git a/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc b/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc new file mode 100644 index 0000000000..a27ac0718f --- /dev/null +++ b/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc @@ -0,0 +1,34 @@ +// Copyright 2021, OpenTelemetry Authors +// +// 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 "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" + +#include +#include +#include + +using namespace opentelemetry; +using namespace opentelemetry::sdk::instrumentationlibrary; + +TEST(InstrumentationLibrary, CreateInstrumentationLibrary) +{ + + std::string library_name = "opentelemetry-cpp"; + std::string library_version = "0.1.0"; + auto instrumentation_library = InstrumentationLibrary::create(library_name, library_version); + + EXPECT_EQ(instrumentation_library->GetName(), library_name); + EXPECT_EQ(instrumentation_library->GetVersion(), library_version); +} diff --git a/sdk/test/trace/tracer_provider_test.cc b/sdk/test/trace/tracer_provider_test.cc index a1308827d5..cf241bc52c 100644 --- a/sdk/test/trace/tracer_provider_test.cc +++ b/sdk/test/trace/tracer_provider_test.cc @@ -24,8 +24,7 @@ TEST(TracerProvider, GetTracer) // Should return the same instance each time. ASSERT_EQ(t1, t2); - // TODO: t3 should be a different instance. - ASSERT_EQ(t1, t3); + ASSERT_NE(t1, t3); // Should be an sdk::trace::Tracer with the processor attached. auto sdkTracer1 = dynamic_cast(t1.get()); @@ -37,6 +36,16 @@ TEST(TracerProvider, GetTracer) std::unique_ptr(new RandomIdGenerator))); auto sdkTracer2 = dynamic_cast(tp2.GetTracer("test").get()); ASSERT_EQ("AlwaysOffSampler", sdkTracer2->GetSampler().GetDescription()); + + auto instrumentation_library1 = sdkTracer1->GetInstrumentationLibrary(); + ASSERT_EQ(instrumentation_library1.GetName(), "test"); + ASSERT_EQ(instrumentation_library1.GetVersion(), ""); + + // Should be an sdk::trace::Tracer with the processor attached. + auto sdkTracer3 = dynamic_cast(t3.get()); + auto instrumentation_library3 = sdkTracer3->GetInstrumentationLibrary(); + ASSERT_EQ(instrumentation_library3.GetName(), "different"); + ASSERT_EQ(instrumentation_library3.GetVersion(), "1.0.0"); } TEST(TracerProvider, Shutdown) @@ -55,4 +64,4 @@ TEST(TracerProvider, ForceFlush) TracerProvider tp1(std::move(processor1)); EXPECT_TRUE(tp1.ForceFlush()); -} \ No newline at end of file +} diff --git a/sdk/test/trace/tracer_test.cc b/sdk/test/trace/tracer_test.cc index 2fa064d4bf..3d1344a724 100644 --- a/sdk/test/trace/tracer_test.cc +++ b/sdk/test/trace/tracer_test.cc @@ -593,4 +593,4 @@ TEST(Tracer, ExpectParent) EXPECT_EQ(spandata_first->GetSpanId(), spandata_second->GetParentSpanId()); EXPECT_EQ(spandata_second->GetSpanId(), spandata_third->GetParentSpanId()); -} \ No newline at end of file +}