Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b717fd5
Declare new functions
LevelVoid Aug 15, 2026
dba5977
Implementation of platform specific functions
LevelVoid Aug 15, 2026
0245e4a
Call new functions in Detect()
LevelVoid Aug 15, 2026
71fea9c
Update class docstrings and readme
LevelVoid Aug 15, 2026
dd0842a
Add unit tests and integration tests
LevelVoid Aug 15, 2026
92a1b97
Fix test on macOS for GetExecutableBuildIdHtlhashTest
LevelVoid Aug 15, 2026
8edc994
Merge branch 'main' into add-required-attributes
LevelVoid Aug 17, 2026
a5e9d51
Merge branch 'main' into add-required-attributes
LevelVoid Aug 17, 2026
cd4374a
Merge branch 'main' into add-required-attributes
LevelVoid Aug 17, 2026
f5f1ac7
Fix clang-format violations
LevelVoid Aug 17, 2026
f696bf4
Combine GetExecutableName and GetExecutablePath into GetExecutableInf…
LevelVoid Aug 18, 2026
cdde1da
Type and entity fixes
LevelVoid Aug 18, 2026
fd5404e
MS CI failure fix
LevelVoid Aug 19, 2026
0dd9a9e
Merge branch 'main' into add-required-attributes
dbarker Aug 21, 2026
ef03ba2
Add imports for Windows, sync docs,fix tellg() validation
LevelVoid Aug 21, 2026
c6be17d
fix formatting
LevelVoid Aug 21, 2026
ad4ae2d
fix Readme lint issue
LevelVoid Aug 21, 2026
9d80789
Merge branch 'main' into add-required-attributes
LevelVoid Aug 21, 2026
c3d5ab8
fix:CI type failure
LevelVoid Aug 22, 2026
d7daad1
fix: CI failure- increase buff size
LevelVoid Aug 22, 2026
02f132d
fix: cppcheck containerOutofBounds warning
LevelVoid Aug 22, 2026
3c97c44
Merge branch 'main' into add-required-attributes
LevelVoid Aug 22, 2026
422fdc5
Merge branch 'main' into add-required-attributes
LevelVoid Aug 22, 2026
a27adf3
fix: merge duplication
LevelVoid Aug 22, 2026
54cf114
fix: service_detector_utils stale call
LevelVoid Aug 22, 2026
2cf5a0f
fix: add head and tail processing with overlap in htlHash
LevelVoid Aug 24, 2026
426c888
fix: Readme and tests update for macOS htlHash
LevelVoid Aug 24, 2026
e5a0f9c
Merge branch 'main' into add-required-attributes
LevelVoid Aug 24, 2026
5d5ca4c
fix: -Wformat warning
LevelVoid Aug 24, 2026
e70971d
add ChangeLog entry
LevelVoid Aug 24, 2026
cc435ed
Merge branch 'main' into add-required-attributes
dbarker Aug 24, 2026
51e6d7c
update: htlHash uses openssl for SHA256
LevelVoid Aug 25, 2026
719d8df
Merge branch 'main' into add-required-attributes
LevelVoid Aug 25, 2026
45a4cc8
fix: remove unused import
LevelVoid Aug 25, 2026
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Increment the:

## [Unreleased]

