diff --git a/CHANGELOG.md b/CHANGELOG.md index 49bfcfebf5..07c1ac222d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Increment the: ## [Unreleased] * Support environment variables for both `OtlpGrpcExporter` and `OtlpHttpExporter` ([#983](https://github.com/open-telemetry/opentelemetry-cpp/pull/983)) +* [API/SDK] Add schema_url support to both Resource and InstrumentationLibrary ([#979](https://github.com/open-telemetry/opentelemetry-cpp/pull/979)) ## [1.0.0] 2021-09-16 diff --git a/api/include/opentelemetry/trace/noop.h b/api/include/opentelemetry/trace/noop.h index 76c0fe0749..27cfaee4ad 100644 --- a/api/include/opentelemetry/trace/noop.h +++ b/api/include/opentelemetry/trace/noop.h @@ -103,9 +103,9 @@ class NoopTracerProvider final : public opentelemetry::trace::TracerProvider new opentelemetry::trace::NoopTracer)} {} - nostd::shared_ptr GetTracer( - nostd::string_view library_name, - nostd::string_view library_version) override + nostd::shared_ptr GetTracer(nostd::string_view library_name, + nostd::string_view library_version, + nostd::string_view schema_url) override { return tracer_; } diff --git a/api/include/opentelemetry/trace/tracer_provider.h b/api/include/opentelemetry/trace/tracer_provider.h index 04e83531c1..540a2f6b24 100644 --- a/api/include/opentelemetry/trace/tracer_provider.h +++ b/api/include/opentelemetry/trace/tracer_provider.h @@ -24,7 +24,8 @@ class TracerProvider * instance. */ virtual nostd::shared_ptr GetTracer(nostd::string_view library_name, - nostd::string_view library_version = "") = 0; + nostd::string_view library_version = "", + nostd::string_view schema_url = "") = 0; }; } // namespace trace OPENTELEMETRY_END_NAMESPACE diff --git a/api/test/trace/provider_test.cc b/api/test/trace/provider_test.cc index 49781e5e48..ca4555535c 100644 --- a/api/test/trace/provider_test.cc +++ b/api/test/trace/provider_test.cc @@ -14,7 +14,8 @@ class TestProvider : public TracerProvider { opentelemetry::nostd::shared_ptr GetTracer( opentelemetry::nostd::string_view library_name, - opentelemetry::nostd::string_view library_version) override + opentelemetry::nostd::string_view library_version, + opentelemetry::nostd::string_view schema_url) override { return opentelemetry::nostd::shared_ptr(nullptr); } diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index 47e6fa79c4..94875228ef 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -1131,9 +1131,11 @@ class TracerProvider : public trace::TracerProvider * @return */ nostd::shared_ptr GetTracer(nostd::string_view name, - nostd::string_view args = "") override + nostd::string_view args = "", + nostd::string_view schema_url = "") override { UNREFERENCED_PARAMETER(args); + UNREFERENCED_PARAMETER(schema_url); ETWProvider::EventFormat evtFmt = config_.encoding; return nostd::shared_ptr{new (std::nothrow) Tracer(*this, name, evtFmt)}; } diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h index a2e9c83ae3..dbb599880e 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h @@ -25,6 +25,9 @@ class OtlpRecordable final : public sdk::trace::Recordable /** Dynamically converts the resource of this span into a proto. */ proto::resource::v1::Resource ProtoResource() const noexcept; + const std::string GetResourceSchemaURL() const noexcept; + const std::string GetInstrumentationLibrarySchemaURL() const noexcept; + proto::common::v1::InstrumentationLibrary GetProtoInstrumentationLibrary() const noexcept; void SetIdentity(const opentelemetry::trace::SpanContext &span_context, diff --git a/exporters/otlp/src/otlp_grpc_exporter.cc b/exporters/otlp/src/otlp_grpc_exporter.cc index a9080cfa39..72b97541dc 100644 --- a/exporters/otlp/src/otlp_grpc_exporter.cc +++ b/exporters/otlp/src/otlp_grpc_exporter.cc @@ -27,7 +27,7 @@ void PopulateRequest(const nostd::span> { auto resource_span = request->add_resource_spans(); auto instrumentation_lib = resource_span->add_instrumentation_library_spans(); - bool has_resource = false; + bool first_pass = true; for (auto &recordable : spans) { @@ -35,10 +35,14 @@ void PopulateRequest(const nostd::span> *instrumentation_lib->add_spans() = std::move(rec->span()); *instrumentation_lib->mutable_instrumentation_library() = rec->GetProtoInstrumentationLibrary(); - if (!has_resource) + if (first_pass) { - *resource_span->mutable_resource() = rec->ProtoResource(); - has_resource = true; + *instrumentation_lib->mutable_schema_url() = rec->GetInstrumentationLibrarySchemaURL(); + + *resource_span->mutable_resource() = rec->ProtoResource(); + *resource_span->mutable_schema_url() = rec->GetResourceSchemaURL(); + + first_pass = false; } } } diff --git a/exporters/otlp/src/otlp_recordable.cc b/exporters/otlp/src/otlp_recordable.cc index 8562ecc7ae..b319a5ea9c 100644 --- a/exporters/otlp/src/otlp_recordable.cc +++ b/exporters/otlp/src/otlp_recordable.cc @@ -237,6 +237,28 @@ proto::resource::v1::Resource OtlpRecordable::ProtoResource() const noexcept return proto; } +const std::string OtlpRecordable::GetResourceSchemaURL() const noexcept +{ + std::string schema_url; + if (resource_) + { + schema_url = resource_->GetSchemaURL(); + } + + return schema_url; +} + +const std::string OtlpRecordable::GetInstrumentationLibrarySchemaURL() const noexcept +{ + std::string schema_url; + if (instrumentation_library_) + { + schema_url = instrumentation_library_->GetSchemaURL(); + } + + return schema_url; +} + proto::common::v1::InstrumentationLibrary OtlpRecordable::GetProtoInstrumentationLibrary() const noexcept { diff --git a/exporters/otlp/test/otlp_recordable_test.cc b/exporters/otlp/test/otlp_recordable_test.cc index bc5e76bac0..ddefe58843 100644 --- a/exporters/otlp/test/otlp_recordable_test.cc +++ b/exporters/otlp/test/otlp_recordable_test.cc @@ -63,6 +63,16 @@ TEST(OtlpRecordable, SetInstrumentationLibrary) EXPECT_EQ(proto_instr_libr.version(), inst_lib->GetVersion()); } +TEST(OtlpRecordable, SetInstrumentationLibraryWithSchemaURL) +{ + OtlpRecordable rec; + const std::string expected_schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto inst_lib = + opentelemetry::sdk::trace::InstrumentationLibrary::Create("test", "v1", expected_schema_url); + rec.SetInstrumentationLibrary(*inst_lib); + EXPECT_EQ(expected_schema_url, rec.GetInstrumentationLibrarySchemaURL()); +} + TEST(OtlpRecordable, SetStartTime) { OtlpRecordable rec; @@ -198,6 +208,19 @@ TEST(OtlpRecordable, SetResource) EXPECT_TRUE(found_service_name); } +TEST(OtlpRecordable, SetResourceWithSchemaURL) +{ + OtlpRecordable rec; + const std::string service_name_key = "service.name"; + const std::string service_name = "test-otlp"; + const std::string expected_schema_url = "https://opentelemetry.io/schemas/1.2.0"; + auto resource = opentelemetry::sdk::resource::Resource::Create({{service_name_key, service_name}}, + expected_schema_url); + rec.SetResource(resource); + + EXPECT_EQ(expected_schema_url, rec.GetResourceSchemaURL()); +} + // Test non-int single types. Int single types are tested using templates (see IntAttributeTest) TEST(OtlpRecordable, SetSingleAtrribute) { diff --git a/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h b/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h index e4f7f78ef1..eeea6cd7aa 100644 --- a/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h +++ b/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h @@ -23,13 +23,15 @@ class InstrumentationLibrary * 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. + * @param schema_url schema url of the telemetry emitted by the library. * @returns the newly created InstrumentationLibrary. */ static nostd::unique_ptr Create(nostd::string_view name, - nostd::string_view version = "") + nostd::string_view version = "", + nostd::string_view schema_url = "") { - return nostd::unique_ptr( - new InstrumentationLibrary{std::string{name}, std::string{version}}); + return nostd::unique_ptr(new InstrumentationLibrary{ + std::string{name}, std::string{version}, std::string{schema_url}}); } /** @@ -39,7 +41,7 @@ class InstrumentationLibrary */ bool operator==(const InstrumentationLibrary &other) const { - return equal(other.name_, other.version_); + return equal(other.name_, other.version_, other.schema_url_); } /** @@ -47,25 +49,32 @@ class InstrumentationLibrary * 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. + * @param schema_url schema url of the telemetry emitted by the library. * @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 + bool equal(const nostd::string_view name, + const nostd::string_view version, + const nostd::string_view schema_url = "") const { - return this->name_ == name && this->version_ == version; + return this->name_ == name && this->version_ == version && this->schema_url_ == schema_url; } const std::string &GetName() const { return name_; } const std::string &GetVersion() const { return version_; } + const std::string &GetSchemaURL() const { return schema_url_; } private: - InstrumentationLibrary(nostd::string_view name, nostd::string_view version) - : name_(name), version_(version) + InstrumentationLibrary(nostd::string_view name, + nostd::string_view version, + nostd::string_view schema_url = "") + : name_(name), version_(version), schema_url_(schema_url) {} private: std::string name_; std::string version_; + std::string schema_url_; }; } // namespace instrumentationlibrary diff --git a/sdk/include/opentelemetry/sdk/resource/resource.h b/sdk/include/opentelemetry/sdk/resource/resource.h index 03aebcd84a..120e871ab5 100644 --- a/sdk/include/opentelemetry/sdk/resource/resource.h +++ b/sdk/include/opentelemetry/sdk/resource/resource.h @@ -29,6 +29,7 @@ class Resource Resource(const Resource &) = default; const ResourceAttributes &GetAttributes() const noexcept; + const std::string &GetSchemaURL() const noexcept; /** * Returns a new, merged {@link Resource} by merging the current Resource @@ -48,7 +49,8 @@ class Resource * @returns the newly created Resource. */ - static Resource Create(const ResourceAttributes &attributes); + static Resource Create(const ResourceAttributes &attributes, + const std::string &schema_url = std::string{}); /** * Returns an Empty resource. @@ -69,14 +71,16 @@ class Resource * Users should use the Create factory method to obtain a Resource * instance. */ - Resource(const ResourceAttributes &attributes = ResourceAttributes()) noexcept; + Resource(const ResourceAttributes &attributes = ResourceAttributes(), + const std::string &schema_url = std::string{}) noexcept; private: ResourceAttributes attributes_; + std::string schema_url_; friend class OTELResourceDetector; }; } // namespace resource } // namespace sdk -OPENTELEMETRY_END_NAMESPACE \ No newline at end of file +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h index ec825b6e3c..9ff35e0116 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -61,7 +61,8 @@ class TracerProvider final : public opentelemetry::trace::TracerProvider opentelemetry::nostd::shared_ptr GetTracer( nostd::string_view library_name, - nostd::string_view library_version = "") noexcept override; + nostd::string_view library_version = "", + nostd::string_view schema_url = "") noexcept override; /** * Attaches a span processor to list of configured processors for this tracer provider. diff --git a/sdk/src/resource/resource.cc b/sdk/src/resource/resource.cc index 930e8e6640..edfb49efc1 100644 --- a/sdk/src/resource/resource.cc +++ b/sdk/src/resource/resource.cc @@ -19,19 +19,22 @@ const std::string kTelemetrySdkVersion = "telemetry.sdk.version"; const std::string kServiceName = "service.name"; const std::string kProcessExecutableName = "process.executable.name"; -Resource::Resource(const ResourceAttributes &attributes) noexcept : attributes_(attributes) {} +Resource::Resource(const ResourceAttributes &attributes, const std::string &schema_url) noexcept + : attributes_(attributes), schema_url_(schema_url) +{} Resource Resource::Merge(const Resource &other) noexcept { ResourceAttributes merged_resource_attributes(other.attributes_); merged_resource_attributes.insert(attributes_.begin(), attributes_.end()); - return Resource(merged_resource_attributes); + return Resource(merged_resource_attributes, other.schema_url_); } -Resource Resource::Create(const ResourceAttributes &attributes) +Resource Resource::Create(const ResourceAttributes &attributes, const std::string &schema_url) { static auto otel_resource = OTELResourceDetector().Detect(); - auto resource = Resource::GetDefault().Merge(otel_resource).Merge(Resource(attributes)); + auto resource = + Resource::GetDefault().Merge(otel_resource).Merge(Resource{attributes, schema_url}); if (resource.attributes_.find(OTEL_CPP_GET_ATTR(AttrServiceName)) == resource.attributes_.end()) { @@ -58,7 +61,8 @@ Resource &Resource::GetDefault() static Resource default_resource( {{OTEL_CPP_GET_ATTR(AttrTelemetrySdkLanguage), "cpp"}, {OTEL_CPP_GET_ATTR(AttrTelemetrySdkName), "opentelemetry"}, - {OTEL_CPP_GET_ATTR(AttrTelemetrySdkVersion), OPENTELEMETRY_SDK_VERSION}}); + {OTEL_CPP_GET_ATTR(AttrTelemetrySdkVersion), OPENTELEMETRY_SDK_VERSION}}, + std::string{}); return default_resource; } @@ -67,6 +71,11 @@ const ResourceAttributes &Resource::GetAttributes() const noexcept return attributes_; } +const std::string &Resource::GetSchemaURL() const noexcept +{ + return schema_url_; +} + } // namespace resource } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index 911e66c2f0..13628094e4 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -35,13 +35,15 @@ TracerProvider::TracerProvider(std::vector> &&pro nostd::shared_ptr TracerProvider::GetTracer( nostd::string_view library_name, - nostd::string_view library_version) noexcept + nostd::string_view library_version, + nostd::string_view schema_url) noexcept { if (library_name.data() == nullptr) { + OTEL_INTERNAL_LOG_ERROR("[TracerProvider::GetTracer] Library name is null."); library_name = ""; } - if (library_name == "") + else if (library_name == "") { OTEL_INTERNAL_LOG_ERROR("[TracerProvider::GetTracer] Library name is empty."); } @@ -51,13 +53,13 @@ nostd::shared_ptr TracerProvider::GetTracer( for (auto &tracer : tracers_) { auto &tracer_lib = tracer->GetInstrumentationLibrary(); - if (tracer_lib.equal(library_name, library_version)) + if (tracer_lib.equal(library_name, library_version, schema_url)) { return nostd::shared_ptr{tracer}; } } - auto lib = InstrumentationLibrary::Create(library_name, library_version); + auto lib = InstrumentationLibrary::Create(library_name, library_version, schema_url); tracers_.push_back(std::shared_ptr( new sdk::trace::Tracer(context_, std::move(lib)))); return nostd::shared_ptr{tracers_.back()}; diff --git a/sdk/test/resource/resource_test.cc b/sdk/test/resource/resource_test.cc index 7f00ddaf48..37abb1d0fc 100644 --- a/sdk/test/resource/resource_test.cc +++ b/sdk/test/resource/resource_test.cc @@ -28,7 +28,6 @@ class TestResource : public Resource TEST(ResourceTest, create_without_servicename) { - ResourceAttributes expected_attributes = { {"service", "backend"}, {"version", (uint32_t)1}, @@ -112,6 +111,17 @@ TEST(ResourceTest, create_with_emptyatrributes) } EXPECT_EQ(received_attributes.size(), expected_attributes.size()); // for missing service.name } + +TEST(ResourceTest, create_with_schemaurl) +{ + const std::string schema_url = "https://opentelemetry.io/schemas/1.2.0"; + ResourceAttributes attributes = {}; + auto resource = Resource::Create(attributes, schema_url); + auto received_schema_url = resource.GetSchemaURL(); + + EXPECT_EQ(received_schema_url, schema_url); +} + TEST(ResourceTest, Merge) { TestResource resource1(ResourceAttributes({{"service", "backend"}})); diff --git a/sdk/test/trace/tracer_provider_test.cc b/sdk/test/trace/tracer_provider_test.cc index 444b416a39..9c1c922c2c 100644 --- a/sdk/test/trace/tracer_provider_test.cc +++ b/sdk/test/trace/tracer_provider_test.cc @@ -26,14 +26,17 @@ TEST(TracerProvider, GetTracer) auto t3 = tp1.GetTracer("different", "1.0.0"); auto t4 = tp1.GetTracer(""); auto t5 = tp1.GetTracer(opentelemetry::nostd::string_view{}); + auto t6 = tp1.GetTracer("different", "1.0.0", "https://opentelemetry.io/schemas/1.2.0"); ASSERT_NE(nullptr, t1); ASSERT_NE(nullptr, t2); ASSERT_NE(nullptr, t3); + ASSERT_NE(nullptr, t6); // Should return the same instance each time. ASSERT_EQ(t1, t2); ASSERT_NE(t1, t3); ASSERT_EQ(t4, t5); + ASSERT_NE(t3, t6); // Should be an sdk::trace::Tracer with the processor attached. auto sdkTracer1 = dynamic_cast(t1.get());