github.com/MetalBlockchain/subnet-evm@v0.4.9/plugin/evm/log.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package evm 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "io" 10 "reflect" 11 "time" 12 13 "github.com/ethereum/go-ethereum/log" 14 ) 15 16 const ( 17 errorKey = "LOG15_ERROR" 18 timeFormat = "2006-01-02T15:04:05-0700" 19 ) 20 21 type SubnetEVMLogger struct { 22 log.Handler 23 } 24 25 // InitLogger initializes logger with alias and sets the log level and format with the original [os.StdErr] interface 26 // along with the context logger. 27 func InitLogger(alias string, level string, jsonFormat bool, writer io.Writer) (SubnetEVMLogger, error) { 28 logFormat := SubnetEVMTermFormat(alias) 29 if jsonFormat { 30 logFormat = SubnetEVMJSONFormat(alias) 31 } 32 33 // Create handler 34 logHandler := log.StreamHandler(writer, logFormat) 35 c := SubnetEVMLogger{Handler: logHandler} 36 37 if err := c.SetLogLevel(level); err != nil { 38 return SubnetEVMLogger{}, err 39 } 40 return c, nil 41 } 42 43 // SetLogLevel sets the log level of initialized log handler. 44 func (c *SubnetEVMLogger) SetLogLevel(level string) error { 45 // Set log level 46 logLevel, err := log.LvlFromString(level) 47 if err != nil { 48 return err 49 } 50 log.Root().SetHandler(log.LvlFilterHandler(logLevel, c)) 51 return nil 52 } 53 54 func SubnetEVMTermFormat(alias string) log.Format { 55 prefix := fmt.Sprintf("<%s Chain>", alias) 56 return log.FormatFunc(func(r *log.Record) []byte { 57 location := fmt.Sprintf("%+v", r.Call) 58 newMsg := fmt.Sprintf("%s %s: %s", prefix, location, r.Msg) 59 r.Msg = newMsg 60 return log.TerminalFormat(false).Format(r) 61 }) 62 } 63 64 func SubnetEVMJSONFormat(alias string) log.Format { 65 prefix := fmt.Sprintf("%s Chain", alias) 66 return log.FormatFunc(func(r *log.Record) []byte { 67 props := make(map[string]interface{}, 5+len(r.Ctx)/2) 68 props["timestamp"] = r.Time 69 props["level"] = r.Lvl.String() 70 props[r.KeyNames.Msg] = r.Msg 71 props["logger"] = prefix 72 props["caller"] = fmt.Sprintf("%+v", r.Call) 73 for i := 0; i < len(r.Ctx); i += 2 { 74 k, ok := r.Ctx[i].(string) 75 if !ok { 76 props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i]) 77 } else { 78 // The number of arguments is normalized from the geth logger 79 // to ensure that this will not cause an index out of bounds error 80 props[k] = formatJSONValue(r.Ctx[i+1]) 81 } 82 } 83 84 b, err := json.Marshal(props) 85 if err != nil { 86 b, _ = json.Marshal(map[string]string{ 87 errorKey: err.Error(), 88 }) 89 return b 90 } 91 92 b = append(b, '\n') 93 return b 94 }) 95 } 96 97 func formatJSONValue(value interface{}) (result interface{}) { 98 defer func() { 99 if err := recover(); err != nil { 100 if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() { 101 result = "nil" 102 } else { 103 panic(err) 104 } 105 } 106 }() 107 108 switch v := value.(type) { 109 case time.Time: 110 return v.Format(timeFormat) 111 112 case error: 113 return v.Error() 114 115 case fmt.Stringer: 116 return v.String() 117 118 default: 119 return v 120 } 121 }