8000 [SDK] Implementation of container resource as per semconv · open-telemetry/opentelemetry-cpp@c740ddb · GitHub
[go: up one dir, main page]

Skip to content

Commit c740ddb

Browse files
[SDK] Implementation of container resource as per semconv
1 parent ac1172e commit c740ddb

File tree

8 files changed

+178
-2
lines changed

8 files changed

+178
-2
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#pragma once
5+
6+
#include <string>
7+
8+
#include "opentelemetry/version.h"
9+
10+
OPENTELEMETRY_BEGIN_NAMESPACE
11+
namespace sdk
12+
{
13+
namespace common
14+
{
15+
/**
16+
Reads the container.id from /proc/self/cgroup file.
17+
@param file_path file path of cgroup
18+
@return container.id as string or empty string
19+
*/
20+
std::string GetContainerIDFromCgroup(const char *file_path);
21+
22+
/**
23+
Matches the line with the regex to find container.id
24+
@param line contains
25+
@return matched id or empty string
26+
*/
27+
std::string ExtractContainerIDFromLine(std::string &line);
28+
} // namespace common
29+
} // namespace sdk
30+
OPENTELEMETRY_END_NAMESPACE

sdk/include/opentelemetry/sdk/resource/resource_detector.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ class OTELResourceDetector : public ResourceDetector
3939
Resource Detect() noexcept override;
4040
};
4141

42+
/**
43+
* ContainerResourceDetector to detect resource attributes when running inside a containerized environment.
44+
* This detector extracts metadata such as container ID from cgroup information
45+
* and sets attributes like container.id following the OpenTelemetry semantic conventions.
46+
*/
47+
class ContainerResourceDetector : public ResourceDetector
48+
{
49+
public:
50+
Resource Detect() noexcept override;
51+
};
52+
4253
} // namespace resource
4354
} // namespace sdk
4455
OPENTELEMETRY_END_NAMESPACE

sdk/src/common/BUILD

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,17 @@ cc_library(
5757
],
5858
)
5959

