diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/config.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/config.go index c8f9ff7c1600..3f3adc8f7e89 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/config.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/config.go @@ -31,6 +31,10 @@ type Config struct { // the collector does not have permission for. MuteProcessIOError bool `mapstructure:"mute_process_io_error,omitempty"` + // MuteProcessCgroupError is a flag that will mute the error encountered when trying to read the cgroup of a process + // the collector does not have permission for. + MuteProcessCgroupError bool `mapstructure:"mute_process_cgroup_error,omitempty"` + // ResilientProcessScraping is a flag that will let the collector continue reading a process even when // the collector does not have permission to read it's executable path (Linux) MuteProcessExeError bool `mapstructure:"mute_process_exe_error,omitempty"` diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/documentation.md b/receiver/hostmetricsreceiver/internal/scraper/processscraper/documentation.md index b8fbcbe66ff6..e182988ba097 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/documentation.md +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/documentation.md @@ -178,6 +178,7 @@ Process threads count. | Name | Description | Values | Enabled | | ---- | ----------- | ------ | ------- | +| process.cgroup | cgroup associated with the process. | Any Str | false | | process.command | The command used to launch the process (i.e. the command name). On Linux based systems, can be set to the zeroth string in proc/[pid]/cmdline. On Windows, can be set to the first parameter extracted from GetCommandLineW. | Any Str | true | | process.command_line | The full command used to launch the process as a single string representing the full command. On Windows, can be set to the result of GetCommandLineW. Do not set this if you have to assemble it just for monitoring; use process.command_args instead. | Any Str | true | | process.executable.name | The name of the process executable. On Linux based systems, can be set to the Name in proc/[pid]/status. On Windows, can be set to the base name of GetProcessImageFileNameW. | Any Str | true | diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_config.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_config.go index 887b329d6cca..c7d0162ea993 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_config.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_config.go @@ -105,6 +105,7 @@ func (rac *ResourceAttributeConfig) Unmarshal(parser *confmap.Conf) error { // ResourceAttributesConfig provides config for hostmetricsreceiver/process resource attributes. type ResourceAttributesConfig struct { + ProcessCgroup ResourceAttributeConfig `mapstructure:"process.cgroup"` ProcessCommand ResourceAttributeConfig `mapstructure:"process.command"` ProcessCommandLine ResourceAttributeConfig `mapstructure:"process.command_line"` ProcessExecutableName ResourceAttributeConfig `mapstructure:"process.executable.name"` @@ -116,6 +117,9 @@ type ResourceAttributesConfig struct { func DefaultResourceAttributesConfig() ResourceAttributesConfig { return ResourceAttributesConfig{ + ProcessCgroup: ResourceAttributeConfig{ + Enabled: false, + }, ProcessCommand: ResourceAttributeConfig{ Enabled: true, }, diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_config_test.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_config_test.go index 007ffa2ec44a..d6428b1701e2 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_config_test.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_config_test.go @@ -41,6 +41,7 @@ func TestMetricsBuilderConfig(t *testing.T) { ProcessThreads: MetricConfig{Enabled: true}, }, ResourceAttributes: ResourceAttributesConfig{ + ProcessCgroup: ResourceAttributeConfig{Enabled: true}, ProcessCommand: ResourceAttributeConfig{Enabled: true}, ProcessCommandLine: ResourceAttributeConfig{Enabled: true}, ProcessExecutableName: ResourceAttributeConfig{Enabled: true}, @@ -70,6 +71,7 @@ func TestMetricsBuilderConfig(t *testing.T) { ProcessThreads: MetricConfig{Enabled: false}, }, ResourceAttributes: ResourceAttributesConfig{ + ProcessCgroup: ResourceAttributeConfig{Enabled: false}, ProcessCommand: ResourceAttributeConfig{Enabled: false}, ProcessCommandLine: ResourceAttributeConfig{Enabled: false}, ProcessExecutableName: ResourceAttributeConfig{Enabled: false}, @@ -113,6 +115,7 @@ func TestResourceAttributesConfig(t *testing.T) { { name: "all_set", want: ResourceAttributesConfig{ + ProcessCgroup: ResourceAttributeConfig{Enabled: true}, ProcessCommand: ResourceAttributeConfig{Enabled: true}, ProcessCommandLine: ResourceAttributeConfig{Enabled: true}, ProcessExecutableName: ResourceAttributeConfig{Enabled: true}, @@ -125,6 +128,7 @@ func TestResourceAttributesConfig(t *testing.T) { { name: "none_set", want: ResourceAttributesConfig{ + ProcessCgroup: ResourceAttributeConfig{Enabled: false}, ProcessCommand: ResourceAttributeConfig{Enabled: false}, ProcessCommandLine: ResourceAttributeConfig{Enabled: false}, ProcessExecutableName: ResourceAttributeConfig{Enabled: false}, diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_metrics_test.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_metrics_test.go index 0a469f044a5a..b7f7849746e2 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_metrics_test.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_metrics_test.go @@ -99,6 +99,7 @@ func TestMetricsBuilder(t *testing.T) { mb.RecordProcessThreadsDataPoint(ts, 1) rb := mb.NewResourceBuilder() + rb.SetProcessCgroup("process.cgroup-val") rb.SetProcessCommand("process.command-val") rb.SetProcessCommandLine("process.command_line-val") rb.SetProcessExecutableName("process.executable.name-val") diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_resource.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_resource.go index 65440eb8560d..7af34e7d7ce1 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_resource.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_resource.go @@ -21,6 +21,13 @@ func NewResourceBuilder(rac ResourceAttributesConfig) *ResourceBuilder { } } +// SetProcessCgroup sets provided value as "process.cgroup" attribute. +func (rb *ResourceBuilder) SetProcessCgroup(val string) { + if rb.config.ProcessCgroup.Enabled { + rb.res.Attributes().PutStr("process.cgroup", val) + } +} + // SetProcessCommand sets provided value as "process.command" attribute. func (rb *ResourceBuilder) SetProcessCommand(val string) { if rb.config.ProcessCommand.Enabled { diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_resource_test.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_resource_test.go index 4f9873e4a515..316827f8299d 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_resource_test.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/generated_resource_test.go @@ -13,6 +13,7 @@ func TestResourceBuilder(t *testing.T) { t.Run(test, func(t *testing.T) { cfg := loadResourceAttributesConfig(t, test) rb := NewResourceBuilder(cfg) + rb.SetProcessCgroup("process.cgroup-val") rb.SetProcessCommand("process.command-val") rb.SetProcessCommandLine("process.command_line-val") rb.SetProcessExecutableName("process.executable.name-val") @@ -28,7 +29,7 @@ func TestResourceBuilder(t *testing.T) { case "default": assert.Equal(t, 7, res.Attributes().Len()) case "all_set": - assert.Equal(t, 7, res.Attributes().Len()) + assert.Equal(t, 8, res.Attributes().Len()) case "none_set": assert.Equal(t, 0, res.Attributes().Len()) return @@ -36,7 +37,12 @@ func TestResourceBuilder(t *testing.T) { assert.Failf(t, "unexpected test case: %s", test) } - val, ok := res.Attributes().Get("process.command") + val, ok := res.Attributes().Get("process.cgroup") + assert.Equal(t, test == "all_set", ok) + if ok { + assert.EqualValues(t, "process.cgroup-val", val.Str()) + } + val, ok = res.Attributes().Get("process.command") assert.True(t, ok) if ok { assert.EqualValues(t, "process.command-val", val.Str()) diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/testdata/config.yaml b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/testdata/config.yaml index 235b54919125..fab0758ec8d4 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/testdata/config.yaml +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/internal/metadata/testdata/config.yaml @@ -28,6 +28,8 @@ all_set: process.threads: enabled: true resource_attributes: + process.cgroup: + enabled: true process.command: enabled: true process.command_line: @@ -71,6 +73,8 @@ none_set: process.threads: enabled: false resource_attributes: + process.cgroup: + enabled: false process.command: enabled: false process.command_line: diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/metadata.yaml b/receiver/hostmetricsreceiver/internal/scraper/processscraper/metadata.yaml index 51fa89be2c3d..645116218955 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/metadata.yaml +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/metadata.yaml @@ -46,6 +46,10 @@ resource_attributes: description: The username of the user that owns the process. enabled: true type: string + process.cgroup: + description: cgroup associated with the process. + enabled: false + type: string attributes: direction: diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process.go index be5231652e3e..6c079c518ae3 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process.go @@ -31,8 +31,9 @@ type processMetadata struct { } type executableMetadata struct { - name string - path string + name string + path string + cgroup string } type commandMetadata struct { @@ -46,6 +47,7 @@ func (m *processMetadata) buildResource(rb *metadata.ResourceBuilder) pcommon.Re rb.SetProcessParentPid(int64(m.parentPid)) rb.SetProcessExecutableName(m.executable.name) rb.SetProcessExecutablePath(m.executable.path) + rb.SetProcessCgroup(m.executable.cgroup) if m.command != nil { rb.SetProcessCommand(m.command.command) if m.command.commandLineSlice != nil { @@ -73,6 +75,7 @@ type processHandles interface { type processHandle interface { NameWithContext(context.Context) (string, error) + CgroupWithContext(context.Context) (string, error) ExeWithContext(context.Context) (string, error) UsernameWithContext(context.Context) (string, error) CmdlineWithContext(context.Context) (string, error) diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper.go index f922031f663b..01d897fe6458 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper.go @@ -203,8 +203,15 @@ func (s *scraper) getProcessMetadata() ([]*processMetadata, error) { } continue } + cgroup, err := getProcessCgroup(ctx, handle) + if err != nil { + if !s.config.MuteProcessCgroupError { + errs.AddPartial(1, fmt.Errorf("error reading process cgroup for pid %v: %w", pid, err)) + } + continue + } - executable := &executableMetadata{name: name, path: exe} + executable := &executableMetadata{name: name, path: exe, cgroup: cgroup} // filter processes by name if (s.includeFS != nil && !s.includeFS.Matches(executable.name)) || diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_darwin.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_darwin.go index 93f5911882ba..2f06e6421753 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_darwin.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_darwin.go @@ -38,6 +38,15 @@ func getProcessName(ctx context.Context, proc processHandle, _ string) (string, return name, nil } +func getProcessCgroup(ctx context.Context, proc processHandle) (string, error) { + cgroup, err := proc.CgroupWithContext(ctx) + if err != nil { + return "", err + } + + return cgroup, nil +} + func getProcessExecutable(ctx context.Context, proc processHandle) (string, error) { cmdline, err := proc.CmdlineWithContext(ctx) if err != nil { diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_linux.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_linux.go index 923a73d1244b..17e5ac592976 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_linux.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_linux.go @@ -46,6 +46,15 @@ func getProcessExecutable(ctx context.Context, proc processHandle) (string, erro return exe, nil } +func getProcessCgroup(ctx context.Context, proc processHandle) (string, error) { + cgroup, err := proc.CgroupWithContext(ctx) + if err != nil { + return "", err + } + + return cgroup, nil +} + func getProcessCommand(ctx context.Context, proc processHandle) (*commandMetadata, error) { cmdline, err := proc.CmdlineSliceWithContext(ctx) if err != nil { diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_others.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_others.go index 15c9ee9aafe2..e2b45ec7cd6e 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_others.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_others.go @@ -25,6 +25,10 @@ func getProcessName(context.Context, processHandle, string) (string, error) { return "", nil } +func getProcessCgroup(ctx context.Context, proc processHandle) (string, error) { + return "", nil +} + func getProcessExecutable(context.Context, processHandle) (string, error) { return "", nil } diff --git a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_windows.go b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_windows.go index b9f68598c4b8..79ec41924f60 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_windows.go +++ b/receiver/hostmetricsreceiver/internal/scraper/processscraper/process_scraper_windows.go @@ -37,6 +37,15 @@ func getProcessName(_ context.Context, _ processHandle, exePath string) (string, return filepath.Base(exePath), nil } +func getProcessCgroup(ctx context.Context, proc processHandle) (string, error) { + cgroup, err := proc.CgroupWithContext(ctx) + if err != nil { + return "", err + } + + return cgroup, nil +} + func getProcessExecutable(ctx context.Context, proc processHandle) (string, error) { exe, err := proc.ExeWithContext(ctx) if err != nil {