Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c740ddb
[SDK] Implementation of container resource as per semconv
nikhilbhatia08 Jul 29, 2025
4bf2bfb
[sdk] minor changes in format, linking and var naming
nikhilbhatia08 Jul 30, 2025
f075c93
changes for ci
nikhilbhatia08 Jul 31, 2025
86cebd6
Merge branch 'main' into implement_resource_detectors
lalitb Aug 2, 2025
00decf9
ci and requested changes
nikhilbhatia08 Aug 2, 2025
b84840f
Merge branch 'implement_resource_detectors' of https://github.com/nik…
nikhilbhatia08 Aug 2, 2025
edef26e
iwyu-fix
nikhilbhatia08 Aug 2, 2025
3742932
newline-fix
nikhilbhatia08 Aug 2, 2025
a495294
newline-fix
nikhilbhatia08 Aug 2, 2025
72565ec
Merge branch 'implement_resource_detectors' of https://github.com/nik…
nikhilbhatia08 Aug 7, 2025
f7b5e94
[EXPORTER] Fixes tsan warnings (#3531)
owent Jul 29, 2025
a836349
[DOC] Document minimum required versions (#3562)
markus456 Jul 29, 2025
5ec872c
Bump github/codeql-action from 3.29.4 to 3.29.5 (#3574)
dependabot[bot] Jul 30, 2025
81624a3
Add subscript to issue templates (#3576)
opentelemetrybot Aug 1, 2025
4cebe35
iwyu-fix
nikhilbhatia08 Aug 2, 2025
a1778b9
newline-fix
nikhilbhatia08 Aug 2, 2025
926517f
Restore ci.yml to match main
nikhilbhatia08 Aug 2, 2025
bd7a71c
Merge branch 'implement_resource_detectors' of https://github.com/nik…
nikhilbhatia08 Aug 7, 2025
2546661
new folder for resource detectors
nikhilbhatia08 Aug 7, 2025
75c495c
BUILD file for resource_detectors
nikhilbhatia08 Aug 7, 2025
220a5ad
BUILD file(resource_detectors) minor changes
nikhilbhatia08 Aug 7, 2025
439e92c
added comments and changes as required
nikhilbhatia08 Aug 7, 2025
9e5bc55
changed to nostd::string_view
nikhilbhatia08 Aug 7, 2025
9bc07e1
regex fix based on nostd::string_view
nikhilbhatia08 Aug 8, 2025
047e35c
Merge branch 'main' into implement_resource_detectors
lalitb Aug 8, 2025
a404c80
constants naming fix
nikhilbhatia08 Aug 8, 2025
7cbcc59
namespace change and tests
nikhilbhatia08 Aug 9, 2025
0e7d011
iwyu and BUILD fix
nikhilbhatia08 Aug 9, 2025
fd93b32
namespace change
nikhilbhatia08 Aug 9, 2025
b030cc9
cmakelists flags addition and fix
nikhilbhatia08 Aug 10, 2025
018a1f8
resource detectors preview-options
nikhilbhatia08 Aug 10, 2025
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
30 changes: 30 additions & 0 deletions sdk/include/opentelemetry/sdk/common/container.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <string>

#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace common
{
/**
Reads the container.id from /proc/self/cgroup file.
@param file_path file path of cgroup
@return container.id as string or empty string
*/
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(const std::string &line);
} // namespace common
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
11 changes: 11 additions & 0 deletions sdk/include/opentelemetry/sdk/resource/resource_detector.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ class OTELResourceDetector : public ResourceDetector
Resource Detect() noexcept override;
};

/**
* 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 ResourceDetector
{
public:
Resource Detect() noexcept override;
};

} // namespace resource
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
11 changes: 11 additions & 0 deletions sdk/src/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ cc_library(
],
)

cc_library(
name = "container",
srcs = [
"container.cc",
],
deps = [
"//api",
"//sdk:headers",
],
)

cc_library(
name = "global_log_handler",
srcs = [
Expand Down
4 changes: 2 additions & 2 deletions sdk/src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

set(COMMON_SRCS random.cc global_log_handler.cc env_variables.cc base64.cc
disabled.cc)
set(COMMON_SRCS random.cc global_log_handler.cc env_variables.cc container.cc
base64.cc disabled.cc)
if(WIN32)
list(APPEND COMMON_SRCS platform/fork_windows.cc)
else()
Expand Down
46 changes: 46 additions & 0 deletions sdk/src/common/container.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include "opentelemetry/sdk/common/container.h"

#include <fstream>
#include <regex>

#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace common
{
std::string GetContainerIDFromCgroup(const char *file_path)
{
std::ifstream cgroup_file(file_path);
Copy link

Copilot AI Jul 30, 2025

Choose a reason for hiding this comment

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

The function doesn't check if the file was successfully opened before attempting to read from it. Consider adding error handling to check if the file exists and is readable.

Suggested change
std::ifstream cgroup_file(file_path);
std::ifstream cgroup_file(file_path);
if (!cgroup_file.is_open())
{
return "";
}

Copilot uses AI. Check for mistakes.

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 ExtractContainerIDFromLine(const std::string &line)
{
Copy link

Copilot AI Jul 30, 2025

Choose a reason for hiding this comment

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

The regex pattern contains unexplained magic logic. Consider adding a comment explaining what container ID formats this regex is designed to match (e.g., Docker, containerd, cri-o) and provide examples of the expected input patterns.

Suggested change
{
{
// 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.
// The pattern breakdown:
// - ^.*/: Matches any path prefix up to the container ID.
// - (?:.*[-:])?: Optionally matches a prefix ending with '-' or ':' (e.g., "docker-", "containerd-").
// - ([0-9a-f]+): Captures the container ID, which is a sequence of hexadecimal characters.
// - (?:\.|\s*$): Ensures the container ID is followed by a '.' or the end of the line.
// Examples of matching lines:
// - "/docker/1234567890abcdef" -> "1234567890abcdef"
// - "/kubepods/burstable/pod1234/containerd-abcdef1234567890" -> "abcdef1234567890"
// - "/kubepods/pod5678/cri-containerd-0987654321abcdef.scope" -> "0987654321abcdef"

Copilot uses AI. Check for mistakes.

static const std::regex container_id_regex(R"(^.*/(?:.*[-:])?([0-9a-f]+)(?:\.|\s*$))");
std::smatch match;

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

return "";
}
} // namespace common
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
1 change: 1 addition & 0 deletions sdk/src/resource/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ cc_library(
deps = [
"//api",
"//sdk:headers",
"//sdk/src/common:container",
"//sdk/src/common:env_variables",
],
)
3 changes: 2 additions & 1 deletion sdk/src/resource/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

add_library(opentelemetry_resources resource.cc resource_detector.cc)
add_library(opentelemetry_resources resource.cc resource_detector.cc
container_detector.cc)

set_target_properties(opentelemetry_resources PROPERTIES EXPORT_NAME resources)
set_target_version(opentelemetry_resources)
Expand Down
41 changes: 41 additions & 0 deletions sdk/src/resource/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/nostd/variant.h"
#include "opentelemetry/sdk/common/container.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>

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace resource
{

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

Resource ContainerResourceDetector::Detect() noexcept
{
std::string container_id = opentelemetry::sdk::common::GetContainerIDFromCgroup(KCGroupPath);
if (container_id.empty())
{
return ResourceDetector::Create({});
}

ResourceAttributes attributes;

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

} // namespace resource
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
9 changes: 6 additions & 3 deletions sdk/src/resource/resource.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ Resource Resource::Merge(const Resource &other) const noexcept

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, schema_url});
static auto otel_resource = OTELResourceDetector().Detect();
static auto container_resource = ContainerResourceDetector().Detect();
auto resource = Resource::GetDefault()
.Merge(otel_resource)
.Merge(container_resource)
.Merge(Resource{attributes, schema_url});

