1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,243 @@
# Regex Processor Plugin
This plugin transforms tag and field _values_ as well as renaming tags, fields
and metrics using regex patterns. Tag and field _values_ can be transformed
using named-groups in a batch fashion.
The regex processor **only operates on string fields**. It will not work on
any other data types, like an integer or float.
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Configuration
```toml @sample.conf
# Transforms tag and field values as well as measurement, tag and field names with regex pattern
[[processors.regex]]
namepass = ["nginx_requests"]
## Tag value conversion(s). Multiple instances are allowed.
[[processors.regex.tags]]
## Tag(s) to process with optional glob expressions such as '*'.
key = "resp_code"
## Regular expression to match the tag value. If the value doesn't
## match the tag is ignored.
pattern = "^(\\d)\\d\\d$"
## Replacement expression defining the value of the target tag. You can
## use regexp groups or named groups e.g. ${1} references the first group.
replacement = "${1}xx"
## Name of the target tag defaulting to 'key' if not specified.
## In case of wildcards being used in `key` the currently processed
## tag-name is used as target.
# result_key = "method"
## Appends the replacement to the target tag instead of overwriting it when
## set to true.
# append = false
## Field value conversion(s). Multiple instances are allowed.
[[processors.regex.fields]]
## Field(s) to process with optional glob expressions such as '*'.
key = "request"
## Regular expression to match the field value. If the value doesn't
## match or the field doesn't contain a string the field is ignored.
pattern = "^/api(?P<method>/[\\w/]+)\\S*"
## Replacement expression defining the value of the target field. You can
## use regexp groups or named groups e.g. ${method} references the group
## named "method".
replacement = "${method}"
## Name of the target field defaulting to 'key' if not specified.
## In case of wildcards being used in `key` the currently processed
## field-name is used as target.
# result_key = "method"
## Rename metric fields
[[processors.regex.field_rename]]
## Regular expression to match on the field name
pattern = "^search_(\\w+)d$"
## Replacement expression defining the name of the new field
replacement = "${1}"
## If the new field name already exists, you can either "overwrite" the
## existing one with the value of the renamed field OR you can "keep"
## both the existing and source field.
# result_key = "keep"
## Rename metric tags
[[processors.regex.tag_rename]]
## Regular expression to match on a tag name
pattern = "^search_(\\w+)d$"
## Replacement expression defining the name of the new tag
replacement = "${1}"
## If the new tag name already exists, you can either "overwrite" the
## existing one with the value of the renamed tag OR you can "keep"
## both the existing and source tag.
# result_key = "keep"
## Rename metrics
[[processors.regex.metric_rename]]
## Regular expression to match on an metric name
pattern = "^search_(\\w+)d$"
## Replacement expression defining the new name of the metric
replacement = "${1}"
```
Please note, you can use multiple `tags`, `fields`, `tag_rename`, `field_rename`
and `metric_rename` sections in one processor. All of those are applied.
### Tag and field _value_ conversions
Conversions are only applied if a tag/field _name_ matches the `key` which can
contain glob statements such as `*` (asterix) _and_ the `pattern` matches the
tag/field _value_. For fields the field values has to be of type `string` to
apply the conversion. If any of the given criteria does not apply the conversion
is not applied to the metric.
The `replacement` option specifies the value of the resulting tag or field. It
can reference capturing groups by index (e.g. `${1}` being the first group) or
by name (e.g. `${mygroup}` being the group named `mygroup`).
By default, the currently processed tag or field is overwritten by the
`replacement`. To create a new tag or field you can additionally specify the
`result_key` option containing the new target tag or field name. In case the
given tag or field already exists, its value is overwritten. For `tags` you
might use the `append` flag to append the `replacement` value to an existing
tag.
### Batch processing using named groups
In `tags` and `fields` sections it is possible to use named groups to create
multiple new tags or fields respectively. To do so, _all_ capture groups have
to be named in the `pattern`. Additional non-capturing ones or other
expressions are allowed. Furthermore, neither `replacement` nor `result_key`
can be set as the resulting tag/field name is the name of the group and the
value corresponds to the group's content.
### Tag and field _name_ conversions
You can batch-rename tags and fields using the `tag_rename` and `field_rename`
sections. Contrary to the `tags` and `fields` sections, the rename operates on
the tag or field _name_, not its _value_.
A tag or field is renamed if the given `pattern` matches the name. The new name
is specified via the `replacement` option. Optionally, the `result_key` can be
set to either `overwrite` or `keep` (default) to control the behavior in case
the target tag/field already exists. For `overwrite` the target tag/field is
replaced by the source key. With this setting, the source tag/field
is removed in any case. When using the `keep` setting (default), the target
tag/field as well as the source is left unchanged and no renaming takes place.
### Metric _name_ conversions
Similar to the tag and field renaming, `metric_rename` section(s) can be used
to rename metrics matching the given `pattern`. The resulting metric name is
given via `replacement` option. If matching `pattern` the conversion is always
applied. The `result_key` option has no effect on metric renaming and shall
not be specified.
## Tags
No tags are applied by this processor.
## Example
In the following examples we are using this metric
```text
nginx_requests,verb=GET,resp_code=200 request="/api/search/?category=plugins&q=regex&sort=asc",referrer="-",ident="-",http_version=1.1,agent="UserAgent",client_ip="127.0.0.1",auth="-",resp_bytes=270i 1519652321000000000
```
### Explicit specification
```toml
[[processors.regex]]
namepass = ["nginx_requests"]
[[processors.regex.tags]]
key = "resp_code"
pattern = "^(\\d)\\d\\d$"
replacement = "${1}xx"
[[processors.regex.fields]]
key = "request"
pattern = "^/api(?P<method>/[\\w/]+)\\S*"
replacement = "${method}"
result_key = "method"
[[processors.regex.fields]]
key = "request"
pattern = ".*category=(\\w+).*"
replacement = "${1}"
result_key = "search_category"
[[processors.regex.field_rename]]
pattern = "^client_(\\w+)$"
replacement = "${1}"
```
will result in
```diff
-nginx_requests,verb=GET,resp_code=200 request="/api/search/?category=plugins&q=regex&sort=asc",referrer="-",ident="-",http_version=1.1,agent="UserAgent",client_ip="127.0.0.1",auth="-",resp_bytes=270i 1519652321000000000
+nginx_requests,verb=GET,resp_code=2xx request="/api/search/?category=plugins&q=regex&sort=asc",method="/search/",category="plugins",referrer="-",ident="-",http_version=1.1,agent="UserAgent",ip="127.0.0.1",auth="-",resp_bytes=270i 1519652321000000000
```
### Appending
```toml
[[processors.regex]]
namepass = ["nginx_requests"]
[[processors.regex.tags]]
key = "resp_code"
pattern = '^2\d\d$'
replacement = " OK"
result_key = "verb"
append = true
```
will result in
```diff
-nginx_requests,verb=GET,resp_code=200 request="/api/search/?category=plugins&q=regex&sort=asc",referrer="-",ident="-",http_version=1.1,agent="UserAgent",client_ip="127.0.0.1",auth="-",resp_bytes=270i 1519652321000000000
+nginx_requests,verb=GET\ OK,resp_code=200 request="/api/search/?category=plugins&q=regex&sort=asc",referrer="-",ident="-",http_version=1.1,agent="UserAgent",client_ip="127.0.0.1",auth="-",resp_bytes=270i 1519652321000000000
```
### Named groups
```toml
[[processors.regex]]
namepass = ["nginx_requests"]
[[processors.regex.fields]]
key = "request"
pattern = '^/api/(?P<method>\w+)[/?].*category=(?P<category>\w+)&(?:.*)'
```
will result in
```diff
-nginx_requests,verb=GET,resp_code=200 request="/api/search/?category=plugins&q=regex&sort=asc",referrer="-",ident="-",http_version=1.1,agent="UserAgent",client_ip="127.0.0.1",auth="-",resp_bytes=270i 1519652321000000000
+nginx_requests,verb=GET,resp_code=200 request="/api/search/?category=plugins&q=regex&sort=asc",method="search",category="plugins",referrer="-",ident="-",http_version=1.1,agent="UserAgent",client_ip="127.0.0.1",auth="-",resp_bytes=270i 1519652321000000000
```
### Metric renaming
```toml
[[processors.regex]]
[[processors.regex.metric_rename]]
pattern = '^(\w+)_.*$'
replacement = "${1}"
```
will result in
```diff
-nginx_requests,verb=GET,resp_code=200 request="/api/search/?category=plugins&q=regex&sort=asc",referrer="-",ident="-",http_version=1.1,agent="UserAgent",client_ip="127.0.0.1",auth="-",resp_bytes=270i 1519652321000000000
+nginx,verb=GET,resp_code=200 request="/api/search/?category=plugins&q=regex&sort=asc",referrer="-",ident="-",http_version=1.1,agent="UserAgent",client_ip="127.0.0.1",auth="-",resp_bytes=270i 1519652321000000000
```

