diff --git a/.chloggen/add-process-start-attribute.yaml b/.chloggen/add-process-start-attribute.yaml new file mode 100755 index 000000000000..544a4aff71eb --- /dev/null +++ b/.chloggen/add-process-start-attribute.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: hostmetricsreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add a new resource attribute `process.start` to metrics created by the `process` scraper + +# One or more tracking issues related to the change +issues: [14084] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/documentation.md b/receiver/hostmetricsreceiver/internal/scraper/processscraper/documentation.md index 1915674c52e3..feace408ebcd 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/documentation.md +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/documentation.md @@ -35,6 +35,7 @@ metrics: | process.owner | The username of the user that owns the process. | Str | | process.parent_pid | Parent Process identifier (PPID). | Int | | process.pid | Process identifier (PID). | Int | +| process.start | The time the process started, e.g. '2016-05-23T08:05:34.853Z'. | Str | ## Metric attributes diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_metrics.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_metrics.go index 6348854b9865..edb7fc5d6cd3 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_metrics.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_metrics.go @@ -569,6 +569,13 @@ func WithProcessPid(val int64) ResourceMetricsOption { } } +// WithProcessStart sets provided value as "process.start" attribute for current resource. +func WithProcessStart(val string) ResourceMetricsOption { + return func(rm pmetric.ResourceMetrics) { + rm.Resource().Attributes().PutStr("process.start", val) + } +} + // WithStartTimeOverride overrides start time for all the resource metrics data points. // This option should be only used if different start time has to be set on metrics coming from different resources. func WithStartTimeOverride(start pcommon.Timestamp) ResourceMetricsOption { diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/metadata.yaml b/receiver/hostmetricsreceiver/internal/scraper/processscraper/metadata.yaml index e44d1e42f527..71bfef28a599 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/metadata.yaml +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/metadata.yaml @@ -37,6 +37,9 @@ resource_attributes: process.owner: description: The username of the user that owns the process. type: string + process.start: + description: The time the process started, e.g. '2016-05-23T08:05:34.853Z'. + type: string attributes: direction: diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process.go index cea7d473e954..8dcdb4ef0b3d 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process.go @@ -16,6 +16,7 @@ package processscraper // import "github.com/open-telemetry/opentelemetry-collec import ( "strings" + "time" "github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/process" @@ -55,6 +56,7 @@ func (m *processMetadata) resourceOptions() []metadata.ResourceMetricsOption { metadata.WithProcessParentPid(int64(m.parentPid)), metadata.WithProcessExecutableName(m.executable.name), metadata.WithProcessExecutablePath(m.executable.path), + metadata.WithProcessStart(time.UnixMilli(m.createTime).UTC().Format("2006-01-02T15:04:05.999Z")), ) if m.command != nil { opts = append(opts, metadata.WithProcessCommand(m.command.command)) diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_test.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_test.go index 66aba5879806..36f0a8105978 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_test.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_test.go @@ -66,8 +66,8 @@ func TestScrape(t *testing.T) { }, } - const createTime = 100 - const expectedStartTime = 100 * 1e6 + const createTime = 1664198907123 // 2022-09-26T13:28:27.123Z + const expectedStartTime = 1664198907123 * 1e6 for _, test := range testCases { t.Run(test.name, func(t *testing.T) { @@ -105,7 +105,7 @@ func TestScrape(t *testing.T) { } require.Greater(t, md.ResourceMetrics().Len(), 1) - assertProcessResourceAttributesExist(t, md.ResourceMetrics()) + assertProcessResourceAttributesValid(t, md.ResourceMetrics()) assertCPUTimeMetricValid(t, md.ResourceMetrics(), expectedStartTime) assertMemoryUsageMetricValid(t, md.ResourceMetrics(), expectedStartTime) assertOldDiskIOMetricValid(t, md.ResourceMetrics(), expectedStartTime) @@ -122,7 +122,7 @@ func TestScrape(t *testing.T) { } } -func assertProcessResourceAttributesExist(t *testing.T, resourceMetrics pmetric.ResourceMetricsSlice) { +func assertProcessResourceAttributesValid(t *testing.T, resourceMetrics pmetric.ResourceMetricsSlice) { for i := 0; i < resourceMetrics.Len(); i++ { attr := resourceMetrics.At(0).Resource().Attributes() internal.AssertContainsAttribute(t, attr, conventions.AttributeProcessPID) @@ -132,6 +132,7 @@ func assertProcessResourceAttributesExist(t *testing.T, resourceMetrics pmetric. internal.AssertContainsAttribute(t, attr, conventions.AttributeProcessCommandLine) internal.AssertContainsAttribute(t, attr, conventions.AttributeProcessOwner) internal.AssertContainsAttribute(t, attr, "process.parent_pid") + internal.AssertStringAttributeValue(t, attr, "process.start", "2022-09-26T13:28:27.123Z") } } diff --git a/receiver/hostmetricsreceiver/internal/testutils.go b/receiver/hostmetricsreceiver/internal/testutils.go index 8e7326f15892..ad70da97a88f 100644 --- a/receiver/hostmetricsreceiver/internal/testutils.go +++ b/receiver/hostmetricsreceiver/internal/testutils.go @@ -28,6 +28,12 @@ func AssertContainsAttribute(t *testing.T, attr pcommon.Map, key string) { assert.True(t, ok) } +func AssertStringAttributeValue(t *testing.T, attr pcommon.Map, key string, value string) { + attribute, ok := attr.Get(key) + assert.True(t, ok) + assert.Equal(t, value, attribute.Str()) +} + func AssertDescriptorEqual(t *testing.T, expected pmetric.Metric, actual pmetric.Metric) { assert.Equal(t, expected.Name(), actual.Name()) assert.Equal(t, expected.Description(), actual.Description())