Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ option(WITH_METRICS_EXEMPLAR_PREVIEW
option(WITH_THREAD_INSTRUMENTATION_PREVIEW
"Whether to enable thread instrumentation" OFF)

option(WITH_RESOURCE_DETECTORS_PREVIEW
"Whether to enable inbuilt resource detectors" OFF)

option(OPENTELEMETRY_SKIP_DYNAMIC_LOADING_TESTS
"Whether to build test libraries that are always linked as shared libs"
OFF)
Expand Down Expand Up @@ -645,6 +648,9 @@ if(NOT WITH_API_ONLY)
add_subdirectory(sdk)
add_subdirectory(ext)
add_subdirectory(exporters)
if(WITH_RESOURCE_DETECTORS_PREVIEW)
add_subdirectory(resource_detectors)
endif()

if(BUILD_TESTING)
add_subdirectory(test_common)
Expand Down
24 changes: 24 additions & 0 deletions resource_detectors/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

package(default_visibility = ["//visibility:public"])

cc_library(
name = "headers",
hdrs = glob(["include/**/*.h"]),
strip_include_prefix = "include",
)

cc_library(
name = "resource_detectors",
srcs = [
"container_detector.cc",
"container_detector_utils.cc",
],
deps = [
"//api",
"//resource_detectors:headers",
"//sdk:headers",
"//sdk/src/resource",
],
)
36 changes: 36 additions & 0 deletions resource_detectors/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

add_library(opentelemetry_resource_detectors container_detector_utils.cc
container_detector.cc)

set_target_properties(opentelemetry_resource_detectors
PROPERTIES EXPORT_NAME resource_detectors)
set_target_version(opentelemetry_resource_detectors)

target_link_libraries(opentelemetry_resource_detectors
PUBLIC opentelemetry_resources)
target_include_directories(
opentelemetry_resource_detectors
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:include>")

otel_add_component(
COMPONENT
resource_detectors
TARGETS
opentelemetry_resource_detectors
FILES_DIRECTORY
"include/opentelemetry/"
FILES_DESTINATION
"include/opentelemetry"
FILES_MATCHING
PATTERN
"*.h"
PATTERN
"container_detector_utils.h"
EXCLUDE)

if(BUILD_TESTING)
add_subdirectory(test)
endif()
41 changes: 41 additions & 0 deletions resource_detectors/container_detector.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include "opentelemetry/resource_detectors/container_detector.h"
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/resource_detectors/container_detector_utils.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/sdk/resource/resource_detector.h"
#include "opentelemetry/semconv/incubating/container_attributes.h"
#include "opentelemetry/version.h"

#include <string>
#include <unordered_map>
#include <utility>

OPENTELEMETRY_BEGIN_NAMESPACE
namespace resource_detector
{

/**
* This is the file path from where we can get container.id
*/
constexpr const char *kCGroupPath = "/proc/self/cgroup";

opentelemetry::sdk::resource::Resource ContainerResourceDetector::Detect() noexcept
{
std::string container_id =
opentelemetry::resource_detector::detail::GetContainerIDFromCgroup(kCGroupPath);
if (container_id.empty())
{
return ResourceDetector::Create({});
}

opentelemetry::sdk::resource::ResourceAttributes attributes;

attributes[semconv::container::kContainerId] = std::move(container_id);
return ResourceDetector::Create(attributes);
}

} // namespace resource_detector
OPENTELEMETRY_END_NAMESPACE
60 changes: 60 additions & 0 deletions resource_detectors/container_detector_utils.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include "opentelemetry/resource_detectors/container_detector_utils.h"
#include "opentelemetry/nostd/string_view.h"

#include <fstream>
#include <regex>
#include <string>

#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace resource_detector
{
namespace detail
{

std::string GetContainerIDFromCgroup(const char *file_path)
{
std::ifstream cgroup_file(file_path);
std::string line;

while (std::getline(cgroup_file, line))
{
std::string container_id = ExtractContainerIDFromLine(line);
if (!container_id.empty())
{
return container_id;
}
}
return std::string();
}

std::string ExtractContainerIDFromLine(nostd::string_view line)
{
/**
* This regex is designed to extract container IDs from cgroup file lines.
* It matches hexadecimal container IDs used by container runtimes like Docker, containerd, and
* cri-o.
* Examples of matching lines:
* - 0::/docker/3fae9b2c6d7e8f90123456789abcdef0123456789abcdef0123456789abcdef0
* - "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa"
* - "e857a4bf05a69080a759574949d7a0e69572e27647800fa7faff6a05a8332aa1"
* Please see the test cases in resource_test.cc for more examples.
*/
static const std::regex container_id_regex(R"(^.*/(?:.*[-:])?([0-9a-f]+)(?:\.|\s*$))");
std::match_results<const char *> match;

if (std::regex_search(line.data(), line.data() + line.size(), match, container_id_regex))
{
return match.str(1);
}

return std::string();
}

} // namespace detail
} // namespace resource_detector
OPENTELEMETRY_END_NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/sdk/resource/resource_detector.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace resource_detector
{

/**
* ContainerResourceDetector to detect resource attributes when running inside a containerized
* environment. This detector extracts metadata such as container ID from cgroup information and
* sets attributes like container.id following the OpenTelemetry semantic conventions.
*/
class ContainerResourceDetector : public opentelemetry::sdk::resource::ResourceDetector
{
public:
opentelemetry::sdk::resource::Resource Detect() noexcept override;
};

} // namespace resource_detector
OPENTELEMETRY_END_NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <string>

#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace resource_detector
{
namespace detail
{

/**
* Reads the container.id from /proc/self/cgroup file.
* @param file_path file path of cgroup
* @return container.id as string or an empty string if not found on error
*/
std::string GetContainerIDFromCgroup(const char *file_path);

/**
* Matches the line with the regex to find container.id
* @param line a single line of text, typically from the /proc/self/cgroup file
* @return matched id or empty string
*/
std::string ExtractContainerIDFromLine(nostd::string_view line);

} // namespace detail
} // namespace resource_detector
OPENTELEMETRY_END_NAMESPACE
15 changes: 15 additions & 0 deletions resource_detectors/test/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

cc_test(
name = "resource_detector_test",
srcs = [
"container_detector_test.cc",
],
tags = ["test"],
deps = [
"//api",
"//resource_detectors",
"@com_google_googletest//:gtest_main",
],
)
14 changes: 14 additions & 0 deletions resource_detectors/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

add_executable(resource_detector_test container_detector_test.cc)

# Link the required dependencies
target_link_libraries(
resource_detector_test PRIVATE opentelemetry_resource_detectors
opentelemetry_api GTest::gtest_main)

gtest_add_tests(
TARGET resource_detector_test
TEST_PREFIX resource_detector.
TEST_LIST resource_detector_test)
69 changes: 69 additions & 0 deletions resource_detectors/test/container_detector_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include <gtest/gtest.h>
#include <cstdio>
#include <fstream>
#include <string>

#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/resource_detectors/container_detector_utils.h"

TEST(ContainerIdDetectorTest, ExtractValidContainerIdFromLine)
{
std::string line =
"13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa";
std::string extracted_id =
opentelemetry::resource_detector::detail::ExtractContainerIDFromLine(line);
EXPECT_EQ(std::string{"ac679f8a8319c8cf7d38e1adf263bc08d23"}, extracted_id);
}

TEST(ContainerIdDetectorTest, ExtractIdFromMockUpCGroupFile)
{
const char *filename = "test_cgroup.txt";

{
std::ofstream outfile(filename);
outfile << "13:name=systemd:/kuberuntime/containerd"
"/kubepods-pod872d2066_00ef_48ea_a7d8_51b18b72d739:cri-containerd:"
"e857a4bf05a69080a759574949d7a0e69572e27647800fa7faff6a05a8332aa1\n";
outfile << "9:cpu:/not-a-container\n";
}

std::string container_id =
opentelemetry::resource_detector::detail::GetContainerIDFromCgroup(filename);
EXPECT_EQ(container_id,
std::string{"e857a4bf05a69080a759574949d7a0e69572e27647800fa7faff6a05a8332aa1"});

std::remove(filename);
}

TEST(ContainerIdDetectorTest, DoesNotExtractInvalidLine)
{
std::string line = "this line does not contain a container id";
std::string id = opentelemetry::resource_detector::detail::ExtractContainerIDFromLine(line);
EXPECT_EQ(id, std::string{""});
}

TEST(ContainerIdDetectorTest, ReturnsEmptyOnNoMatch)
{
const char *filename = "test_empty_cgroup.txt";

{
std::ofstream outfile(filename);
outfile << "no container id here\n";
}

std::string id = opentelemetry::resource_detector::detail::GetContainerIDFromCgroup(filename);
EXPECT_EQ(id, std::string{""});

std::remove(filename); // cleanup
}

TEST(ContainerIdDetectorTest, ReturnsEmptyOnFileFailingToOpen)
{
const char *filename = "test_invalid_cgroup.txt";

std::string id = opentelemetry::resource_detector::detail::GetContainerIDFromCgroup(filename);
EXPECT_EQ(id, std::string{""});
}
8 changes: 4 additions & 4 deletions sdk/src/resource/resource_detector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ namespace sdk
namespace resource
{

const char *OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES";
const char *OTEL_SERVICE_NAME = "OTEL_SERVICE_NAME";
constexpr const char *kOtelResourceAttributes = "OTEL_RESOURCE_ATTRIBUTES";
constexpr const char *kOtelServiceName = "OTEL_SERVICE_NAME";

Resource ResourceDetector::Create(const ResourceAttributes &attributes,
const std::string &schema_url)
Expand All @@ -33,9 +33,9 @@ Resource OTELResourceDetector::Detect() noexcept
std::string attributes_str, service_name;

bool attributes_exists = opentelemetry::sdk::common::GetStringEnvironmentVariable(
OTEL_RESOURCE_ATTRIBUTES, attributes_str);
kOtelResourceAttributes, attributes_str);
bool service_name_exists =
opentelemetry::sdk::common::GetStringEnvironmentVariable(OTEL_SERVICE_NAME, service_name);
opentelemetry::sdk::common::GetStringEnvironmentVariable(kOtelServiceName, service_name);

if (!attributes_exists && !service_name_exists)
{
Expand Down
1 change: 1 addition & 0 deletions test_common/cmake/preview-options.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ set(WITH_THREAD_INSTRUMENTATION_PREVIEW ${ENABLE_PREVIEW} CACHE BOOL "" FORCE)
set(WITH_OTLP_GRPC_SSL_MTLS_PREVIEW ${ENABLE_PREVIEW} CACHE BOOL "" FORCE)
set(WITH_OTLP_GRPC_CREDENTIAL_PREVIEW ${ENABLE_PREVIEW} CACHE BOOL "" FORCE)
set(WITH_OTLP_RETRY_PREVIEW ${ENABLE_PREVIEW} CACHE BOOL "" FORCE)
set(WITH_RESOURCE_DETECTORS_PREVIEW ${ENABLE_PREVIEW} CACHE BOOL "" FORCE)
set(WITH_OTLP_HTTP_COMPRESSION ${ENABLE_PREVIEW} CACHE BOOL "" FORCE)
set(WITH_CURL_LOGGING ${ENABLE_PREVIEW} CACHE BOOL "" FORCE)
Loading