View file

@ -0,0 +1,269 @@
package regex
import (
"errors"
"fmt"
"regexp"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/filter"
)
type converterType int
const (
convertTags = iota
convertFields
convertTagRename
convertFieldRename
convertMetricRename
)
func (ct converterType) String() string {
switch ct {
case convertTags:
return "tags"
case convertFields:
return "fields"
case convertTagRename:
return "tag_rename"
case convertFieldRename:
return "field_rename"
case convertMetricRename:
return "metric_rename"
}
return fmt.Sprintf("unknown %d", int(ct))
}
type converter struct {
Key string `toml:"key"`
Pattern string `toml:"pattern"`
Replacement string `toml:"replacement"`
ResultKey string `toml:"result_key"`
Append bool `toml:"append"`
filter filter.Filter
re *regexp.Regexp
groups []string
apply func(m telegraf.Metric)
}
func (c *converter) setup(ct converterType, log telegraf.Logger) error {
// Compile the pattern
re, err := regexp.Compile(c.Pattern)
if err != nil {
return err
}
c.re = re
switch ct {
case convertTags, convertFields:
if c.Key == "" {
return errors.New("key required")
}
f, err := filter.Compile([]string{c.Key})
if err != nil {
return err
}
c.filter = f
// Check for named groups
if c.ResultKey == "" && c.Replacement == "" {
groups := c.re.SubexpNames()
allNamed := len(groups) > 1
for _, g := range groups[1:] {
if g == "" {
allNamed = false
break
}
}
if allNamed {
log.Debugf("%s: Using named-group mode...", ct)
c.groups = groups[1:]
} else {
msg := "Neither 'result_key' nor 'replacement' given with unnamed or mixed groups;"
msg += " using explicit, empty replacement!"
log.Warnf("%s: %s", ct, msg)
}
} else {
log.Debugf("%s: Using explicit mode...", ct)
}
case convertTagRename, convertFieldRename:
switch c.ResultKey {
case "":
c.ResultKey = "keep"
case "overwrite", "keep":
// Do nothing as those are valid choices
default:
return fmt.Errorf("invalid metrics result_key %q", c.ResultKey)
}
}
// Select the application function
switch ct {
case convertTags:
c.apply = c.applyTags
case convertFields:
c.apply = c.applyFields
case convertTagRename:
c.apply = c.applyTagRename
case convertFieldRename:
c.apply = c.applyFieldRename
case convertMetricRename:
c.apply = c.applyMetricRename
}
return nil
}
func (c *converter) applyTags(m telegraf.Metric) {
for _, tag := range m.TagList() {
if !c.filter.Match(tag.Key) || !c.re.MatchString(tag.Value) {
continue
}
// Handle named groups
if len(c.groups) > 0 {
matches := c.re.FindStringSubmatch(tag.Value)
for i, match := range matches[1:] {
if match == "" {
continue
}
name := c.groups[i]
if c.Append {
if v, ok := m.GetTag(name); ok {
match = v + match
}
}
m.AddTag(name, match)
}
continue
}
// Handle explicit replacements
newKey := tag.Key
if c.ResultKey != "" {
newKey = c.ResultKey
}
newValue := c.re.ReplaceAllString(tag.Value, c.Replacement)
if c.Append {
if v, ok := m.GetTag(newKey); ok {
newValue = v + newValue
}
}
m.AddTag(newKey, newValue)
}
}
func (c *converter) applyFields(m telegraf.Metric) {
for _, field := range m.FieldList() {
if !c.filter.Match(field.Key) {
continue
}
value, ok := field.Value.(string)
if !ok || !c.re.MatchString(value) {
continue
}
// Handle named groups
if len(c.groups) > 0 {
matches := c.re.FindStringSubmatch(value)
for i, match := range matches[1:] {
if match == "" {
continue
}
name := c.groups[i]
if c.Append {
if v, ok := m.GetTag(name); ok {
match = v + match
}
}
m.AddField(name, match)
}
continue
}
// Handle explicit replacements
newKey := field.Key
if c.ResultKey != "" {
newKey = c.ResultKey
}
newValue := c.re.ReplaceAllString(value, c.Replacement)
m.AddField(newKey, newValue)
}
}
func (c *converter) applyTagRename(m telegraf.Metric) {
replacements := make(map[string]string)
for _, tag := range m.TagList() {
name := tag.Key
if c.re.MatchString(name) {
newName := c.re.ReplaceAllString(name, c.Replacement)
if !m.HasTag(newName) {
// There is no colliding tag, we can just change the name.
tag.Key = newName
continue
}
if c.ResultKey == "overwrite" {
// We got a colliding tag, remember the replacement and do it later
replacements[name] = newName
}
}
}
// We needed to postpone the replacement as we cannot modify the tag-list
// while iterating it as this will result in invalid memory dereference panic.
for oldName, newName := range replacements {
value, ok := m.GetTag(oldName)
if !ok {
// Just in case the tag got removed in the meantime
continue
}
m.AddTag(newName, value)
m.RemoveTag(oldName)
}
}
func (c *converter) applyFieldRename(m telegraf.Metric) {
replacements := make(map[string]string)
for _, field := range m.FieldList() {
name := field.Key
if c.re.MatchString(name) {
newName := c.re.ReplaceAllString(name, c.Replacement)
if !m.HasField(newName) {
// There is no colliding field, we can just change the name.
field.Key = newName
continue
}
if c.ResultKey == "overwrite" {
// We got a colliding field, remember the replacement and do it later
replacements[name] = newName
}
}
}
// We needed to postpone the replacement as we cannot modify the field-list
// while iterating it as this will result in invalid memory dereference panic.
for oldName, newName := range replacements {
value, ok := m.GetField(oldName)
if !ok {
// Just in case the field got removed in the meantime
continue
}
m.AddField(newName, value)
m.RemoveField(oldName)
}
}
func (c *converter) applyMetricRename(m telegraf.Metric) {
value := m.Name()
if c.re.MatchString(value) {
newValue := c.re.ReplaceAllString(value, c.Replacement)
m.SetName(newValue)
}
}