60+
cc_library(
61+
name = "container",
62+
srcs = [
63+
"container.cc",
64+
],
65+
deps = [
66+
"//api",
67+
"//sdk:headers",
68+
],
69+
)
70+
6071
cc_library(
6172
name = "global_log_handler",
6273
srcs = [

sdk/src/common/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright The OpenTelemetry Authors
22
# SPDX-License-Identifier: Apache-2.0
33

4-
set(COMMON_SRCS random.cc global_log_handler.cc env_variables.cc base64.cc
4+
set(COMMON_SRCS random.cc global_log_handler.cc env_variables.cc container.cc base64.cc
55
disabled.cc)
66
if(WIN32)
77
list(APPEND COMMON_SRCS platform/fork_windows.cc)

sdk/src/common/container.cc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#include "opentelemetry/sdk/common/container.h"
5+
6+
#ifdef _MSC_VER
7+
# include <string.h>
8+
# define strcasecmp _stricmp
9+
#else
10+
# include <strings.h>
11+
#endif
12+
13+
#include <fstream>
14+
#include <regex>
15+
16+
#include "opentelemetry/version.h"
17+
18+
OPENTELEMETRY_BEGIN_NAMESPACE
19+
namespace sdk
20+
{
21+
namespace common
22+
{
23+
std::string GetContainerIDFromCgroup(const char* file_path)
24+
{
25+
std::ifstream cgroup_file(file_path);
26+
std::string line;
27+
28+
while(std::getline(cgroup_file, line))
29+
{
30+
std::string container_id = ExtractContainerIDFromLine(line);
31+
if(!container_id.empty())
32+
{
33+
return container_id;
34+
}
35+
}
36+
return "";
37+
}
38+
39+
std::string ExtractContainerIDFromLine(std::string &line)
40+
{
41+
static const std::regex container_id_regex(R"(^.*/(?:.*[-:])?([0-9a-f]+)(?:\.|\s*$))");
42+
std::smatch match;
43+
44+
if (std::regex_search(line, match, container_id_regex))
45+
{
46+
return match.str(1);
47+
}
48+
49+
return "";
50+
}
51+
} // namespace common
52+
} // namespace sdk
53+
OPENTELEMETRY_END_NAMESPACE

sdk/src/resource/resource.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ Resource Resource::Merge(const Resource &other) const noexcept
4141
Resource Resource::Create(const ResourceAttributes &attributes, const std::string &schema_url)
4242
{
4343
static auto otel_resource = OTELResourceDetector().Detect();
44+
static auto container_resource = ContainerResourceDetector().Detect();
4445
auto resource =
45-
Resource::GetDefault().Merge(otel_resource).Merge(Resource{attributes, schema_url});
46+
Resource::GetDefault().Merge(otel_resource).Merge(container_resource).Merge(Resource{attributes, schema_url});
4647

4748
if (resource.attributes_.find(semconv::service::kServiceName) == resource.attributes_.end())
4849
{

sdk/src/resource/resource_detector.cc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
#include "opentelemetry/sdk/resource/resource_detector.h"
55
#include "opentelemetry/nostd/variant.h"
66
#include "opentelemetry/sdk/common/env_variables.h"
7+
#include "opentelemetry/sdk/common/container.h"
78
#include "opentelemetry/sdk/resource/resource.h"
89
#include "opentelemetry/semconv/service_attributes.h"
10+
#include "opentelemetry/semconv/incubating/container_attributes.h"
911
#include "opentelemetry/version.h"
1012

1113
#include <stddef.h>
@@ -21,6 +23,10 @@ namespace resource
2123

2224
const char *OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES";
2325
const char *OTEL_SERVICE_NAME = "OTEL_SERVICE_NAME";
26+
/**
27+
* This is the file path from where we can get container.id
28+
*/
29+
const char *C_GROUP_PATH = "/proc/self/cgroup";
2430

2531
Resource ResourceDetector::Create(const ResourceAttributes &attributes,
2632
const std::string &schema_url)
@@ -68,6 +74,23 @@ Resource OTELResourceDetector::Detect() noexcept
6874
return ResourceDetector::Create(attributes);
6975
}
7076

77+
Resource ContainerResourceDetector::Detect() noexcept
78+
{
79+
std::string container_id = opentelemetry::sdk::common::GetContainerIDFromCgroup(C_GROUP_PATH);
80+
if(container_id.empty())
81+
{
82+
return ResourceDetector::Create({});
83+
}
84+
85+
ResourceAttributes attributes;
86+
87+
if(!container_id.empty())
88+
{
89+
attributes[semconv::container::kContainerId] = container_id;
90+
}
91+
return ResourceDetector::Create(attributes);
92+
}
93+
7194
} // namespace resource
7295
} // namespace sdk
7396
OPENTELEMETRY_END_NAMESPACE

sdk/test/resource/resource_test.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
#include <string>
99
#include <unordered_map>
1010
#include <utility>
11+
#include<fstream>
1112

1213
#include "opentelemetry/nostd/variant.h"
1314
#include "opentelemetry/sdk/common/attribute_utils.h"
15+
#include "opentelemetry/sdk/common/container.h"
1416
#include "opentelemetry/sdk/resource/resource.h"
1517
#include "opentelemetry/sdk/resource/resource_detector.h"
1618
#include "opentelemetry/sdk/version/version.h"
@@ -292,3 +294,48 @@ TEST(ResourceTest, DerivedResourceDetector)
292294
EXPECT_EQ(resource.GetSchemaURL(), detector.schema_url);
293295
EXPECT_TRUE(received_attributes.find("key") != received_attributes.end());
294296
}
297+
298+
TEST(ResourceTest, ExctractValidContainerId)
299+
{
300+
std::string line = "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa";
301+
std::string extracted_id = opentelemetry::sdk::common::ExtractContainerIDFromLine(line);
302+
EXPECT_EQ(std::string{"ac679f8a8319c8cf7d38e1adf263bc08d23"}, extracted_id);
303+
}
304+
305+
TEST(ResourceTest, ExtractIdFromMockUpCGroupFile)
306+
{
307+
const char *filename = "test_cgroup.txt";
308+
309+
{
310+
std::ofstream outfile(filename);
311+
outfile << "13:name=systemd:/kuberuntime/containerd/kubepods-pod872d2066_00ef_48ea_a7d8_51b18b72d739:cri-containerd:e857a4bf05a69080a759574949d7a0e69572e27647800fa7faff6a05a8332aa1\n";
312+
outfile << "9:cpu:/not-a-container\n";
313+
}
314+
315+
std::string container_id = opentelemetry::sdk::common::GetContainerIDFromCgroup(filename);
316+
EXPECT_EQ(container_id, std::string{"e857a4bf05a69080a759574949d7a0e69572e27647800fa7faff6a05a8332aa1"});
317+
318+
std::remove(filename);
319+
}
320+
321+
TEST(ResourceTest, DoesNotExtractInvalidLine)
322+
{
323+
std::string line = "this line does not contain a container id";
324+
std::string id = opentelemetry::sdk::common::ExtractContainerIDFromLine(line);
325+
EXPECT_EQ(id, std::string{""});
326+
}
327+
328+
TEST(ContainerIdDetectorTest, ReturnsEmptyOnNoMatch)
329+
{
330+
const char *filename = "test_empty_cgroup.txt";
331+
332+
{
333+
std::ofstream outfile(filename);
334+
outfile << "no container id here\n";
335+
}
336+
337+
std::string id = opentelemetry::sdk::common::GetContainerIDFromCgroup(filename);
338+
EXPECT_EQ(id, std::string{""});
339+
340+
std::remove(filename); // cleanup
341+
}

0 commit comments

Comments
 (0)
0