-
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
Feature/3484 - set metric name with 'measurement' modifier for grok parser #4433
Changes from 48 commits
e12eced
08a11d7
9c4b522
4e24a1b
ec7f131
504d978
542c030
554b960
36a23ea
f40371e
9c84595
cc40629
79d9ea4
bbd68b3
bf7220d
a931eb1
e450b26
001658a
7fa27f4
1be2a8e
aa750ec
892c95a
04f09d6
8063b38
bfc13a7
67db143
8a9da28
cafa95e
c6087ab
e4b6f23
d224673
f52ceeb
285cf0b
0c3ac29
74900ed
d0f5389
b10f592
441bc41
d1e0c7c
b7ed886
903a977
0040530
054c20e
0e5e115
e3d9ca0
1b8ce4a
797f9bd
255e596
6d49188
34075e3
a246c11
4ae64bd
c019cfa
7e20044
0be9d85
5b1bbbd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,12 +22,12 @@ const ( | |
|
||
// LogParser in the primary interface for the plugin | ||
type GrokConfig struct { | ||
MeasurementName string `toml:"measurement"` | ||
Patterns []string | ||
NamedPatterns []string | ||
CustomPatterns string | ||
CustomPatternFiles []string | ||
TimeZone string | ||
MeasurementName string `toml:"measurement"` | ||
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. Revert change so there is less churn in the git history and to keep the real changes clear. |
||
} | ||
|
||
type logEntry struct { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,9 +44,9 @@ func TestGrokParseLogFiles(t *testing.T) { | |
|
||
logparser := &LogParserPlugin{ | ||
GrokConfig: GrokConfig{ | ||
MeasurementName: "logparser_grok", | ||
Patterns: []string{"%{TEST_LOG_A}", "%{TEST_LOG_B}"}, | ||
CustomPatternFiles: []string{thisdir + "grok/testdata/test-patterns"}, | ||
MeasurementName: "logparser_grok", | ||
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. Revert change |
||
}, | ||
FromBeginning: true, | ||
Files: []string{thisdir + "grok/testdata/*.log"}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,7 @@ var timeLayouts = map[string]string{ | |
} | ||
|
||
const ( | ||
MEASUREMENT = "measurement" | ||
INT = "int" | ||
TAG = "tag" | ||
FLOAT = "float" | ||
|
@@ -56,7 +57,7 @@ var ( | |
// %{IPORHOST:clientip:tag} | ||
// %{HTTPDATE:ts1:ts-http} | ||
// %{HTTPDATE:ts2:ts-"02 Jan 06 15:04"} | ||
modifierRe = regexp.MustCompile(`%{\w+:(\w+):(ts-".+"|t?s?-?\w+)}`) | ||
modifierRe = regexp.MustCompile(`%{\w+:(\w+|):(ts-".+"|t?s?-?\w+)}`) | ||
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. Let's just require a name that isn't used, similar to how timestamp works. This will make the code even simpler and we won't need to explain the new form. |
||
// matches a plain pattern name. ie, %{NUMBER} | ||
patternOnlyRe = regexp.MustCompile(`%{(\w+)}`) | ||
) | ||
|
@@ -74,6 +75,9 @@ type Parser struct { | |
Measurement string | ||
DefaultTags map[string]string | ||
|
||
//holds any modifiers set on named user patterns | ||
patternModifiers map[string][]string | ||
|
||
// Timezone is an optional component to help render log dates to | ||
// your chosen zone. | ||
// Default: "" which renders UTC | ||
|
@@ -126,6 +130,7 @@ func (p *Parser) Compile() error { | |
p.tsMap = make(map[string]map[string]string) | ||
p.patterns = make(map[string]string) | ||
p.tsModder = &tsModder{} | ||
p.patternModifiers = make(map[string][]string) | ||
var err error | ||
p.g, err = grok.NewWithConfig(&grok.Config{NamedCapturesOnly: true}) | ||
if err != nil { | ||
|
@@ -137,12 +142,32 @@ func (p *Parser) Compile() error { | |
p.NamedPatterns = make([]string, 0, len(p.Patterns)) | ||
for i, pattern := range p.Patterns { | ||
pattern = strings.TrimSpace(pattern) | ||
if pattern == "" { | ||
continue | ||
|
||
//checks that there is only one named field in pattern and 2 ':' indicating a modifier | ||
//then extract any modifiers off pattern | ||
if strings.Count(pattern, "%") == 1 && strings.Count(pattern, ":") == 2 { | ||
pattern = strings.Trim(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. This will leave a 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. Actually, i would just remove this 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. so i was under the impression that this form: |
||
splitPattern := strings.SplitN(pattern, ":", 3) | ||
if pattern == "" { | ||
continue | ||
} | ||
name := fmt.Sprintf("GROK_INTERNAL_PATTERN_%d", i) | ||
|
||
//map pattern modifiers by name | ||
p.patternModifiers["%{"+name+"}"] = splitPattern[1:3] | ||
p.CustomPatterns += "\n" + name + " " + "%{" + splitPattern[0] + "}" + "\n" | ||
p.NamedPatterns = append(p.NamedPatterns, "%{"+name+"}") | ||
} else { | ||
if strings.Contains(pattern, ":measurement}") { | ||
return fmt.Errorf("pattern with measurement modifier must have own 'pattern' field") | ||
} | ||
if pattern == "" { | ||
continue | ||
} | ||
name := fmt.Sprintf("GROK_INTERNAL_PATTERN_%d", i) | ||
p.CustomPatterns += "\n" + name + " " + pattern + "\n" | ||
p.NamedPatterns = append(p.NamedPatterns, "%{"+name+"}") | ||
} | ||
name := fmt.Sprintf("GROK_INTERNAL_PATTERN_%d", i) | ||
p.CustomPatterns += "\n" + name + " " + pattern + "\n" | ||
p.NamedPatterns = append(p.NamedPatterns, "%{"+name+"}") | ||
} | ||
|
||
if len(p.NamedPatterns) == 0 { | ||
|
@@ -213,7 +238,8 @@ func (p *Parser) ParseLine(line string) (telegraf.Metric, error) { | |
|
||
timestamp := time.Now() | ||
for k, v := range values { | ||
if k == "" || v == "" { | ||
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. still want this |
||
if (k == "" || v == "") && p.typeMap[patternName][k] != "measurement" { | ||
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. remove |
||
log.Printf("D! skipping key: %v", k) | ||
continue | ||
} | ||
|
||
|
@@ -238,6 +264,8 @@ func (p *Parser) ParseLine(line string) (telegraf.Metric, error) { | |
} | ||
|
||
switch t { | ||
case MEASUREMENT: | ||
p.Measurement = v | ||
case INT: | ||
iv, err := strconv.ParseInt(v, 10, 64) | ||
if err != nil { | ||
|
@@ -348,8 +376,17 @@ func (p *Parser) ParseLine(line string) (telegraf.Metric, error) { | |
} | ||
} | ||
|
||
//check the modifiers on the pattern | ||
modifiers, ok := p.patternModifiers[patternName] | ||
if ok && modifiers[1] == "measurement" { | ||
if p.patternModifiers[patternName][0] == "" { | ||
return nil, fmt.Errorf("pattern: %v must be named to use 'measurement' modifier", patternName) | ||
} | ||
p.Measurement = p.patternModifiers[patternName][0] | ||
} | ||
|
||
if len(fields) == 0 { | ||
return nil, fmt.Errorf("logparser_grok: must have one or more fields") | ||
return nil, fmt.Errorf("grok: must have one or more fields") | ||
} | ||
|
||
return metric.New(p.Measurement, tags, fields, p.tsModder.tsMod(timestamp)) | ||
|
@@ -453,6 +490,12 @@ func (p *Parser) parseTypedCaptures(name, pattern string) (string, error) { | |
} | ||
hasTimestamp = true | ||
} else { | ||
//for handling measurement tag with no name | ||
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. remove |
||
if match[1] == "" && match[2] == "measurement" { | ||
match[1] = "measurement_name" | ||
//add "measurement_name" to pattern so it is valid grok | ||
pattern = strings.Replace(pattern, "::measurement", ":measurement_name:measurement", 1) | ||
} | ||
p.typeMap[patternName][match[1]] = match[2] | ||
} | ||
|
||
|
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.
I think the patterns should behave the same regardless of if it is in the
pattern
orcustom_pattern
. Can we just say%{WORD::measurement}
will use the matched text and%{TEST:test_name:measurement}
will use the static valuetest_name
?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.
Yea I think that should be made clear. The issue with that would be if people use the dynamic name on an entire pattern, ie not a single field, it would add the text of the whole pattern as a metric name.