Skip to content
Open
Show file tree
Hide file tree
Changes from 13 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 src/OpenTelemetry.Resources.Process/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

* Extended `ProcessDetector` to include `process.executable.path`,
`process.working_directory`, `process.args_count`,`process.creation.time`,
`process.executable.name`, `process.interactive` as well as `process.title`
as per the latest OpenTelemetry Semantic Convention resource/entity definition.
([#2971](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3347))

## 1.13.0-beta.1

Released 2025-Oct-22
Expand Down
24 changes: 15 additions & 9 deletions src/OpenTelemetry.Resources.Process/ProcessDetector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,26 @@ internal sealed class ProcessDetector : IResourceDetector
/// <returns>Resource with key-value pairs of resource attributes.</returns>
public Resource Detect()
{
return new Resource(new List<KeyValuePair<string, object>>(2)
using var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
var attributes = new List<KeyValuePair<string, object>>(9)
{
new(ProcessSemanticConventions.AttributeProcessOwner, Environment.UserName),
new(ProcessSemanticConventions.AttributeProcessArgsCount, Environment.GetCommandLineArgs().Length),
new(ProcessSemanticConventions.AttributeProcessStartTime, currentProcess.StartTime.ToString("O") ?? string.Empty),
new(ProcessSemanticConventions.AttributeProcessTitle, currentProcess.MainWindowTitle),
new(ProcessSemanticConventions.AttributeProcessWorkingDir, Environment.CurrentDirectory),

new(ProcessSemanticConventions.AttributeProcessExecName, currentProcess.ProcessName),
Copy link
Contributor

Choose a reason for hiding this comment

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

Some process related properties may throw exceptions. For example, StartTime throws 3 exceptions, and there is likely no way to detect errors like Win32Exception ahead of time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking at the exceptions the only one we are interested in is win32 so have added a try catch and also exited earlier if process has exited.

The others are for scenarios we don't support ie windows 98.

The current directory does through a number of exceptions but they are for the set method which is not used.

new(ProcessSemanticConventions.AttributeProcessInteractive, Environment.UserInteractive),
#if NET
new(ProcessSemanticConventions.AttributeProcessPid, Environment.ProcessId),
});
new(ProcessSemanticConventions.AttributeProcessExecPath, Environment.ProcessPath ?? string.Empty),
};
#else
new(ProcessSemanticConventions.AttributeProcessPid, GetProcessPid()),
});
static int GetProcessPid()
{
using var process = System.Diagnostics.Process.GetCurrentProcess();
return process.Id;
}
new(ProcessSemanticConventions.AttributeProcessPid, currentProcess.Id),
};
#endif

return new Resource(attributes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,11 @@ internal static class ProcessSemanticConventions
{
public const string AttributeProcessOwner = "process.owner";
public const string AttributeProcessPid = "process.pid";
public const string AttributeProcessExecPath = "process.executable.path";
public const string AttributeProcessWorkingDir = "process.working_directory";
public const string AttributeProcessArgsCount = "process.args_count";
public const string AttributeProcessStartTime = "process.creation.time";
public const string AttributeProcessExecName = "process.executable.name";
public const string AttributeProcessInteractive = "process.interactive";
public const string AttributeProcessTitle = "process.title";
}
4 changes: 3 additions & 1 deletion src/OpenTelemetry.Resources.Process/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ using var loggerFactory = LoggerFactory.Create(builder =>
The resource detectors will record the following metadata based on where
your application is running:

- **ProcessDetector**: `process.owner`, `process.pid`.
- **ProcessDetector**: `process.owner`, `process.pid`, `process.executable.path`,
`process.working_directory`, `process.args_count`,`process.creation.time`,
`process.executable.name`, `process.interactive`, `process.title`.

## References

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,23 @@ public void TestProcessAttributes()
var resource = ResourceBuilder.CreateEmpty().AddProcessDetector().Build();

var resourceAttributes = resource.Attributes.ToDictionary(x => x.Key, x => x.Value);
#if NET
Assert.Equal(9, resourceAttributes.Count);
#else
Assert.Equal(8, resourceAttributes.Count);
#endif

Assert.Equal(2, resourceAttributes.Count);

Assert.IsType<long>(resourceAttributes[ProcessSemanticConventions.AttributeProcessArgsCount]);
Assert.IsType<string>(resourceAttributes[ProcessSemanticConventions.AttributeProcessExecName]);
#if NET
Assert.IsType<string>(resourceAttributes[ProcessSemanticConventions.AttributeProcessExecPath]);
#endif
Assert.IsType<bool>(resourceAttributes[ProcessSemanticConventions.AttributeProcessInteractive]);
Assert.IsType<string>(resourceAttributes[ProcessSemanticConventions.AttributeProcessOwner]);
Assert.IsType<long>(resourceAttributes[ProcessSemanticConventions.AttributeProcessPid]);

Assert.IsType<string>(resourceAttributes[ProcessSemanticConventions.AttributeProcessStartTime]);
Assert.IsType<string>(resourceAttributes[ProcessSemanticConventions.AttributeProcessTitle]);
Assert.IsType<string>(resourceAttributes[ProcessSemanticConventions.AttributeProcessWorkingDir]);
}
}