github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/config/struct_fields.go (about)

     1  package config
     2  
     3  import (
     4  	"reflect"
     5  	"strings"
     6  
     7  	"github.com/treeverse/lakefs/pkg/logging"
     8  )
     9  
    10  const (
    11  	FieldMaskedValue   = "******"
    12  	FieldMaskedNoValue = "------"
    13  )
    14  
    15  // MapLoggingFields returns all logging.Fields formatted based on our configuration keys 'dot.name.key' with
    16  // associated values. Supports squash, and secret to skip printing out secrets.
    17  func MapLoggingFields(value interface{}) logging.Fields {
    18  	fields := make(logging.Fields)
    19  	structFieldsFunc(reflect.ValueOf(value), "mapstructure", ",squash", nil, func(key string, value interface{}) {
    20  		fields[key] = value
    21  	})
    22  	return fields
    23  }
    24  
    25  func structFieldsFunc(value reflect.Value, tag, squashValue string, prefix []string, cb func(key string, value interface{})) {
    26  	// finite loop: Go types are well-founded.
    27  	for value.Kind() == reflect.Ptr {
    28  		if value.IsZero() {
    29  			// If required, would already have errored out.
    30  			return
    31  		}
    32  		value = value.Elem()
    33  	}
    34  
    35  	// Got to a value we like to call 'cb' with the key/value information
    36  	if value.Kind() != reflect.Struct {
    37  		key := strings.Join(prefix, sep)
    38  		cb(key, value)
    39  		return
    40  	}
    41  
    42  	// Scan the struct and
    43  	for i := 0; i < value.NumField(); i++ {
    44  		fieldType := value.Type().Field(i)
    45  		var (
    46  			// fieldName is the name to use for the field
    47  			fieldName string
    48  			// squash the sub-struct no additional accessor when true
    49  			squash bool
    50  			ok     bool
    51  		)
    52  		if fieldName, ok = fieldType.Tag.Lookup(tag); ok {
    53  			if strings.HasSuffix(fieldName, squashValue) {
    54  				squash = true
    55  				fieldName = strings.TrimSuffix(fieldName, squashValue)
    56  			}
    57  		} else {
    58  			fieldName = strings.ToLower(fieldType.Name)
    59  		}
    60  		// Update prefix to recurse into this field.
    61  		if !squash {
    62  			prefix = append(prefix, fieldName)
    63  		}
    64  		fieldValue := value.Field(i)
    65  
    66  		switch fieldValue.Interface().(type) {
    67  		case SecureString:
    68  			// don't pass value of SecureString
    69  			key := strings.Join(prefix, sep)
    70  			val := FieldMaskedValue
    71  			if fieldValue.IsZero() {
    72  				val = FieldMaskedNoValue
    73  			}
    74  			cb(key, val)
    75  		default:
    76  			structFieldsFunc(fieldValue, tag, squashValue, prefix, cb)
    77  		}
    78  		// Restore prefix
    79  		if !squash {
    80  			prefix = prefix[:len(prefix)-1]
    81  		}
    82  	}
    83  }