in grok.go [307:362]
func (grok *Grok) expand(pattern string, namedCapturesOnly bool) (string, map[string]string, error) {
hints := make(map[string]string)
expandedPattern := pattern
// recursion break is guarding against cyclic reference in pattern definitions
// as this is performed only once at compile time more clever optimization (e.g detecting cycles in graph) is TBD
for recursionBreak := 1000; recursionBreak > 0; recursionBreak-- {
subMatches := reusePattern.FindAllStringSubmatch(expandedPattern, -1)
if len(subMatches) == 0 {
// nothing to expand anymore
break
}
for _, nameSubmatch := range subMatches {
// grok can be specified in either of these forms:
// %{SYNTAX} - e.g {NUMBER}
// %{SYNTAX:ID} - e.g {NUMBER:MY_AGE}
// %{SYNTAX:ID:TYPE} - e.g {NUMBER:MY_AGE:INT}
// nameSubmatch is equal to [["%{NAME:ID:TYPe}" "NAME:ID:TYPe"]]
// we need only inner part
nameParts := strings.Split(nameSubmatch[1], ":")
grokId := nameParts[0]
var targetId string
if len(nameParts) > 1 {
targetId = strings.ReplaceAll(nameParts[1], ".", dotSep)
} else {
targetId = nameParts[0]
}
// compile hints for used patterns
if len(nameParts) == 3 {
hints[targetId] = nameParts[2]
}
knownPattern, found := grok.lookupPattern(grokId)
if !found {
return "", nil, fmt.Errorf("pattern definition %q unknown: %w", grokId, ErrParseFailure)
}
var replacementPattern string
if namedCapturesOnly && len(nameParts) == 1 {
// this has no semantic (pattern:foo) so we don't need to capture
replacementPattern = "(" + knownPattern + ")"
} else {
replacementPattern = "(?P<" + targetId + ">" + knownPattern + ")"
}
// expand pattern with definition
expandedPattern = strings.ReplaceAll(expandedPattern, nameSubmatch[0], replacementPattern)
}
}
return expandedPattern, hints, nil
}