Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding semantic-convention attributes for resources #872

Merged
merged 8 commits into from
Jun 24, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions exporters/jaeger/src/recordable.cc
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include <opentelemetry/exporters/jaeger/recordable.h>
#include "opentelemetry/exporters/jaeger/recordable.h"
#include "opentelemetry/sdk/resource/semantic_conventions.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace exporter
{
namespace jaeger
{

using namespace opentelemetry::sdk::resource;

Recordable::Recordable() : span_{new thrift::Span} {}

void Recordable::PopulateAttribute(nostd::string_view key, const common::AttributeValue &value)
Expand Down Expand Up @@ -117,9 +120,10 @@ void Recordable::SetResource(const opentelemetry::sdk::resource::Resource &resou
{
// only service.name attribute is supported by specs as of now.
auto attributes = resource.GetAttributes();
if (attributes.find("service.name") != attributes.end())
if (attributes.find(SemanticConventions::GetAttributeSericeName()) != attributes.end())
lalitb marked this conversation as resolved.
Show resolved Hide resolved
{
service_name_ = nostd::get<std::string>(attributes["service.name"]);
service_name_ =
nostd::get<std::string>(attributes[SemanticConventions::GetAttributeSericeName()]);
}
}

Expand Down
8 changes: 6 additions & 2 deletions exporters/zipkin/src/recordable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

#include "opentelemetry/exporters/zipkin/recordable.h"
#include "opentelemetry/sdk/resource/semantic_conventions.h"

#include <map>
#include <string>
Expand All @@ -12,6 +13,8 @@ namespace exporter
namespace zipkin
{

using namespace opentelemetry::sdk::resource;

// constexpr needs keys to be constexpr, const is next best to use.
static const std::map<opentelemetry::trace::SpanKind, std::string> kSpanKindMap = {
{opentelemetry::trace::SpanKind::kClient, "CLIENT"},
Expand Down Expand Up @@ -208,9 +211,10 @@ void Recordable::SetResource(const opentelemetry::sdk::resource::Resource &resou
{
// only service.name attribute is supported by specs as of now.
auto attributes = resource.GetAttributes();
if (attributes.find("service.name") != attributes.end())
if (attributes.find(SemanticConventions::GetAttributeServiceName()) != attributes.end())
{
service_name_ = nostd::get<std::string>(attributes["service.name"]);
service_name_ =
nostd::get<std::string>(attributes[SemanticConventions::GetAttributeServiceName()]);
}
}

Expand Down
136 changes: 136 additions & 0 deletions sdk/include/opentelemetry/sdk/resource/semantic_conventions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "opentelemetry/version.h"

#define OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(attribute_name, attribute_value) \
static constexpr const char *GetAttribute##attribute_name() noexcept { return attribute_value; }

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace resource
{
/**
* Stores the Constants for semantic Attribute names for resources outlined by the OpenTelemetry
* specifications. <see
* href,"https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/README.md"/>.
*/

class SemanticConventions final
{

public:
// service attributes
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ServiceName, "service.name");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do this instead. Take this compile-time hash function:

/// <summary>
/// Compile-time constexpr djb2 hash function for strings
/// </summary>
static constexpr uint32_t hashCode(const char *str, uint32_t h = 0)
{
  return (uint32_t)(!str[h] ? 5381 : ((uint32_t)hashCode(str, h + 1) * (uint32_t)33) ^ str[h]);
}

#define CONST_UINT32_T(x) std::integral_constant<uint32_t, (uint32_t)x>::value

#define CONST_HASHCODE(name) CONST_UINT32_T(OPENTELEMETRY_NAMESPACE::utils::hashCode(#name))

Then for every attribute, generate a table of values. At compile time - by listing all of them. Sorry for using this example - something like this:

// Hash function below is computing module tag from literal at compile-time using C++11 constexpr
#define DBG_MODULE_CTAG(name)       CONST_UINT32_T(djb2_constexpr_hash(#name))
#define DBG_MODULE_TAG(name)        djb2_hash(name)
#define DBG_LOG_NAMETAG(name)       { DBG_MODULE_CTAG(name) , #name } ,

So - you created a compile time table that maps your attribute names to unique IDs. You can still refer to original attributes by their aliases. Like ATTR_ServiceName, macro-magic permitting.

The goal is that you can do tricks like switch-case, constexpr, and mapping from attribute to unique attribute id. Then instead of many methods, you end up with attr(name) or attr(id). That's how Qt5 tr() works, and how translations / resources work in Android pretty much. Except that tr() requires string literal, and Android resources are all revolving around getResource(id).

In our model, I think, it'd be more appropriate to perform compile-time mapping from attr("my.attribute.name") -> returns actual attribute name within given semantic convention. Not many getters. One getter. Lookup table. And preferably, compile-time lookup, not method call.

OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ServiceNamespace, "service.namespace");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ServiceInstance, "service.instance.id");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ServiceVersion, "service.version ");

// telemetry attributes
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(TelemetrySdkName, "telemetry.sdk.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(TelemetrySdkLanguage, "telemetry.sdk.language");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(TelemetrySdkVersion, "telemetry.sdk.version");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(TelemetryAutoVersion, "telemetry.auto.version");

// compute unit: container attributes
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ContainerName, "container.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ContainerId, "container.id");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ContainerRuntime, "container.runtime");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ContainerImageName, "container.image.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ContainerImageTag, "container.image.tag");

// compute unit: faas attributes
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(FaasName, "faas.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(FaasId, "faas.id");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(FaasVersion, "faas.version");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(FaasInstance, "faas.instance");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(FaasMaxMemory, "faas.max_memory");

// compute unit : process attributes
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ProcessId, "process.pid");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ProcessExecutableName, "process.executable.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ProcessExecutablePath, "process.executable.path");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ProcessCommand, "process.command");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ProcessCommandLine, "process.command_line");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ProcessCommandArgs, "process.command_args");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ProcessOwner, "process.owner");

// compute : process runtimes
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ProcessRuntimeName, "process.runtime.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ProcessRuntimeVersion, "process.runtime.version");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(ProcessRuntimeDescription, "process.runtime.description");

// compute unit : WebEngine
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(WebEngineName, "webengine.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(WebEngineVersion, "webengine.version");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(WebEngineDescription, "webengine.description");

// compute instance : host
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(HostId, "host.id");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(HostName, "host.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(HostType, "host.type");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(HostArch, "host.arch");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(HostImageName, "host.image.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(HostImageId, "host.image.id");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(HostImageVersion, "host.image.version");

// env os attributes
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(OsType, "os.type");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(OsDescription, "os.description");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(OsName, "os.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(OsVersion, "os.version");

// env device attributes
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(DeviceId, "device.id");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(DeviceModelIdentifier, "device.model.identifier");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(DeviceModelName, "device.model.name");

// env cloud
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(CloudProvider, "cloud.provider");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(CloudAccountId, "cloud.account.id");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(CloudRegion, "cloud.region");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(CloudAvailabilityZone, "cloud.availability_zone");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(CloudPlatform, "cloud.platform");

// env deployment
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(DeploymentEnvironment, "deployment.environment");

// env kubernetes
// - cluster
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sClusterName, "k8s.cluster.name");
// - node
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sNodeName, "k8s.node.name");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sNodeUid, "k8s.node.uid");
// - namespace
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sNamespaceName, "k8s.namespace.name");
// - pod
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sPodUid, "k8s.pod.uid");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sPodName, "k8s.pod.name");
// - container
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sContainerName, "k8s.container.name");
// - replicaset
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sReplicaSetUid, "k8s.replicaset.uid");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sReplicaSetName, "k8s.replicaset.name");
// - deployment
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sDeploymentUid, "k8s.deployment.uid");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sDeploymentName, "k8s.deployment.name");
// - stateful-set
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sStatefulSetUid, "k8s.statefulset.uid");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sStatefulSetName, "k8s.statefulset.name");
// - daemon set
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sDaemonSetUid, "k8s.daemonset.uid");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sDaemonSetName, "k8s.daemonset.name");
// - job
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sJobUid, "k8s.job.uid");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(K8sJobName, "k8s.job.name");
// - cronjob
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(CronjobUid, "k8s.cronjob.id");
OTEL_RESOURCE_GENERATE_SEMCONV_METHOD(CronjobName, "k8s.cronjob.name");
};

} // namespace resource
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
16 changes: 10 additions & 6 deletions sdk/src/resource/resource.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/nostd/span.h"
#include "opentelemetry/sdk/resource/resource_detector.h"
#include "opentelemetry/sdk/resource/semantic_conventions.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand Down Expand Up @@ -32,15 +33,17 @@ Resource Resource::Create(const ResourceAttributes &attributes)
static auto otel_resource = OTELResourceDetector().Detect();
auto resource = Resource::GetDefault().Merge(otel_resource).Merge(Resource(attributes));

