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  }