Skip to content

Commit fab6a41

Browse files
[SDK] Implementation of container resource as per semconv (#3572)
1 parent 5f6d99f commit fab6a41

File tree

12 files changed

+329
-4
lines changed

12 files changed

+329
-4
lines changed

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ option(WITH_METRICS_EXEMPLAR_PREVIEW
261261
option(WITH_THREAD_INSTRUMENTATION_PREVIEW
262262
"Whether to enable thread instrumentation" OFF)
263263

264+
option(WITH_RESOURCE_DETECTORS_PREVIEW
265+
"Whether to enable inbuilt resource detectors" OFF)
266+
264267
option(OPENTELEMETRY_SKIP_DYNAMIC_LOADING_TESTS
265268
"Whether to build test libraries that are always linked as shared libs"
266269
OFF)
@@ -645,6 +648,9 @@ if(NOT WITH_API_ONLY)
645648
add_subdirectory(sdk)
646649
add_subdirectory(ext)
647650
add_subdirectory(exporters)
651+
if(WITH_RESOURCE_DETECTORS_PREVIEW)
652+
add_subdirectory(resource_detectors)
653+
endif()
648654

649655
if(BUILD_TESTING)
650656
add_subdirectory(test_common)

resource_detectors/BUILD

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright The OpenTelemetry Authors
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
package(default_visibility = ["//visibility:public"])
5+
6+
cc_library(
7+
name = "headers",
8+
hdrs = glob(["include/**/*.h"]),
9+
strip_include_prefix = "include",
10+
)
11+
12+
cc_library(
13+
name = "resource_detectors",
14+
srcs = [
15+
"container_detector.cc",
16+
"container_detector_utils.cc",
17+
],
18+
deps = [
19+
"//api",
20+
"//resource_detectors:headers",
21+
"//sdk:headers",
22+
"//sdk/src/resource",
23+
],
24+
)

resource_detectors/CMakeLists.txt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright The OpenTelemetry Authors
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
add_library(opentelemetry_resource_detectors container_detector_utils.cc
5+
container_detector.cc)
6+
7+
set_target_properties(opentelemetry_resource_detectors
8+
PROPERTIES EXPORT_NAME resource_detectors)
9+
set_target_version(opentelemetry_resource_detectors)
10+
11+
target_link_libraries(opentelemetry_resource_detectors
12+
PUBLIC opentelemetry_resources)
13+
target_include_directories(
14+
opentelemetry_resource_detectors
15+
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
16+
"$<INSTALL_INTERFACE:include>")
17+
18+
otel_add_component(
19+
COMPONENT
20+
resource_detectors
21+
TARGETS
22+
opentelemetry_resource_detectors
23+
FILES_DIRECTORY
24+
"include/opentelemetry/"
25+
FILES_DESTINATION
26+
"include/opentelemetry"
27+
FILES_MATCHING
28+
PATTERN
29+
"*.h"
30+
PATTERN
31+
"container_detector_utils.h"
32+
EXCLUDE)
33+
34+
if(BUILD_TESTING)
35+
add_subdirectory(test)
36+
endif()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#include "opentelemetry/resource_detectors/container_detector.h"
5+
#include "opentelemetry/nostd/variant.h"
6+
#include "opentelemetry/resource_detectors/container_detector_utils.h"
7+
#include "opentelemetry/sdk/resource/resource.h"
8+
#include "opentelemetry/sdk/resource/resource_detector.h"
9+
#include "opentelemetry/semconv/incubating/container_attributes.h"
10+
#include "opentelemetry/version.h"
11+
12+
#include <string>
13+
#include <unordered_map>
14+
#include <utility>
15+
16+
OPENTELEMETRY_BEGIN_NAMESPACE
17+
namespace resource_detector
18+
{
19+
20+
/**
21+
* This is the file path from where we can get container.id
22+
*/
23+
constexpr const char *kCGroupPath = "/proc/self/cgroup";
24+
25+
opentelemetry::sdk::resource::Resource ContainerResourceDetector::Detect() noexcept
26+
{
27+
std::string container_id =
28+
opentelemetry::resource_detector::detail::GetContainerIDFromCgroup(kCGroupPath);
29+
if (container_id.empty())
30+
{
31+
return ResourceDetector::Create({});
32+
}
33+
34+
opentelemetry::sdk::resource::ResourceAttributes attributes;
35+
36+
attributes[semconv::container::kContainerId] = std::move(container_id);
37+
return ResourceDetector::Create(attributes);
38+
}
39+
40+
} // namespace resource_detector
41+
OPENTELEMETRY_END_NAMESPACE
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#include "opentelemetry/resource_detectors/container_detector_utils.h"
5+
#include "opentelemetry/nostd/string_view.h"
6+
7+
#include <fstream>
8+
#include <regex>
9+
#include <string>
10+
11+
#include "opentelemetry/version.h"
12+
13+
OPENTELEMETRY_BEGIN_NAMESPACE
14+
namespace resource_detector
15+
{
16+
namespace detail
17+
{
18+
19+
std::string GetContainerIDFromCgroup(const char *file_path)
20+
{
21+
std::ifstream cgroup_file(file_path);
22+
std::string line;
23+
24+
while (std::getline(cgroup_file, line))
25+
{
26+
std::string container_id = ExtractContainerIDFromLine(line);
27+
if (!container_id.empty())
28+
{
29+
return container_id;
30+
}
31+
}
32+
return std::string();
33+
}
34+
35+
std::string ExtractContainerIDFromLine(nostd::string_view line)
36+
{
37+
/**
38+
* This regex is designed to extract container IDs from cgroup file lines.
39+
* It matches hexadecimal container IDs used by container runtimes like Docker, containerd, and
40+
* cri-o.
41+
* Examples of matching lines:
42+
* - 0::/docker/3fae9b2c6d7e8f90123456789abcdef0123456789abcdef0123456789abcdef0
43+
* - "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa"
44+
* - "e857a4bf05a69080a759574949d7a0e69572e27647800fa7faff6a05a8332aa1"
45+
* Please see the test cases in resource_test.cc for more examples.
46+
*/
47+
static const std::regex container_id_regex(R"(^.*/(?:.*[-:])?([0-9a-f]+)(?:\.|\s*$))");
48+
std::match_results<const char *> match;
49+
50+
if (std::regex_search(line.data(), line.data() + line.size(), match, container_id_regex))
51+
{
52+
return match.str(1);
53+
}
54+
55+
return std::string();
56+
}
57+
58+
} // namespace detail
59+
} // namespace resource_detector
60+
OPENTELEMETRY_END_NAMESPACE
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#pragma once
5+
6+
#include "opentelemetry/sdk/resource/resource.h"
7+
#include "opentelemetry/sdk/resource/resource_detector.h"
8+
#include "opentelemetry/version.h"
9+
10+
OPENTELEMETRY_BEGIN_NAMESPACE
11+
namespace resource_detector
12+
{
13+
14+
/**
15+
* ContainerResourceDetector to detect resource attributes when running inside a containerized
16+
* environment. This detector extracts metadata such as container ID from cgroup information and
17+
* sets attributes like container.id following the OpenTelemetry semantic conventions.
18+
*/
19+
class ContainerResourceDetector : public opentelemetry::sdk::resource::ResourceDetector
20+
{
21+
public:
22+
opentelemetry::sdk::resource::Resource Detect() noexcept override;
23+
};
24+
25+
} // namespace resource_detector
26+
OPENTELEMETRY_END_NAMESPACE
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#pragma once
5+
6+
#include <string>
7+
8+
#include "opentelemetry/nostd/string_view.h"
9+
#include "opentelemetry/version.h"
10+
11+
OPENTELEMETRY_BEGIN_NAMESPACE
12+
namespace resource_detector
13+
{
14+
namespace detail
15+
{
16+
17+
/**
18+
* Reads the container.id from /proc/self/cgroup file.
19+
* @param file_path file path of cgroup
20+
* @return container.id as string or an empty string if not found on error
21+
*/
22+
std::string GetContainerIDFromCgroup(const char *file_path);
23+
24+
/**
25+
* Matches the line with the regex to find container.id
26+
* @param line a single line of text, typically from the /proc/self/cgroup file
27+
* @return matched id or empty string
28+
*/
29+
std::string ExtractContainerIDFromLine(nostd::string_view line);
30+
31+
} // namespace detail
32+
} // namespace resource_detector
33+
OPENTELEMETRY_END_NAMESPACE

resource_detectors/test/BUILD

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright The OpenTelemetry Authors
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
cc_test(
5+
name = "resource_detector_test",
6+
srcs = [
7+
"container_detector_test.cc",
8+
],
9+
tags = ["test"],
10+
deps = [
11+
"//api",
12+
"//resource_detectors",
13+
"@com_google_googletest//:gtest_main",
14+
],
15+
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright The OpenTelemetry Authors
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
add_executable(resource_detector_test container_detector_test.cc)
5+
6+
# Link the required dependencies
7+
target_link_libraries(
8+
resource_detector_test PRIVATE opentelemetry_resource_detectors
9+
opentelemetry_api GTest::gtest_main)
10+
11+
gtest_add_tests(
12+
TARGET resource_detector_test
13+
TEST_PREFIX resource_detector.
14+
TEST_LIST resource_detector_test)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#include <gtest/gtest.h>
5+
#include <cstdio>
6+
#include <fstream>
7+
#include <string>
8+
9+
#include "opentelemetry/nostd/string_view.h"
10+
#include "opentelemetry/resource_detectors/container_detector_utils.h"
11+
12+
TEST(ContainerIdDetectorTest, ExtractValidContainerIdFromLine)
13+
{
14+
std::string line =
15+
"13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa";
16+
std::string extracted_id =
17+
opentelemetry::resource_detector::detail::ExtractContainerIDFromLine(line);
18+
EXPECT_EQ(std::string{"ac679f8a8319c8cf7d38e1adf263bc08d23"}, extracted_id);
19+
}
20+
21+
TEST(ContainerIdDetectorTest, ExtractIdFromMockUpCGroupFile)
22+
{
23+
const char *filename = "test_cgroup.txt";
24+
25+
{
26+
std::ofstream outfile(filename);
27+
outfile << "13:name=systemd:/kuberuntime/containerd"
28+
"/kubepods-pod872d2066_00ef_48ea_a7d8_51b18b72d739:cri-containerd:"
29+
"e857a4bf05a69080a759574949d7a0e69572e27647800fa7faff6a05a8332aa1\n";
30+
outfile << "9:cpu:/not-a-container\n";
31+
}
32+
33+
std::string container_id =
34+
opentelemetry::resource_detector::detail::GetContainerIDFromCgroup(filename);
35+
EXPECT_EQ(container_id,
36+
std::string{"e857a4bf05a69080a759574949d7a0e69572e27647800fa7faff6a05a8332aa1"});
37+
38+
std::remove(filename);
39+
}
40+
41+
TEST(ContainerIdDetectorTest, DoesNotExtractInvalidLine)
42+
{
43+
std::string line = "this line does not contain a container id";
44+
std::string id = opentelemetry::resource_detector::detail::ExtractContainerIDFromLine(line);
45+
EXPECT_EQ(id, std::string{""});
46+
}
47+
48+
TEST(ContainerIdDetectorTest, ReturnsEmptyOnNoMatch)
49+
{
50+
const char *filename = "test_empty_cgroup.txt";
51+
52+
{
53+
std::ofstream outfile(filename);
54+
outfile << "no container id here\n";
55+
}
56+
57+
std::string id = opentelemetry::resource_detector::detail::GetContainerIDFromCgroup(filename);
58+
EXPECT_EQ(id, std::string{""});
59+
60+
std::remove(filename); // cleanup
61+
}
62+
63+
TEST(ContainerIdDetectorTest, ReturnsEmptyOnFileFailingToOpen)
64+
{
65+
const char *filename = "test_invalid_cgroup.txt";
66+
67+
std::string id = opentelemetry::resource_detector::detail::GetContainerIDFromCgroup(filename);
68+
EXPECT_EQ(id, std::string{""});
69+
}

0 commit comments

Comments
 (0)