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

feat(inputs.cgroup): Support more cgroup v2 formats #16474

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
51 changes: 47 additions & 4 deletions plugins/inputs/cgroup/cgroup_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ func (g *CGroup) Gather(acc telegraf.Accumulator) error {
acc.AddError(err)
}
}

return nil
}

Expand Down Expand Up @@ -175,7 +174,7 @@ type fileFormat struct {
}

const keyPattern = "[[:alnum:]:_.]+"
const valuePattern = "[\\d-]+"
const valuePattern = "(?:max|[\\d-\\.]+)"

var fileFormats = [...]fileFormat{
// VAL\n
Expand Down Expand Up @@ -205,9 +204,9 @@ var fileFormats = [...]fileFormat{
// VAL0 VAL1 ...\n
{
name: "Space separated values",
pattern: "^(" + valuePattern + " )+\n$",
pattern: "^(" + valuePattern + " ?)+\n$",
parser: func(measurement string, fields map[string]interface{}, b []byte) {
re := regexp.MustCompile("(" + valuePattern + ") ")
re := regexp.MustCompile("(" + valuePattern + ")")
matches := re.FindAllStringSubmatch(string(b), -1)
for i, v := range matches {
fields[measurement+"."+strconv.Itoa(i)] = numberOrString(v[1])
Expand All @@ -229,13 +228,57 @@ var fileFormats = [...]fileFormat{
}
},
},
// NAME0 KEY0=VAL0 ...\n
// NAME1 KEY1=VAL1 ...\n
// ...
{
name: "Equal sign separated key-value pairs, multiple lines with name",
pattern: fmt.Sprintf("^(%s( %s=%s)+\n)+$", keyPattern, keyPattern, valuePattern),
parser: func(measurement string, fields map[string]interface{}, b []byte) {
lines := strings.Split(string(b), "\n")
for _, line := range lines {
f := strings.Fields(line)
if len(f) == 0 {
continue
}
name := f[0]
for _, field := range f[1:] {
k, v, found := strings.Cut(field, "=")
if found {
fields[strings.Join([]string{measurement, name, k}, ".")] = numberOrString(v)
}
}
}
},
},
// KEY0=VAL0 KEY1=VAL1 ...\n
{
name: "Equal sign separated key-value pairs on a single line",
pattern: fmt.Sprintf("^(%s=%s ?)+\n$", keyPattern, valuePattern),
parser: func(measurement string, fields map[string]interface{}, b []byte) {
f := strings.Fields(string(b))
if len(f) == 0 {
return
}
for _, field := range f {
k, v, found := strings.Cut(field, "=")
if found {
fields[strings.Join([]string{measurement, k}, ".")] = numberOrString(v)
}
}
},
},
}

func numberOrString(s string) interface{} {
i, err := strconv.ParseInt(s, 10, 64)
if err == nil {
return i
}
f, err := strconv.ParseFloat(s, 64)
if err == nil {
return f
}

return s
}
Comment on lines 273 to 284
Copy link
Member

Choose a reason for hiding this comment

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

To be honest, this concerns me a bit as this calls for type conflicts. Imagine you get

MY_KEY=4

in a first gather cycle but

MY_KEY=3.14

in a second cycle. The very same field will be an integer in the first cycle but a float in the second cycle! This cases all sorts of issues on the output side. We've been there in the past...
I'm not sure if the issue exists for cgroups as the problem could be avoided if floats always contain a decimal separator but if that's not guaranteed I suggest to either have a named list of fields being float or to add a flag to always force fields into float (and not int).

Copy link
Author

Choose a reason for hiding this comment

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

As far as I understand the only files that have numbers with a fractional part are {cpu, io, memory}.pressure that use the PSI format.

They always have the decimal separator so in that case they will always be parsed as floats:

$ cat /sys/fs/cgroup/cpu.pressure
some avg10=0.00 avg60=0.00 avg300=0.00 total=397576165
full avg10=0.00 avg60=0.00 avg300=0.00 total=376593821

However, the issue you are describing could also happen for values that have "max" as the default but are otherwise ints... In that case what would be a good alternative to the "max" string? Maybe math.MaxInt64?

Copy link
Member

Choose a reason for hiding this comment

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

Are fields with the max string always of the integer type? If so, math.MaxInt64 would be the best way to go. For the PSI/float metrics, it seems like they always contain the decimal separator (e.g. 0.00) so this should be good. Maybe add a comment to the code for the next one stumbling upon this?

Expand Down
Loading
Loading