-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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 regex processor plugin #3839
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Regex Processor Plugin | ||
|
||
The `regex` plugin transforms tag and field values with regex pattern. If `result_key` parameter is present, it can produce new tags and fields from existing ones. | ||
|
||
### Configuration: | ||
|
||
```toml | ||
[[processors.regex]] | ||
namepass = ["nginx_requests"] | ||
|
||
# Tag and field conversions defined in a separate sub-tables | ||
[[processors.regex.tags]] | ||
## Tag to change | ||
key = "resp_code" | ||
## Regular expression to match on a tag value | ||
pattern = "^(\\d)\\d\\d$" | ||
## Pattern for constructing a new value (${1} represents first subgroup) | ||
replacement = "${1}xx" | ||
|
||
[[processors.regex.fields]] | ||
key = "request" | ||
## All the power of the Go regular expressions available here | ||
## For example, named subgroups | ||
pattern = "^/api(?P<method>/[\\w/]+)\\S*" | ||
replacement = "${method}" | ||
## If result_key is present, a new field will be created | ||
## instead of changing existing field | ||
result_key = "method" | ||
|
||
# Multiple conversions may be applied for one field sequentially | ||
# Let's extract one more value | ||
[[processors.regex.fields]] | ||
key = "request" | ||
pattern = ".*category=(\\w+).*" | ||
replacement = "${1}" | ||
result_key = "search_category" | ||
``` | ||
|
||
### Tags: | ||
|
||
No tags are applied by this processor. | ||
|
||
### Example Output: | ||
``` | ||
nginx_requests,verb=GET,resp_code=2xx request="/api/search/?category=plugins&q=regex&sort=asc",method="/search/",search_category="plugins",referrer="-",ident="-",http_version=1.1,agent="UserAgent",client_ip="127.0.0.1",auth="-",resp_bytes=270i 1519652321000000000 | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package regex | ||
|
||
import ( | ||
"regexp" | ||
|
||
"github.com/influxdata/telegraf" | ||
"github.com/influxdata/telegraf/plugins/processors" | ||
) | ||
|
||
type Regex struct { | ||
Tags []converter | ||
Fields []converter | ||
} | ||
|
||
type converter struct { | ||
Key string | ||
Pattern string | ||
Replacement string | ||
ResultKey string | ||
} | ||
|
||
const sampleConfig = ` | ||
## Tag and field conversions defined in a separate sub-tables | ||
# [[processors.regex.tags]] | ||
# ## Tag to change | ||
# key = "resp_code" | ||
# ## Regular expression to match on a tag value | ||
# pattern = "^(\\d)\\d\\d$" | ||
# ## Pattern for constructing a new value (${1} represents first subgroup) | ||
# replacement = "${1}xx" | ||
|
||
# [[processors.regex.fields]] | ||
# key = "request" | ||
# ## All the power of the Go regular expressions available here | ||
# ## For example, named subgroups | ||
# pattern = "^/api(?P<method>/[\\w/]+)\\S*" | ||
# replacement = "${method}" | ||
# ## If result_key is present, a new field will be created | ||
# ## instead of changing existing field | ||
# result_key = "method" | ||
|
||
## Multiple conversions may be applied for one field sequentially | ||
## Let's extract one more value | ||
# [[processors.regex.fields]] | ||
# key = "request" | ||
# pattern = ".*category=(\\w+).*" | ||
# replacement = "${1}" | ||
# result_key = "search_category" | ||
` | ||
|
||
func (r *Regex) SampleConfig() string { | ||
return sampleConfig | ||
} | ||
|
||
func (r *Regex) Description() string { | ||
return "Transforms tag and field values with regex pattern" | ||
} | ||
|
||
func (r *Regex) Apply(in ...telegraf.Metric) []telegraf.Metric { | ||
for _, metric := range in { | ||
for _, converter := range r.Tags { | ||
if value, ok := metric.Tags()[converter.Key]; ok { | ||
metric.AddTag( | ||
getKey(converter), | ||
getValue(converter, value), | ||
) | ||
} | ||
} | ||
|
||
for _, converter := range r.Fields { | ||
if value, ok := metric.Fields()[converter.Key]; ok { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
switch value := value.(type) { | ||
case string: | ||
metric.AddField( | ||
getKey(converter), | ||
getValue(converter, value), | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
return in | ||
} | ||
|
||
func getKey(c converter) string { | ||
if c.ResultKey != "" { | ||
return c.ResultKey | ||
} | ||
return c.Key | ||
} | ||
|
||
func getValue(c converter, value string) string { | ||
regex := regexp.MustCompile(c.Pattern) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should compile the regex pattern only once on startup, you could do this in Apply with a boolean field on the Regex type so it is only executed once. |
||
if c.ResultKey != "" && !regex.MatchString(value) { | ||
return "" | ||
} | ||
return regex.ReplaceAllString(value, c.Replacement) | ||
} | ||
|
||
func init() { | ||
processors.Add("regex", func() telegraf.Processor { | ||
return &Regex{} | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use
metric.GetTag
to avoid allocating.