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 }