if (resource.attributes_.find(semconv::service::kServiceName) == resource.attributes_.end())
{
Expand Down
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";
const char *KOtelResourceAttributes = "OTEL_RESOURCE_ATTRIBUTES";
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
52 changes: 52 additions & 0 deletions sdk/test/resource/resource_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@

#include <gtest/gtest.h>
#include <stdint.h>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <map>
#include <string>
#include <unordered_map>
#include <utility>

#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
#include "opentelemetry/sdk/common/container.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/sdk/resource/resource_detector.h"
#include "opentelemetry/sdk/version/version.h"
Expand Down Expand Up @@ -292,3 +295,52 @@ TEST(ResourceTest, DerivedResourceDetector)
EXPECT_EQ(resource.GetSchemaURL(), detector.schema_url);
EXPECT_TRUE(received_attributes.find("key") != received_attributes.end());
}

TEST(ResourceTest, ExtractValidContainerId)
{
std::string line =
"13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa";
std::string extracted_id = opentelemetry::sdk::common::ExtractContainerIDFromLine(line);
EXPECT_EQ(std::string{"ac679f8a8319c8cf7d38e1adf263bc08d23"}, extracted_id);
}

TEST(ResourceTest, 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::sdk::common::GetContainerIDFromCgroup(filename);
EXPECT_EQ(container_id,
std::string{"e857a4bf05a69080a759574949d7a0e69572e27647800fa7faff6a05a8332aa1"});

std::remove(filename);
Copy link

Copilot AI Jul 30, 2025

Choose a reason for hiding this comment

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

The test file cleanup should be in a finally block or use RAII to ensure cleanup occurs even if the test fails. Consider using a test fixture with proper cleanup or a temporary file wrapper.

Suggested change
std::remove(filename);

Copilot uses AI. Check for mistakes.

}

TEST(ResourceTest, DoesNotExtractInvalidLine)
{
std::string line = "this line does not contain a container id";
std::string id = opentelemetry::sdk::common::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::sdk::common::GetContainerIDFromCgroup(filename);
EXPECT_EQ(id, std::string{""});

std::remove(filename); // cleanup
Copy link

Copilot AI Jul 30, 2025

Choose a reason for hiding this comment

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

The test file cleanup should be in a finally block or use RAII to ensure cleanup occurs even if the test fails. Consider using a test fixture with proper cleanup or a temporary file wrapper.

Suggested change
const char *filename = "test_empty_cgroup.txt";
{
std::ofstream outfile(filename);
outfile << "no container id here\n";
}
std::string id = opentelemetry::sdk::common::GetContainerIDFromCgroup(filename);
EXPECT_EQ(id, std::string{""});
std::remove(filename); // cleanup
TemporaryFile temp_file("test_empty_cgroup.txt");
{
std::ofstream outfile(temp_file.GetFilename());
outfile << "no container id here\n";
}
std::string id = opentelemetry::sdk::common::GetContainerIDFromCgroup(temp_file.GetFilename());
EXPECT_EQ(id, std::string{""});

Copilot uses AI. Check for mistakes.

}
Loading