github.com/adacta-ru/mattermost-server/v6@v6.0.0/mlog/human/parser.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package human 5 6 import ( 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io" 11 "strconv" 12 "strings" 13 "time" 14 15 "github.com/adacta-ru/mattermost-server/v6/mlog" 16 ) 17 18 func ParseLogMessage(msg string) LogEntry { 19 result, err := parseLogMessage(msg) 20 if err != nil { 21 // If failed to parse, just output a LogEntry where all fields are blank, but Message is the original string 22 var result2 LogEntry 23 result2.Message = msg 24 return result2 25 } 26 return result 27 } 28 29 func parseLogMessage(msg string) (result LogEntry, err error) { 30 31 // Note: This implementation uses a custom json decoding loop. 32 // The primary advantage of this versus decoding directly into a map is to 33 // preserve the order of the fields. This can be simplified if we end up 34 // having the formatter sort fields alphabetically (logrus does by default) 35 36 dec := json.NewDecoder(strings.NewReader(msg)) 37 38 // look for an initial "{" 39 token, err := dec.Token() 40 if err != nil { 41 return result, err 42 } 43 d, ok := token.(json.Delim) 44 if !ok || d != '{' { 45 return result, fmt.Errorf("input is not a JSON object, found: %v", token) 46 } 47 48 // read all key-value pairs 49 for dec.More() { 50 key, err2 := dec.Token() 51 if err2 != nil { 52 return result, err2 53 } 54 skey, ok2 := key.(string) 55 if !ok2 { 56 return result, errors.New("key is not a value string") 57 } 58 if !dec.More() { 59 return result, errors.New("missing value pair") 60 } 61 62 switch skey { 63 case "ts": 64 var ts json.Number 65 if err2 := dec.Decode(&ts); err2 != nil { 66 return result, err2 67 } 68 timeVal, err2 := numberToTime(ts) 69 if err2 != nil { 70 return result, err2 71 } 72 result.Time = timeVal 73 74 case "level": 75 s, err2 := decodeAsString(dec) 76 if err2 != nil { 77 return result, err2 78 } 79 result.Level = s 80 81 case "msg": 82 s, err2 := decodeAsString(dec) 83 if err2 != nil { 84 return result, err2 85 } 86 result.Message = s 87 88 case "caller": 89 s, err2 := decodeAsString(dec) 90 if err2 != nil { 91 return result, err2 92 } 93 result.Caller = s 94 95 default: 96 var p interface{} 97 if err2 := dec.Decode(&p); err2 != nil { 98 return result, err2 99 } 100 var f mlog.Field 101 f.Key = skey 102 f.Interface = p 103 result.Fields = append(result.Fields, f) 104 } 105 } 106 107 // read the "}" 108 token, err = dec.Token() 109 if err != nil { 110 return result, err 111 } 112 d, ok = token.(json.Delim) 113 if !ok || d != '}' { 114 return result, fmt.Errorf("failed to read '}', read: %v", token) 115 } 116 117 // make sure nothing else trailing 118 if token, err := dec.Token(); err != io.EOF { 119 return result, err 120 } else if token != nil { 121 return result, errors.New("found trailing data") 122 } 123 124 return result, nil 125 } 126 127 // Translate a number into a time 128 func numberToTime(v json.Number) (time.Time, error) { 129 // Using floating point math to extract the nanoseconds leads to a time that doesn't exactly match the input 130 // Instead, parse out the components from the string representation 131 132 var t time.Time 133 134 // First make sure it is a number... 135 flt, err := v.Float64() 136 if err != nil { 137 return t, err 138 } 139 140 s := v.String() 141 142 if strings.ContainsAny(s, "eE") { 143 // input is in scientific notation. Convert to standard decimal notation 144 s = strconv.FormatFloat(flt, 'f', -1, 64) 145 } 146 147 // extract the seconds and nanoseconds separately 148 var nanos, sec int64 149 150 parts := strings.SplitN(s, ".", 2) 151 sec, err = strconv.ParseInt(parts[0], 10, 64) 152 if err != nil { 153 return t, err 154 } 155 156 if len(parts) == 2 { 157 nanosText := parts[1] + "000000000" 158 nanosText = nanosText[:9] 159 nanos, err = strconv.ParseInt(nanosText, 10, 64) 160 if err != nil { 161 return t, err 162 } 163 } 164 165 t = time.Unix(sec, nanos) 166 return t, nil 167 } 168 169 // Decodes a value from JSON, coercing it to a string value as necessary 170 func decodeAsString(dec *json.Decoder) (s string, err error) { 171 var v interface{} 172 if err = dec.Decode(&v); err != nil { 173 return s, err 174 } 175 var ok bool 176 if s, ok = v.(string); ok { 177 return s, err 178 } 179 s = fmt.Sprint(v) 180 return s, err 181 }