github.com/prebid/prebid-server/v2@v2.18.0/config/structlog.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/golang/glog"
    10  )
    11  
    12  type logMsg func(string, ...interface{})
    13  
    14  var mapregex = regexp.MustCompile(`mapstructure:"([^"]+)"`)
    15  var blocklistregexp = []*regexp.Regexp{
    16  	regexp.MustCompile("password"),
    17  }
    18  
    19  // LogGeneral will log nearly any sort of value, but requires the name of the root object to be in the
    20  // prefix if you want that name to be logged. Structs will append .<fieldname> recursively to the prefix
    21  // to document deeper structure.
    22  func logGeneral(v reflect.Value, prefix string) {
    23  	logGeneralWithLogger(v, prefix, glog.Infof)
    24  }
    25  
    26  func logGeneralWithLogger(v reflect.Value, prefix string, logger logMsg) {
    27  	switch v.Kind() {
    28  	case reflect.Struct:
    29  		logStructWithLogger(v, prefix, logger)
    30  	case reflect.Map:
    31  		logMapWithLogger(v, prefix, logger)
    32  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    33  		logger("%s: %d", prefix, v.Int())
    34  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    35  		logger("%s: %d", prefix, v.Uint())
    36  	case reflect.Float32, reflect.Float64:
    37  		logger("%s: %f", prefix, v.Float())
    38  	case reflect.Bool:
    39  		logger("%s: %t", prefix, v.Bool())
    40  	default:
    41  		// logString, by using v.String(), will not fail, and indicate what additional cases we need to handle
    42  		logger("%s: %s", prefix, v.String())
    43  	}
    44  }
    45  
    46  func logStructWithLogger(v reflect.Value, prefix string, logger logMsg) {
    47  	if v.Kind() != reflect.Struct {
    48  		glog.Fatalf("LogStruct called on type %s, whuch is not a struct!", v.Type().String())
    49  	}
    50  	t := v.Type()
    51  	for i := 0; i < t.NumField(); i++ {
    52  		fieldname := fieldNameByTag(t.Field(i))
    53  		if allowedName(fieldname) {
    54  			logGeneralWithLogger(v.Field(i), extendPrefix(prefix, fieldname), logger)
    55  		} else {
    56  			logger("%s.%s: <REDACTED>", prefix, fieldname)
    57  		}
    58  	}
    59  }
    60  
    61  func logMapWithLogger(v reflect.Value, prefix string, logger logMsg) {
    62  	if v.Kind() != reflect.Map {
    63  		glog.Fatalf("LogMap called on type %s, whuch is not a map!", v.Type().String())
    64  	}
    65  	for _, k := range v.MapKeys() {
    66  		if k.Kind() == reflect.String && !allowedName(k.String()) {
    67  			logger("%s: <REDACTED>", extendMapPrefix(prefix, k.String()))
    68  		} else {
    69  			// Use Sprintf("%v", k.Interface) to handle non-string keys. Should not be possible to have a key
    70  			// too complex to represent by %v.
    71  			// NOTE: This will break if we have an unexported map in the object. If so we will have to switch
    72  			// on k.Kind() rather than rely on fmt.Sprintf("%v") doing that work.
    73  			logGeneralWithLogger(v.MapIndex(k), extendMapPrefix(prefix, fmt.Sprintf("%v", k.Interface())), logger)
    74  		}
    75  	}
    76  }
    77  
    78  func fieldNameByTag(f reflect.StructField) string {
    79  	match := mapregex.FindStringSubmatch(string(f.Tag))
    80  	if len(match) == 0 || len(match[1]) == 0 {
    81  		return fmt.Sprintf("((%s))", f.Name)
    82  	}
    83  	return match[1]
    84  }
    85  
    86  func allowedName(name string) bool {
    87  	for _, r := range blocklistregexp {
    88  		if r.MatchString(name) {
    89  			return false
    90  		}
    91  	}
    92  	return true
    93  }
    94  
    95  func extendPrefix(prefix string, field string) string {
    96  	if len(strings.Trim(prefix, " \t")) == 0 {
    97  		return fmt.Sprintf("%s%s", prefix, field)
    98  	}
    99  	return fmt.Sprintf("%s.%s", prefix, field)
   100  }
   101  
   102  func extendMapPrefix(prefix string, field string) string {
   103  	if len(strings.Trim(prefix, " \t")) == 0 {
   104  		return fmt.Sprintf("%s<map>[%s]", prefix, field)
   105  	}
   106  	return fmt.Sprintf("%s[%s]", prefix, field)
   107  }