github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/logging/structure/structure.go (about)

     1  // Copyright Monax Industries Limited
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package structure
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  
    10  	"github.com/go-kit/kit/log"
    11  )
    12  
    13  const (
    14  	// Log time (time.Time)
    15  	TimeKey = "time"
    16  	// Call site for log invocation (go-stack.Call)
    17  	CallerKey = "caller"
    18  	// Trace for log call
    19  	TraceKey = "trace"
    20  	// Level name (string)
    21  	LevelKey = "level"
    22  	// Channel name in a vector channel logging context
    23  	ChannelKey = "log_channel"
    24  	// Log message (string)
    25  	MessageKey = "message"
    26  	// Error key
    27  	ErrorKey = "error"
    28  	// Tx hash key
    29  	TxHashKey = "tx_hash"
    30  	// Captured logging source (like tendermint_log15, stdlib_log)
    31  	CapturedLoggingSourceKey = "captured_logging_source"
    32  	// Top-level component (choose one) name
    33  	ComponentKey = "component"
    34  	// Tendermint component etc
    35  	Tendermint = "tendermint"
    36  	// Vector-valued scope
    37  	ScopeKey = "scope"
    38  	// Globally unique identifier persisting while a single instance (root process)
    39  	// of this program/service is running
    40  	RunId = "run_id"
    41  	// Provides special instructions (that may be ignored) to downstream loggers
    42  	SignalKey = "__signal__"
    43  	// The sync signal instructs sync-able loggers to sync
    44  	SyncSignal       = "__sync__"
    45  	ReloadSignal     = "__reload__"
    46  	InfoChannelName  = "Info"
    47  	TraceChannelName = "Trace"
    48  )
    49  
    50  // Pull the specified values from a structured log line into a map.
    51  // Assumes keys are single-valued.
    52  // Returns a map of the key-values from the requested keys and
    53  // the unmatched remainder keyvals as context as a slice of key-values.
    54  func ValuesAndContext(keyvals []interface{},
    55  	keys ...interface{}) (map[string]interface{}, []interface{}) {
    56  
    57  	vals := make(map[string]interface{}, len(keys))
    58  	context := make([]interface{}, len(keyvals))
    59  	copy(context, keyvals)
    60  	deletions := 0
    61  	// We can't really do better than a linear scan of both lists here. N is small
    62  	// so screw the asymptotics.
    63  	// Guard against odd-length list
    64  	for i := 0; i < 2*(len(keyvals)/2); i += 2 {
    65  		for k := 0; k < len(keys); k++ {
    66  			if keyvals[i] == keys[k] {
    67  				// Pull the matching key-value pair into vals to return
    68  				vals[Stringify(keys[k])] = keyvals[i+1]
    69  				// Delete the key once it's found
    70  				keys = DeleteAt(keys, k)
    71  				// And remove the key-value pair from context
    72  				context = Delete(context, i-deletions, 2)
    73  				// Keep a track of how much we've shrunk the context to offset next
    74  				// deletion
    75  				deletions += 2
    76  				break
    77  			}
    78  		}
    79  	}
    80  	return vals, context
    81  }
    82  
    83  // Returns keyvals as a map from keys to vals
    84  func KeyValuesMap(keyvals []interface{}) map[string]interface{} {
    85  	length := len(keyvals) / 2
    86  	vals := make(map[string]interface{}, length)
    87  	for i := 0; i < 2*length; i += 2 {
    88  		vals[Stringify(keyvals[i])] = keyvals[i+1]
    89  	}
    90  	return vals
    91  }
    92  
    93  func RemoveKeys(keyvals []interface{}, dropKeys ...interface{}) []interface{} {
    94  	return DropKeys(keyvals, func(key, value interface{}) bool {
    95  		for _, dropKey := range dropKeys {
    96  			if key == dropKey {
    97  				return true
    98  			}
    99  		}
   100  		return false
   101  	})
   102  }
   103  
   104  func OnlyKeys(keyvals []interface{}, includeKeys ...interface{}) []interface{} {
   105  	return DropKeys(keyvals, func(key, value interface{}) bool {
   106  		for _, includeKey := range includeKeys {
   107  			if key == includeKey {
   108  				return false
   109  			}
   110  		}
   111  		return true
   112  	})
   113  }
   114  
   115  // Drops all key value pairs where dropKeyValPredicate is true
   116  func DropKeys(keyvals []interface{}, dropKeyValPredicate func(key, value interface{}) bool) []interface{} {
   117  	keyvalsDropped := make([]interface{}, 0, len(keyvals))
   118  	for i := 0; i < 2*(len(keyvals)/2); i += 2 {
   119  		if !dropKeyValPredicate(keyvals[i], keyvals[i+1]) {
   120  			keyvalsDropped = append(keyvalsDropped, keyvals[i], keyvals[i+1])
   121  		}
   122  	}
   123  	return keyvalsDropped
   124  }
   125  
   126  // Stateful index that tracks the location of a possible vector value
   127  type vectorValueindex struct {
   128  	// Location of the value belonging to a key in output slice
   129  	valueIndex int
   130  	// Whether or not the value is currently a vector
   131  	vector bool
   132  }
   133  
   134  // To help with downstream serialisation
   135  type Vector []interface{}
   136  
   137  func (v Vector) Slice() []interface{} {
   138  	return v
   139  }
   140  
   141  func (v Vector) String() string {
   142  	return fmt.Sprintf("%v", v.Slice())
   143  }
   144  
   145  func (v Vector) MarshalJSON() ([]byte, error) {
   146  	return json.Marshal(v.Slice())
   147  }
   148  
   149  func (v Vector) MarshalText() ([]byte, error) {
   150  	return []byte(v.String()), nil
   151  }
   152  
   153  // 'Vectorises' values associated with repeated string keys member by collapsing many values into a single vector value.
   154  // The result is a copy of keyvals where the first occurrence of each matching key and its first value are replaced by
   155  // that key and all of its values in a single slice.
   156  func Vectorise(keyvals []interface{}, vectorKeys ...string) []interface{} {
   157  	// We rely on working against a single backing array, so we use a capacity that is the maximum possible size of the
   158  	// slice after vectorising (in the case there are no duplicate keys and this is a no-op)
   159  	outputKeyvals := make([]interface{}, 0, len(keyvals))
   160  	// Track the location and vector status of the values in the output
   161  	valueIndices := make(map[string]*vectorValueindex, len(vectorKeys))
   162  	elided := 0
   163  	for i := 0; i < 2*(len(keyvals)/2); i += 2 {
   164  		key := keyvals[i]
   165  		val := keyvals[i+1]
   166  
   167  		// Only attempt to vectorise string keys
   168  		if k, ok := key.(string); ok {
   169  			if valueIndices[k] == nil {
   170  				// Record that this key has been seen once
   171  				valueIndices[k] = &vectorValueindex{
   172  					valueIndex: i + 1 - elided,
   173  				}
   174  				// Copy the key-value to output with the single value
   175  				outputKeyvals = append(outputKeyvals, key, val)
   176  			} else {
   177  				// We have seen this key before
   178  				vi := valueIndices[k]
   179  				if !vi.vector {
   180  					// This must be the only second occurrence of the key so now vectorise the value
   181  					outputKeyvals[vi.valueIndex] = Vector([]interface{}{outputKeyvals[vi.valueIndex]})
   182  					vi.vector = true
   183  				}
   184  				// Grow the vector value
   185  				outputKeyvals[vi.valueIndex] = append(outputKeyvals[vi.valueIndex].(Vector), val)
   186  				// We are now running two more elements behind the input keyvals because we have absorbed this key-value pair
   187  				elided += 2
   188  			}
   189  		} else {
   190  			// Just copy the key-value to the output for non-string keys
   191  			outputKeyvals = append(outputKeyvals, key, val)
   192  		}
   193  	}
   194  	return outputKeyvals
   195  }
   196  
   197  // Return a single value corresponding to key in keyvals
   198  func Value(keyvals []interface{}, key interface{}) interface{} {
   199  	for i := 0; i < 2*(len(keyvals)/2); i += 2 {
   200  		if keyvals[i] == key {
   201  			return keyvals[i+1]
   202  		}
   203  	}
   204  	return nil
   205  }
   206  
   207  // Maps key values pairs with a function (key, value) -> (new key, new value)
   208  func MapKeyValues(keyvals []interface{}, fn func(interface{}, interface{}) (interface{}, interface{})) ([]interface{}, error) {
   209  	mappedKeyvals := make([]interface{}, 0)
   210  	for i := 0; i < len(keyvals); {
   211  		keymap, ok := keyvals[i].(map[string]interface{})
   212  		if ok {
   213  			for key, val := range keymap {
   214  				k, v := fn(key, val)
   215  				mappedKeyvals = append(mappedKeyvals, k, v)
   216  			}
   217  			i++
   218  		} else {
   219  			if i+1 >= len(keyvals) {
   220  				return nil, fmt.Errorf("log line contains an odd number of elements so "+
   221  					"was dropped: %v", keyvals)
   222  			}
   223  			k, v := fn(keyvals[i], keyvals[i+1])
   224  			mappedKeyvals = append(mappedKeyvals, k, v)
   225  			i += 2
   226  		}
   227  	}
   228  	return mappedKeyvals, nil
   229  }
   230  
   231  // Deletes n elements starting with the ith from a slice by splicing.
   232  // Beware uses append so the underlying backing array will be modified!
   233  func Delete(slice []interface{}, i int, n int) []interface{} {
   234  	return append(slice[:i], slice[i+n:]...)
   235  }
   236  
   237  // Delete an element at a specific index and return the contracted list
   238  func DeleteAt(slice []interface{}, i int) []interface{} {
   239  	return Delete(slice, i, 1)
   240  }
   241  
   242  // Provides a canonical way to stringify keys
   243  func Stringify(v interface{}) string {
   244  	switch v {
   245  	// For named keys we want to handle explicitly
   246  
   247  	default:
   248  		// Stringify keys
   249  		switch k := v.(type) {
   250  		case string:
   251  			return k
   252  		case fmt.Stringer:
   253  			return k.String()
   254  		default:
   255  			return fmt.Sprint(v)
   256  		}
   257  	}
   258  }
   259  
   260  // Sends the sync signal which causes any syncing loggers to sync.
   261  // loggers receiving the signal should drop the signal logline from output
   262  func Sync(logger log.Logger) error {
   263  	return logger.Log(SignalKey, SyncSignal)
   264  }
   265  
   266  func Reload(logger log.Logger) error {
   267  	return logger.Log(SignalKey, ReloadSignal)
   268  }
   269  
   270  // Tried to interpret the logline as a signal by matching the last key-value pair as a signal,
   271  // returns empty string if no match. The idea with signals is that the should be transmitted to a root logger
   272  // as a single key-value pair so we avoid the need to do a linear probe over every log line in order to detect a signal.
   273  func Signal(keyvals []interface{}) string {
   274  	last := len(keyvals) - 1
   275  	if last > 0 && keyvals[last-1] == SignalKey {
   276  		signal, ok := keyvals[last].(string)
   277  		if ok {
   278  			return signal
   279  		}
   280  	}
   281  	return ""
   282  }