View file

@ -0,0 +1,105 @@
//go:generate ../../../tools/readme_config_includer/generator
package regex
import (
_ "embed"
"fmt"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/processors"
)
//go:embed sample.conf
var sampleConfig string
type Regex struct {
Tags []converter `toml:"tags"`
Fields []converter `toml:"fields"`
TagRename []converter `toml:"tag_rename"`
FieldRename []converter `toml:"field_rename"`
MetricRename []converter `toml:"metric_rename"`
Log telegraf.Logger `toml:"-"`
}
func (*Regex) SampleConfig() string {
return sampleConfig
}
func (r *Regex) Init() error {
// Compile the regular expressions
for i := range r.Tags {
if err := r.Tags[i].setup(convertTags, r.Log); err != nil {
return fmt.Errorf("'tags' %w", err)
}
}
for i := range r.Fields {
if err := r.Fields[i].setup(convertFields, r.Log); err != nil {
return fmt.Errorf("'fields' %w", err)
}
}
for i, c := range r.TagRename {
if c.Key != "" {
r.Log.Info("'tag_rename' section contains a key which is ignored during processing")
}
if err := r.TagRename[i].setup(convertTagRename, r.Log); err != nil {
return fmt.Errorf("'tag_rename' %w", err)
}
}
for i, c := range r.FieldRename {
if c.Key != "" {
r.Log.Info("'field_rename' section contains a key which is ignored during processing")
}
if err := r.FieldRename[i].setup(convertFieldRename, r.Log); err != nil {
return fmt.Errorf("'field_rename' %w", err)
}
}
for i, c := range r.MetricRename {
if c.Key != "" {
r.Log.Info("'metric_rename' section contains a key which is ignored during processing")
}
if c.ResultKey != "" {
r.Log.Info("'metric_rename' section contains a 'result_key' ignored during processing as metrics will ALWAYS the name")
}
if err := r.MetricRename[i].setup(convertMetricRename, r.Log); err != nil {
return fmt.Errorf("'metric_rename' %w", err)
}
}
return nil
}
func (r *Regex) Apply(in ...telegraf.Metric) []telegraf.Metric {
for _, metric := range in {
for _, c := range r.Tags {
c.apply(metric)
}
for _, c := range r.Fields {
c.apply(metric)
}
for _, c := range r.TagRename {
c.apply(metric)
}
for _, c := range r.FieldRename {
c.apply(metric)
}
for _, c := range r.MetricRename {
c.apply(metric)
}
}
return in
}
func init() {
processors.Add("regex", func() telegraf.Processor { return &Regex{} })
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,66 @@
# Transforms tag and field values as well as measurement, tag and field names with regex pattern
[[processors.regex]]
namepass = ["nginx_requests"]
## Tag value conversion(s). Multiple instances are allowed.
[[processors.regex.tags]]
## Tag(s) to process with optional glob expressions such as '*'.
key = "resp_code"
## Regular expression to match the tag value. If the value doesn't
## match the tag is ignored.
pattern = "^(\\d)\\d\\d$"
## Replacement expression defining the value of the target tag. You can
## use regexp groups or named groups e.g. ${1} references the first group.
replacement = "${1}xx"
## Name of the target tag defaulting to 'key' if not specified.
## In case of wildcards being used in `key` the currently processed
## tag-name is used as target.
# result_key = "method"
## Appends the replacement to the target tag instead of overwriting it when
## set to true.
# append = false
## Field value conversion(s). Multiple instances are allowed.
[[processors.regex.fields]]
## Field(s) to process with optional glob expressions such as '*'.
key = "request"
## Regular expression to match the field value. If the value doesn't
## match or the field doesn't contain a string the field is ignored.
pattern = "^/api(?P<method>/[\\w/]+)\\S*"
## Replacement expression defining the value of the target field. You can
## use regexp groups or named groups e.g. ${method} references the group
## named "method".
replacement = "${method}"
## Name of the target field defaulting to 'key' if not specified.
## In case of wildcards being used in `key` the currently processed
## field-name is used as target.
# result_key = "method"
## Rename metric fields
[[processors.regex.field_rename]]
## Regular expression to match on the field name
pattern = "^search_(\\w+)d$"
## Replacement expression defining the name of the new field
replacement = "${1}"
## If the new field name already exists, you can either "overwrite" the
## existing one with the value of the renamed field OR you can "keep"
## both the existing and source field.
# result_key = "keep"
## Rename metric tags
[[processors.regex.tag_rename]]
## Regular expression to match on a tag name
pattern = "^search_(\\w+)d$"
## Replacement expression defining the name of the new tag
replacement = "${1}"
## If the new tag name already exists, you can either "overwrite" the
## existing one with the value of the renamed tag OR you can "keep"
## both the existing and source tag.
# result_key = "keep"
## Rename metrics
[[processors.regex.metric_rename]]
## Regular expression to match on an metric name
pattern = "^search_(\\w+)d$"
## Replacement expression defining the new name of the metric
replacement = "${1}"