if (resource.attributes_.find(kServiceName) == resource.attributes_.end())
if (resource.attributes_.find(SemanticConventions::GetAttributeServiceName()) ==
resource.attributes_.end())
{
std::string default_service_name = "unknown_service";
auto it_process_executable_name = resource.attributes_.find(kProcessExecutableName);
auto it_process_executable_name =
resource.attributes_.find(SemanticConventions::GetAttributeProcessExecutableName());
if (it_process_executable_name != resource.attributes_.end())
{
default_service_name += ":" + nostd::get<std::string>(it_process_executable_name->second);
}
resource.attributes_[kServiceName] = default_service_name;
resource.attributes_[SemanticConventions::GetAttributeServiceName()] = default_service_name;
}
return resource;
}
Expand All @@ -53,9 +56,10 @@ Resource &Resource::GetEmpty()

Resource &Resource::GetDefault()
{
static Resource default_resource({{kTelemetrySdkLanguage, "cpp"},
{kTelemetrySdkName, "opentelemetry"},
{kTelemetrySdkVersion, OPENTELEMETRY_SDK_VERSION}});
static Resource default_resource(
{{SemanticConventions::GetAttributeTelemetrySdkLanguage(), "cpp"},
{SemanticConventions::GetAttributeTelemetrySdkName(), "opentelemetry"},
{SemanticConventions::GetAttributeTelemetrySdkVersion(), OPENTELEMETRY_SDK_VERSION}});
return default_resource;
}

