Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 contains
Copy link

Copilot AI Jul 29, 2025

Choose a reason for hiding this comment

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

Incomplete documentation comment. The '@param line contains' description is cut off and should specify what the line parameter contains.

Suggested change
@param line contains
@param line a single line of text, typically from the /proc/self/cgroup file

Copilot uses AI. Check for mistakes.

@return matched id or empty string
*/
std::string ExtractContainerIDFromLine(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
2 changes: 1 addition & 1 deletion sdk/src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

set(COMMON_SRCS random.cc global_log_handler.cc env_variables.cc base64.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)
Expand Down
53 changes: 53 additions & 0 deletions sdk/src/common/container.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

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

#ifdef _MSC_VER
# include <string.h>
# define strcasecmp _stricmp
#else
# include <strings.h>
#endif

#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))
Copy link

Copilot AI Jul 29, 2025

Choose a reason for hiding this comment

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

Missing space after 'while'. Should be 'while (std::getline(cgroup_file, line))' to follow consistent formatting style.

Suggested change
while(std::getline(cgroup_file, line))
while (std::getline(cgroup_file, line))

Copilot uses AI. Check for mistakes.

{
std::string container_id = ExtractContainerIDFromLine(line);
if(!container_id.empty())
Copy link

Copilot AI Jul 29, 2025

Choose a reason for hiding this comment

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

Missing space after 'if'. Should be 'if (!container_id.empty())' to follow consistent formatting style.

Suggested change
if(!container_id.empty())
if (!container_id.empty())

Copilot uses AI. Check for mistakes.

{
return container_id;
}
}
return "";
}

std::string ExtractContainerIDFromLine(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
3 changes: 2 additions & 1 deletion sdk/src/resource/resource.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ 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();
static auto container_resource = ContainerResourceDetector().Detect();
auto resource =
Resource::GetDefault().Merge(otel_resource).Merge(Resource{attributes, schema_url});
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
23 changes: 23 additions & 0 deletions sdk/src/resource/resource_detector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
#include "opentelemetry/sdk/resource/resource_detector.h"
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/sdk/common/env_variables.h"
#include "opentelemetry/sdk/common/container.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/semconv/service_attributes.h"
#include "opentelemetry/semconv/incubating/container_attributes.h"
#include "opentelemetry/version.h"

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

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

Resource ResourceDetector::Create(const ResourceAttributes &attributes,
const std::string &schema_url)
Expand Down Expand Up @@ -68,6 +74,23 @@ Resource OTELResourceDetector::Detect() noexcept
return ResourceDetector::Create(attributes);
}

Resource ContainerResourceDetector::Detect() noexcept
{
std::string container_id = opentelemetry::sdk::common::GetContainerIDFromCgroup(C_GROUP_PATH);
if(container_id.empty())
Copy link

Copilot AI Jul 29, 2025

Choose a reason for hiding this comment

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

Missing space after 'if'. Should be 'if (container_id.empty())' to follow consistent formatting style.

Suggested change
if(container_id.empty())
if (container_id.empty())

Copilot uses AI. Check for mistakes.

{
return ResourceDetector::Create({});
}

ResourceAttributes attributes;

if(!container_id.empty())
Copy link

Copilot AI Jul 29, 2025

Choose a reason for hiding this comment

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

Missing space after 'if'. Should be 'if (!container_id.empty())' to follow consistent formatting style.

Suggested change
if(!container_id.empty())
if (!container_id.empty())

Copilot uses AI. Check for mistakes.

{
attributes[semconv::container::kContainerId] = container_id;
}
Copy link

Copilot AI Jul 29, 2025

Choose a reason for hiding this comment

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

This condition is redundant since container_id.empty() was already checked at line 80. If container_id is empty, the function returns early, so this check is unnecessary.

Suggested change
if(!container_id.empty())
{
attributes[semconv::container::kContainerId] = container_id;
}
attributes[semconv::container::kContainerId] = container_id;

Copilot uses AI. Check for mistakes.

return ResourceDetector::Create(attributes);
}

} // namespace resource
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
47 changes: 47 additions & 0 deletions sdk/test/resource/resource_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
#include <string>
#include <unordered_map>
#include <utility>
#include<fstream>
Copy link

Copilot AI Jul 29, 2025

Choose a reason for hiding this comment

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

Missing space after #include. Should be '#include ' to follow consistent formatting style.

Suggested change
#include<fstream>
#include <fstream>

Copilot uses AI. Check for mistakes.


#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 +294,48 @@ TEST(ResourceTest, DerivedResourceDetector)
EXPECT_EQ(resource.GetSchemaURL(), detector.schema_url);
EXPECT_TRUE(received_attributes.find("key") != received_attributes.end());
}

TEST(ResourceTest, ExctractValidContainerId)
Copy link

Copilot AI Jul 29, 2025

Choose a reason for hiding this comment

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

Typo in test name: 'ExctractValidContainerId' should be 'ExtractValidContainerId'.

Suggested change
TEST(ResourceTest, ExctractValidContainerId)
TEST(ResourceTest, ExtractValidContainerId)

Copilot uses AI. Check for mistakes.

{
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