github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/logging/logconfig/filter.go (about) 1 package logconfig 2 3 import ( 4 "fmt" 5 6 "regexp" 7 ) 8 9 func BuildFilterPredicate(filterConfig *FilterConfig) (func([]interface{}) bool, error) { 10 predicate, err := BuildKeyValuesPredicate(filterConfig.Predicates, filterConfig.FilterMode.MatchAll()) 11 if err != nil { 12 return nil, err 13 } 14 include := filterConfig.FilterMode.Include() 15 return func(keyvals []interface{}) bool { 16 // XOR the predicate with include. If include is true then negate the match. 17 return predicate(keyvals) != include 18 }, nil 19 } 20 21 func BuildKeyValuesPredicate(kvpConfigs []*KeyValuePredicateConfig, matchAll bool) (func([]interface{}) bool, error) { 22 length := len(kvpConfigs) 23 keyRegexes := make([]*regexp.Regexp, length) 24 valueRegexes := make([]*regexp.Regexp, length) 25 26 // Compile all KV regexes 27 for i, kvpConfig := range kvpConfigs { 28 // Store a nil regex to indicate no key match should occur 29 if kvpConfig.KeyRegex != "" { 30 keyRegex, err := regexp.Compile(kvpConfig.KeyRegex) 31 if err != nil { 32 return nil, err 33 } 34 keyRegexes[i] = keyRegex 35 } 36 // Store a nil regex to indicate no value match should occur 37 if kvpConfig.ValueRegex != "" { 38 valueRegex, err := regexp.Compile(kvpConfig.ValueRegex) 39 if err != nil { 40 return nil, err 41 } 42 valueRegexes[i] = valueRegex 43 } 44 } 45 46 return func(keyvals []interface{}) bool { 47 return matchLogLine(keyvals, keyRegexes, valueRegexes, matchAll) 48 }, nil 49 } 50 51 // matchLogLine tries to match a log line by trying to match each key value pair with each pair of key value regexes 52 // if matchAll is true then matchLogLine returns true iff every key value regexes finds a match or the line or regexes 53 // are empty 54 func matchLogLine(keyvals []interface{}, keyRegexes, valueRegexes []*regexp.Regexp, matchAll bool) bool { 55 all := matchAll 56 // We should be passed an aligned list of keyRegexes and valueRegexes, but since we can't error here we'll guard 57 // against a failure of the caller to pass valid arguments 58 length := len(keyRegexes) 59 if len(valueRegexes) < length { 60 length = len(valueRegexes) 61 } 62 for i := 0; i < length; i++ { 63 matched := findMatchInLogLine(keyvals, keyRegexes[i], valueRegexes[i]) 64 if matchAll { 65 all = all && matched 66 } else if matched { 67 return true 68 } 69 } 70 return all 71 } 72 73 func findMatchInLogLine(keyvals []interface{}, keyRegex, valueRegex *regexp.Regexp) bool { 74 for i := 0; i < 2*(len(keyvals)/2); i += 2 { 75 key := convertToString(keyvals[i]) 76 val := convertToString(keyvals[i+1]) 77 if key == nil && val == nil { 78 continue 79 } 80 // At least one of key or val could be converted from string, only match on either if the conversion worked 81 // Try to match on both key and value, falling back to a positive match if either or both or not supplied 82 if match(keyRegex, key) && match(valueRegex, val) { 83 return true 84 } 85 } 86 return false 87 } 88 89 func match(regex *regexp.Regexp, text *string) bool { 90 // Always match on a nil regex (indicating no constraint on text), 91 // and otherwise never match on nil text (indicating a non-string convertible type) 92 return regex == nil || (text != nil && regex.MatchString(*text)) 93 } 94 95 func convertToString(value interface{}) *string { 96 // We have the option of returning nil here to indicate a conversion was 97 // not possible/does not make sense. Although we are not opting to use it 98 // currently 99 switch v := value.(type) { 100 case string: 101 return &v 102 default: 103 s := fmt.Sprintf("%v", v) 104 return &s 105 } 106 }