Expand Down
64 changes: 31 additions & 33 deletions sdk/test/resource/resource_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
#include "opentelemetry/sdk/resource/resource_detector.h"
#include "opentelemetry/sdk/resource/semantic_conventions.h"

#include <cstdlib>
#include <string>
Expand All @@ -17,30 +18,29 @@
# define putenv _putenv
#endif

class TestResource : public opentelemetry::sdk::resource::Resource
using namespace opentelemetry::sdk::resource;

class TestResource : public Resource
{
public:
TestResource(opentelemetry::sdk::resource::ResourceAttributes attributes =
opentelemetry::sdk::resource::ResourceAttributes())
: Resource(attributes)
{}
TestResource(ResourceAttributes attributes = ResourceAttributes()) : Resource(attributes) {}
};

TEST(ResourceTest, create_without_servicename)
{

opentelemetry::sdk::resource::ResourceAttributes expected_attributes = {
ResourceAttributes expected_attributes = {
{"service", "backend"},
{"version", (uint32_t)1},
{"cost", 234.23},
{"telemetry.sdk.language", "cpp"},
{"telemetry.sdk.name", "opentelemetry"},
{"telemetry.sdk.version", OPENTELEMETRY_SDK_VERSION},
{"service.name", "unknown_service"}};
{SemanticConventions::GetAttributeTelemetrySdkLanguage(), "cpp"},
Copy link
Contributor

@maxgolov maxgolov Jun 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly would prefer an API model similar to Qt5 "translation resources", which solves the same problem - just in a different knowledge domain. More on that here.

To elaborate:

  • let's say you have a namespace called MyAttributeNames , and either constexpr or extern that looks like Qt5 tr(name) ... Let's name it attr(name)

  • then in order to obtain the actual name for a given system / naming convention, instead of SemanticConventions::GetAttributeTelemetrySdkLanguage() , you'd write attr("sdk.language") .... Moreover, since you are not gonna be using all attributes at once, it is Okay to declare these as literals, e.g. constexpr const char[] OTEL_ATTR_SDK_LANGUAGE = "sdk.language"; - so the overhead of having these as static inline constexpr is pay-per-play. You'd only use what you use. And in msvc you can use string pooling to merge identical strings.

That way instead of having N getters, which will also present a challenge - should you ever need to project this C++ code to C#, Python or Javascript/Node, etc... you'd have ONE attribute semantic convention "translation" method - called AttributeNames::attr(const char * name) and you'd invoke it as:

using namespace AttributeNames;
...
{attr(OTEL_ATTR_SDK_LANGUAGE), "cpp"}
... // or
{attr("sdk.language"), "cpp"}

Then we should strive the transform to be done at compile time, without generating those 50 methods. It does not scale. It'd scale nicely if you have one method / one utility function, hopefully constexpr. And a table of literals, resources in a classic definition of this world in C++ (as in translation resources, resource compiler, etc.).

Again, tr() from Qt5 seems like the most reasonable way to achieve that.

If you want to enforce , such as - if attribute has not been found in the lookup table, fail compilation (not allowing users to pass invalid semantic convention parameters) - we can use a constexpr hash function that calculates a hash of an argument string. And verifies that the value passed to attr is one of allowed values. You can also do this with constexpr, if you pre-generate the table. Which you should generate anyways.

The model with attr() would also scale nicely, should there be new conventions or new attributes added. Polluting namespace with 50 getters just won't scale.

{SemanticConventions::GetAttributeTelemetrySdkName(), "opentelemetry"},
{SemanticConventions::GetAttributeTelemetrySdkVersion(), OPENTELEMETRY_SDK_VERSION},
{SemanticConventions::GetAttributeServiceName(), "unknown_service"}};

opentelemetry::sdk::resource::ResourceAttributes attributes = {
ResourceAttributes attributes = {
{"service", "backend"}, {"version", (uint32_t)1}, {"cost", 234.23}};
auto resource = opentelemetry::sdk::resource::Resource::Create(attributes);
auto resource = Resource::Create(attributes);
auto received_attributes = resource.GetAttributes();
for (auto &e : received_attributes)
{
Expand All @@ -61,17 +61,17 @@ TEST(ResourceTest, create_without_servicename)

TEST(ResourceTest, create_with_servicename)
{
opentelemetry::sdk::resource::ResourceAttributes expected_attributes = {
ResourceAttributes expected_attributes = {
{"version", (uint32_t)1},
{"cost", 234.23},
{"telemetry.sdk.language", "cpp"},
{"telemetry.sdk.name", "opentelemetry"},
{"telemetry.sdk.version", OPENTELEMETRY_SDK_VERSION},
{"service.name", "backend"},
{SemanticConventions::GetAttributeTelemetrySdkLanguage(), "cpp"},
Copy link
Contributor

@maxgolov maxgolov Jun 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually to elaborate on my idea - you probably do not even need to change anything. Keep passing original string literals. Maybe replace them by constants, such as OTEL_ATTR_SDK_LANG. That's it. And even the original form of constant - should be fine.

Then we have an assignment operator in the ResourceAttributes implementation, that could have done the semantic transformation depending on what exporter / system is being used. Then you'd just completely avoid calling getters. You'd pre-configure your ResourceAttributes by specifying destination convention (like, imagine - your ResourceAttributes class can translate from English to French, and you pass in English keys, but they get magically translated to French, because your system "locale" is French).

If you need concrete example, I can send some WIP code to illustrate the model?

Copy link
Member Author

@lalitb lalitb Jun 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @maxgolov for the elaboration.

Copy link
Contributor

@maxgolov maxgolov Jun 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can definitely avoid getters. OR - at least, it all boils down to a single getter method (attr(...) or GetResourceName(...) or resName(...)) - that allows to transform from schema-agnostic common attribute name or attribute id to semantic- / schema- specific attribute name.

I feel like more concrete code example (based off above constexpr hash calculation) would be helpful?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maxgolov I have done the changes (hopefully) as per your suggestion :)
Have kept both the alternatives for now so that we can compare both and finalize.
semantic_conventions.h -> the suggested change.
semantic_conventions_old.h -> Initial implementation

{SemanticConventions::GetAttributeTelemetrySdkName(), "opentelemetry"},
{SemanticConventions::GetAttributeTelemetrySdkVersion(), OPENTELEMETRY_SDK_VERSION},
{SemanticConventions::GetAttributeServiceName(), "backend"},
};
opentelemetry::sdk::resource::ResourceAttributes attributes = {
ResourceAttributes attributes = {
{"service.name", "backend"}, {"version", (uint32_t)1}, {"cost", 234.23}};
auto resource = opentelemetry::sdk::resource::Resource::Create(attributes);
auto resource = Resource::Create(attributes);
auto received_attributes = resource.GetAttributes();
for (auto &e : received_attributes)
{
Expand All @@ -94,15 +94,15 @@ TEST(ResourceTest, create_with_servicename)

TEST(ResourceTest, create_with_emptyatrributes)
{
opentelemetry::sdk::resource::ResourceAttributes expected_attributes = {
{"telemetry.sdk.language", "cpp"},
{"telemetry.sdk.name", "opentelemetry"},
{"telemetry.sdk.version", OPENTELEMETRY_SDK_VERSION},
{"service.name", "unknown_service"},
ResourceAttributes expected_attributes = {
{SemanticConventions::GetAttributeTelemetrySdkLanguage(), "cpp"},
{SemanticConventions::GetAttributeTelemetrySdkName(), "opentelemetry"},
{SemanticConventions::GetAttributeTelemetrySdkVersion(), OPENTELEMETRY_SDK_VERSION},
{SemanticConventions::GetAttributeServiceName(), "unknown_service"},
};
opentelemetry::sdk::resource::ResourceAttributes attributes = {};
auto resource = opentelemetry::sdk::resource::Resource::Create(attributes);
auto received_attributes = resource.GetAttributes();
ResourceAttributes attributes = {};
auto resource = Resource::Create(attributes);
auto received_attributes = resource.GetAttributes();
for (auto &e : received_attributes)
{
EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end());
Expand All @@ -114,10 +114,8 @@ TEST(ResourceTest, create_with_emptyatrributes)
}
TEST(ResourceTest, Merge)
{
TestResource resource1(
opentelemetry::sdk::resource::ResourceAttributes({{"service", "backend"}}));
TestResource resource2(
opentelemetry::sdk::resource::ResourceAttributes({{"host", "service-host"}}));
TestResource resource1(ResourceAttributes({{"service", "backend"}}));
TestResource resource2(ResourceAttributes({{"host", "service-host"}}));
std::map<std::string, std::string> expected_attributes = {{"service", "backend"},
{"host", "service-host"}};

Expand Down Expand Up @@ -164,7 +162,7 @@ TEST(ResourceTest, OtelResourceDetector)
char env[] = "OTEL_RESOURCE_ATTRIBUTES=k=v";
putenv(env);

opentelemetry::sdk::resource::OTELResourceDetector detector;
OTELResourceDetector detector;
auto resource = detector.Detect();
auto received_attributes = resource.GetAttributes();
for (auto &e : received_attributes)
Expand Down Expand Up @@ -192,7 +190,7 @@ TEST(ResourceTest, OtelResourceDetectorEmptyEnv)
#else
unsetenv("OTEL_RESOURCE_ATTRIBUTES");
#endif
opentelemetry::sdk::resource::OTELResourceDetector detector;
OTELResourceDetector detector;
auto resource = detector.Detect();
auto received_attributes = resource.GetAttributes();
for (auto &e : received_attributes)
Expand Down