Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GetOneRootEvent method to process handler #150

Merged
merged 4 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
28 changes: 23 additions & 5 deletions metric/system/process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func ListStates(hostfs resolve.Resolver) ([]ProcState, error) {
}

// GetPIDState returns the state of a given PID
// It will return ProcNotExist if the process was not found.
// It will return ErrProcNotExist if the process was not found.
func GetPIDState(hostfs resolve.Resolver, pid int) (PidState, error) {
// This library still doesn't have a good cross-platform way to distinguish between "does not eixst" and other process errors.
// This is a fairly difficult problem to solve in a cross-platform way
Expand All @@ -72,7 +72,7 @@ func GetPIDState(hostfs resolve.Resolver, pid int) (PidState, error) {
return "", fmt.Errorf("Error trying to find process: %d: %w", pid, err)
}
if !exists {
return "", ProcNotExist
return "", ErrProcNotExist
}
// GetInfoForPid will return the smallest possible dataset for a PID
procState, err := GetInfoForPid(hostfs, pid)
Expand Down Expand Up @@ -123,9 +123,7 @@ func (procStats *Stats) Get() ([]mapstr.M, []mapstr.M, error) {
// Add the RSS pct memory first
process.Memory.Rss.Pct = GetProcMemPercentage(process, totalPhyMem)
// Create the root event
root := process.FormatForRoot()
rootMap := mapstr.M{}
_ = typeconv.Convert(&rootMap, root)
rootMap := processRootEvent(process)

proc, err := procStats.getProcessEvent(&process)
if err != nil {
Expand All @@ -151,6 +149,26 @@ func (procStats *Stats) GetOne(pid int) (mapstr.M, error) {
return procStats.getProcessEvent(&pidStat)
}

// GetOneRootEvent is the same as `GetOne()` but it returns an
// event formatted as expected by ECS
func (procStats *Stats) GetOneRootEvent(pid int) (mapstr.M, mapstr.M, error) {
pidStat, _, err := procStats.pidFill(pid, false)
if err != nil {
return nil, nil, fmt.Errorf("error fetching PID %d: %w", pid, err)
}

procStats.ProcsMap.SetPid(pid, pidStat)

procMap, err := procStats.getProcessEvent(&pidStat)
if err != nil {
return nil, nil, fmt.Errorf("error formatting process %d: %w", pid, err)
}

rootMap := processRootEvent(pidStat)

return procMap, rootMap, err
}

// GetSelf gets process info for the beat itself
func (procStats *Stats) GetSelf() (ProcState, error) {
self := os.Getpid()
Expand Down
15 changes: 13 additions & 2 deletions metric/system/process/process_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,18 @@ import (
"sync"

"github.com/elastic/elastic-agent-libs/logp"
"github.com/elastic/elastic-agent-libs/mapstr"
"github.com/elastic/elastic-agent-libs/match"
"github.com/elastic/elastic-agent-libs/transform/typeconv"
"github.com/elastic/elastic-agent-system-metrics/metric/system/cgroup"
"github.com/elastic/elastic-agent-system-metrics/metric/system/resolve"
"github.com/elastic/go-sysinfo/types"

"github.com/elastic/go-sysinfo"
)

// ProcNotExist indicates that a process was not found.
var ProcNotExist = errors.New("process does not exist")
// ErrProcNotExist indicates that a process was not found.
var ErrProcNotExist = errors.New("process does not exist")

// ProcsMap is a convenience wrapper for the oft-used idiom of map[int]ProcState
type ProcsMap map[int]ProcState
Expand Down Expand Up @@ -207,3 +209,12 @@ func (procStats *Stats) Init() error {
}
return nil
}

// processRootEvent formats the process state event for the ECS root fields used by the system/process metricsets
func processRootEvent(process ProcState) mapstr.M {
// Create the root event
root := process.FormatForRoot()
rootMap := mapstr.M{}
_ = typeconv.Convert(&rootMap, root)
return rootMap
}
31 changes: 31 additions & 0 deletions metric/system/process/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,37 @@ func TestGetState(t *testing.T) {
"got process state %q. Last error: %v", got, err)
}

func TestGetOneRoot(t *testing.T) {
testConfig := Stats{
Procs: []string{".*"},
Hostfs: resolve.NewTestResolver("/"),
CPUTicks: false,
CacheCmdLine: true,
EnvWhitelist: []string{".*"},
IncludeTop: IncludeTopConfig{
Enabled: true,
ByCPU: 4,
ByMemory: 0,
},
EnableCgroups: false,
CgroupOpts: cgroup.ReaderOptions{
RootfsMountpoint: resolve.NewTestResolver("/"),
IgnoreRootCgroups: true,
},
}
err := testConfig.Init()
assert.NoError(t, err, "Init")

evt, rootEvt, err := testConfig.GetOneRootEvent(os.Getpid())
require.NoError(t, err)

t.Logf("got event: %s\n root: %s", evt.StringToPrint(), rootEvt.StringToPrint())
fearful-symmetry marked this conversation as resolved.
Show resolved Hide resolved

require.NotEmpty(t, rootEvt["process"].(map[string]interface{})["pid"])

require.NotEmpty(t, evt["cpu"])
}

func TestGetOne(t *testing.T) {
testConfig := Stats{
Procs: []string{".*"},
Expand Down
2 changes: 2 additions & 0 deletions metric/system/process/process_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ func (t ProcFDInfo) IsZero() bool {
return t.Open.IsZero() && t.Limit.Hard.IsZero() && t.Limit.Soft.IsZero()
}

// FormatForRoot takes the ProcState event and turns the fields into a ProcStateRootEvent
// struct. These are the root fields expected for events sent from the system/process metricset.
func (p *ProcState) FormatForRoot() ProcStateRootEvent {
root := ProcStateRootEvent{}

Expand Down
Loading