* [RESOURCE DETECTOR] Add required and recommended attributes for process entity
[#4437](https://github.com/open-telemetry/opentelemetry-cpp/pull/4437)
* [CONFIGURATION] Add a configuration builder for the host resource detector
[#4451](https://github.com/open-telemetry/opentelemetry-cpp/issues/4451)
* [CONFIGURATION] Build the configured resource detectors in SdkBuilder, apply
Expand Down
1 change: 1 addition & 0 deletions resource_detectors/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ cc_library(
"//resource_detectors:headers",
"//sdk:headers",
"//sdk/src/resource",
"@boringssl//:crypto",
],
)

Expand Down
7 changes: 6 additions & 1 deletion resource_detectors/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# opentelemetry_container_resource_detector
#

find_package(OpenSSL REQUIRED)

add_library(opentelemetry_container_resource_detector
src/container_detector.cc src/container_detector_utils.cc)

Expand All @@ -23,6 +25,8 @@ target_include_directories(
# opentelemetry_container_resource_detector_builder
#

find_package(OpenSSL REQUIRED)

add_library(opentelemetry_container_resource_detector_builder
src/container_detector_builder.cc)

Expand Down Expand Up @@ -109,7 +113,8 @@ set_target_properties(opentelemetry_process_resource_detector
set_target_version(opentelemetry_process_resource_detector)

target_link_libraries(opentelemetry_process_resource_detector
PUBLIC opentelemetry_resources)
PUBLIC opentelemetry_resources
PRIVATE OpenSSL::Crypto)
target_include_directories(
opentelemetry_process_resource_detector
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
Expand Down
14 changes: 14 additions & 0 deletions resource_detectors/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ or inaccessible.
| --- | --- | --- | --- | --- |
| `process.pid` | Process ID | Yes | Yes | Yes |
| `process.executable.path` | Path via `/proc` (Linux) or Win32 APIs | Yes | Yes | Yes |
| `process.executable.name` | Basename of the executable path | Yes | Yes | Yes |
| `process.creation.time` | Process start time in ISO 8601 UTC | Yes | Yes | Yes |
| `process.owner` | Username of the process owner | Yes | Yes | Yes |
| `process.executable.build_id.htlhash` | Deterministic SHA256-based build ID | Yes | Yes | Yes |

Limitations:

- On macOS, `process.executable.path`, `process.executable.name`, and
`process.executable.build_id.htlhash` are resolved via `_NSGetExecutablePath()`,
which only works for the **current process**. These attributes are always
populated for the running process, but cannot be resolved for an arbitrary PID.
- For executables smaller than 4096 bytes the head and tail slices of the
htlhash algorithm overlap (both cover the entire file), which is correct per
the [spec](https://opentelemetry.io/docs/specs/semconv/attributes-registry/process/#algorithm-for-processexecutablebuild_idhtlhash).

### Env Entity Resource Detector (Experimental)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,32 @@ namespace resource_detector
namespace detail
{

/**
* Contains the path and name of the executable.
*/
struct ExecutableInfo
{
std::string path;
std::string name;
};

/**
* Forms a file path for a process type based on the given PID.
* for example - /proc/<pid>/cmdline, /proc/<pid>/exe
*/
std::string FormFilePath(const int32_t &pid, const char *process_type);

/**
* Retrieves the absolute file system path to the executable for a given PID.
* Retrieves the absolute file system path and the base name of the process executable.
* Platform-specific behavior:
* - Windows: Uses OpenProcess() + GetProcessImageFileNameW().
* - Windows: Uses OpenProcess() + QueryFullProcessImageNameW().
* - Linux/Unix: Reads the /proc/<pid>/exe symbolic link.
* - macOS: Uses _NSGetExecutablePath() for the current process only; returns
* an empty string for other PIDs.
*
* @param pid Process ID.
*/
std::string GetExecutablePath(const int32_t &pid);
ExecutableInfo GetExecutableInfo(const int32_t &pid);

/**
* Extracts the command-line arguments and the command.
Expand All @@ -49,6 +58,47 @@ std::vector<std::string> ExtractCommandWithArgs(const std::string &command_line_
*/
std::vector<std::string> GetCommandWithArgs(const int32_t &pid);

/**
* Retrieves the process creation time as an ISO 8601 string (e.g. "2023-11-21T09:25:34.853Z").
* Platform-specific behavior:
* - Linux: Reads starttime from /proc/<pid>/stat and combines with boot time.
* - macOS: Uses sysctl(KERN_PROC) to obtain kinfo_proc.kp_proc.p_starttime.
* - Windows: Uses GetProcessTimes() to obtain lpCreationTime (FILETIME).
* Returns an empty string if the information is unavailable.
*
* @param pid Process ID.
*/
std::string GetProcessCreationTime(const int32_t &pid);

/**
* Retrieves the username of the user that owns the process.
* Platform-specific behavior:
* - Linux/macOS: Uses getuid() + getpwuid_r() to resolve the effective user name.
* - Windows: Uses OpenProcessToken() + GetTokenInformation() + LookupAccountSidW().
* Returns an empty string if the information is unavailable.
*
* @param pid Process ID.
*/
std::string GetProcessOwner();

/**
* Computes the deterministic htlhash build ID for the process executable.
* Algorithm: SHA256(File[:4096] || File[-4096:] || BigEndianUInt64(FileLen))
* For files <= 4096 bytes the two slices overlap (both equal the whole file),
* matching the spec requirement that inputs are "not padded".
* The result is the first 16 bytes (128 bits) of the digest as a lowercase hex string.
* Returns an empty string if the executable cannot be read.
*
* @param pid Process ID.
*/
std::string GetExecutableBuildIdHtlhash(const int32_t &pid);

/**
* Computes a SHA-256 hash of the given data and returns it as a lowercase hex string.
* This is exposed primarily for unit testing the internal SHA-256 implementation.
*/
std::string ComputeSha256Hex(const std::string &data);

} // namespace detail
} // namespace resource_detector
OPENTELEMETRY_END_NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,28 @@ namespace resource_detector

/**
* ProcessResourceDetector to detect resource attributes when running in a process.
* This detector extracts metadata such as process ID, executable path, and command line arguments
* and sets attributes like process.pid, process.executable.path, and process.command following
* the OpenTelemetry semantic conventions.
* This detector extracts metadata such as process ID, executable path, executable name,
* process creation time, process owner, and executable build ID, then sets attributes
* following the OpenTelemetry semantic conventions:
*
* Process entity attributes:
* - process.pid (required) — current process identifier
Comment thread
dbarker marked this conversation as resolved.
* - process.creation.time (required) — ISO 8601 UTC creation timestamp
* - process.owner (recommended) — username of the process owner
*
* Process Executable entity attributes:
* - process.executable.path (recommended) — full path via /proc or Win32 APIs
* - process.executable.name (recommended) — basename of the executable path
* - process.executable.build_id.htlhash (required) — deterministic SHA256-based build ID
*
* Attributes that cannot be determined on the current platform are omitted.
*/
class ProcessResourceDetector : public opentelemetry::sdk::resource::ResourceDetector
{
public:
/**
* Detect retrieves the resource attributes for the current process.
* It reads:
* - process.pid from the current process ID
* - process.executable.path from the executable path of the current process
* - process.command from the command used to launch the process
* and returns a Resource with these attributes set.
* See the class-level documentation for the complete list of attributes populated.
*/
opentelemetry::sdk::resource::Resource Detect() noexcept override;
};
Expand Down
55 changes: 52 additions & 3 deletions resource_detectors/src/process_detector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ opentelemetry::sdk::resource::Resource ProcessResourceDetector::Detect() noexcep

try
{
std::string executable_path = opentelemetry::resource_detector::detail::GetExecutablePath(pid);
if (!executable_path.empty())
auto exe_info = opentelemetry::resource_detector::detail::GetExecutableInfo(pid);
if (!exe_info.path.empty())
{
attributes[semconv::process::kProcessExecutablePath] = std::move(executable_path);
attributes[semconv::process::kProcessExecutablePath] = std::move(exe_info.path);

if (!exe_info.name.empty())
{
attributes[semconv::process::kProcessExecutableName] = std::move(exe_info.name);
}
}
}
catch (const ::std::exception &ex)
Expand All @@ -65,6 +70,50 @@ opentelemetry::sdk::resource::Resource ProcessResourceDetector::Detect() noexcep
<< "Error extracting command with arguments: " << ex.what());
}

try
{
std::string creation_time =
opentelemetry::resource_detector::detail::GetProcessCreationTime(pid);
if (!creation_time.empty())
{
attributes[semconv::process::kProcessCreationTime] = std::move(creation_time);
}
}
catch (const std::exception &ex)
{
OTEL_INTERNAL_LOG_ERROR("[Process Resource Detector] "
<< "Error extracting process creation time: " << ex.what());
}

try
{
std::string owner = opentelemetry::resource_detector::detail::GetProcessOwner();
if (!owner.empty())
{
attributes[semconv::process::kProcessOwner] = std::move(owner);
}
}
catch (const std::exception &ex)
{
OTEL_INTERNAL_LOG_ERROR("[Process Resource Detector] " << "Error extracting process owner: "
<< ex.what());
}

try
{
std::string build_id =
opentelemetry::resource_detector::detail::GetExecutableBuildIdHtlhash(pid);
if (!build_id.empty())
{
attributes[semconv::process::kProcessExecutableBuildIdHtlhash] = std::move(build_id);
}
}
catch (const std::exception &ex)
{
OTEL_INTERNAL_LOG_ERROR("[Process Resource Detector] "
<< "Error computing executable build id (htlhash): " << ex.what());
}

return ResourceDetector::Create(attributes);
}

Expand Down
Loading