github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/log/format.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:41</date> 10 //</624342646340456448> 11 12 package log 13 14 import ( 15 "bytes" 16 "encoding/json" 17 "fmt" 18 "reflect" 19 "strconv" 20 "strings" 21 "sync" 22 "sync/atomic" 23 "time" 24 "unicode/utf8" 25 ) 26 27 const ( 28 timeFormat = "2006-01-02T15:04:05-0700" 29 termTimeFormat = "01-02|15:04:05.000" 30 floatFormat = 'f' 31 termMsgJust = 40 32 ) 33 34 //为了避免不灵活的日志行,对locationtrims进行了修剪以供显示。 35 var locationTrims = []string{ 36 "github.com/ethereum/go-ethereum/", 37 } 38 39 //printorigins设置或取消设置终端的日志位置(文件:行)打印 40 //格式输出。 41 func PrintOrigins(print bool) { 42 if print { 43 atomic.StoreUint32(&locationEnabled, 1) 44 } else { 45 atomic.StoreUint32(&locationEnabled, 0) 46 } 47 } 48 49 //locationEnabled是一个原子标记,用于控制终端格式化程序 50 //打印条目时也应附加日志位置。 51 var locationEnabled uint32 52 53 //locationLength是遇到的最大路径长度,所有日志都是 54 //填充以帮助对齐。 55 var locationLength uint32 56 57 //FieldPadding是一个全局映射,迄今为止字段值长度最大。 58 //以更智能的方式填充日志上下文。 59 var fieldPadding = make(map[string]int) 60 61 //FieldPaddingLock是一个全局互斥体,用于保护字段填充映射。 62 var fieldPaddingLock sync.RWMutex 63 64 type Format interface { 65 Format(r *Record) []byte 66 } 67 68 //formatfunc返回一个新的格式对象,该对象使用 69 //执行记录格式设置的给定函数。 70 func FormatFunc(f func(*Record) []byte) Format { 71 return formatFunc(f) 72 } 73 74 type formatFunc func(*Record) []byte 75 76 func (f formatFunc) Format(r *Record) []byte { 77 return f(r) 78 } 79 80 //TerminalStringer是与stdlib stringer类似的接口,允许 81 //当打印到 82 //屏幕。 83 type TerminalStringer interface { 84 TerminalString() string 85 } 86 87 //TerminalFormat格式为人类可读性优化的日志记录 88 //一种具有彩色编码电平输出和用户友好时间戳的终端。 89 //此格式只能用于交互式程序或在开发时使用。 90 // 91 //[级别][时间]mesage key=value key=value… 92 // 93 //例子: 94 // 95 //[DBUG][5月16日20:58:45]删除路由ns=haproxy addr=127.0.0.1:50002 96 // 97 func TerminalFormat(usecolor bool) Format { 98 return FormatFunc(func(r *Record) []byte { 99 var color = 0 100 if usecolor { 101 switch r.Lvl { 102 case LvlCrit: 103 color = 35 104 case LvlError: 105 color = 31 106 case LvlWarn: 107 color = 33 108 case LvlInfo: 109 color = 32 110 case LvlDebug: 111 color = 36 112 case LvlTrace: 113 color = 34 114 } 115 } 116 117 b := &bytes.Buffer{} 118 lvl := r.Lvl.AlignedString() 119 if atomic.LoadUint32(&locationEnabled) != 0 { 120 //已请求日志源打印,设置位置路径和行号的格式 121 location := fmt.Sprintf("%+v", r.Call) 122 for _, prefix := range locationTrims { 123 location = strings.TrimPrefix(location, prefix) 124 } 125 //保持风扇对准的最大位置长度 126 align := int(atomic.LoadUint32(&locationLength)) 127 if align < len(location) { 128 align = len(location) 129 atomic.StoreUint32(&locationLength, uint32(align)) 130 } 131 padding := strings.Repeat(" ", align-len(location)) 132 133 //组装并打印日志标题 134 if color > 0 { 135 fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg) 136 } else { 137 fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg) 138 } 139 } else { 140 if color > 0 { 141 fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg) 142 } else { 143 fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg) 144 } 145 } 146 //尝试调整短消息的日志输出 147 length := utf8.RuneCountInString(r.Msg) 148 if len(r.Ctx) > 0 && length < termMsgJust { 149 b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length)) 150 } 151 //打印钥匙日志Fmt样式 152 logfmt(b, r.Ctx, color, true) 153 return b.Bytes() 154 }) 155 } 156 157 //logfmt format以logfmt格式打印记录,这是一种易于机器分析但可供人阅读的格式。 158 //键/值对的格式。 159 // 160 //有关更多详细信息,请参阅:http://godoc.org/github.com/kr/logfmt 161 // 162 func LogfmtFormat() Format { 163 return FormatFunc(func(r *Record) []byte { 164 common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg} 165 buf := &bytes.Buffer{} 166 logfmt(buf, append(common, r.Ctx...), 0, false) 167 return buf.Bytes() 168 }) 169 } 170 171 func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) { 172 for i := 0; i < len(ctx); i += 2 { 173 if i != 0 { 174 buf.WriteByte(' ') 175 } 176 177 k, ok := ctx[i].(string) 178 v := formatLogfmtValue(ctx[i+1], term) 179 if !ok { 180 k, v = errorKey, formatLogfmtValue(k, term) 181 } 182 183 //我们应该检查一下你所有的密钥字节是否都是无效的。 184 fieldPaddingLock.RLock() 185 padding := fieldPadding[k] 186 fieldPaddingLock.RUnlock() 187 188 length := utf8.RuneCountInString(v) 189 if padding < length { 190 padding = length 191 192 fieldPaddingLock.Lock() 193 fieldPadding[k] = padding 194 fieldPaddingLock.Unlock() 195 } 196 if color > 0 { 197 fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, k) 198 } else { 199 buf.WriteString(k) 200 buf.WriteByte('=') 201 } 202 buf.WriteString(v) 203 if i < len(ctx)-2 { 204 buf.Write(bytes.Repeat([]byte{' '}, padding-length)) 205 } 206 } 207 buf.WriteByte('\n') 208 } 209 210 //JSonFormat将日志记录格式化为用换行符分隔的JSON对象。 211 //它相当于jsonFormatex(false,true)。 212 func JSONFormat() Format { 213 return JSONFormatEx(false, true) 214 } 215 216 //jsonFormatOrderedEx将日志记录格式化为json数组。如果漂亮是真的, 217 //记录将被打印得很漂亮。如果行分隔为真,则记录 218 //将在每个记录之间用新行记录。 219 func JSONFormatOrderedEx(pretty, lineSeparated bool) Format { 220 jsonMarshal := json.Marshal 221 if pretty { 222 jsonMarshal = func(v interface{}) ([]byte, error) { 223 return json.MarshalIndent(v, "", " ") 224 } 225 } 226 return FormatFunc(func(r *Record) []byte { 227 props := make(map[string]interface{}) 228 229 props[r.KeyNames.Time] = r.Time 230 props[r.KeyNames.Lvl] = r.Lvl.String() 231 props[r.KeyNames.Msg] = r.Msg 232 233 ctx := make([]string, len(r.Ctx)) 234 for i := 0; i < len(r.Ctx); i += 2 { 235 k, ok := r.Ctx[i].(string) 236 if !ok { 237 props[errorKey] = fmt.Sprintf("%+v is not a string key,", r.Ctx[i]) 238 } 239 ctx[i] = k 240 ctx[i+1] = formatLogfmtValue(r.Ctx[i+1], true) 241 } 242 props[r.KeyNames.Ctx] = ctx 243 244 b, err := jsonMarshal(props) 245 if err != nil { 246 b, _ = jsonMarshal(map[string]string{ 247 errorKey: err.Error(), 248 }) 249 return b 250 } 251 if lineSeparated { 252 b = append(b, '\n') 253 } 254 return b 255 }) 256 } 257 258 //jsonFormatex将日志记录格式化为json对象。如果漂亮是真的, 259 //记录将被打印得很漂亮。如果行分隔为真,则记录 260 //将在每个记录之间用新行记录。 261 func JSONFormatEx(pretty, lineSeparated bool) Format { 262 jsonMarshal := json.Marshal 263 if pretty { 264 jsonMarshal = func(v interface{}) ([]byte, error) { 265 return json.MarshalIndent(v, "", " ") 266 } 267 } 268 269 return FormatFunc(func(r *Record) []byte { 270 props := make(map[string]interface{}) 271 272 props[r.KeyNames.Time] = r.Time 273 props[r.KeyNames.Lvl] = r.Lvl.String() 274 props[r.KeyNames.Msg] = r.Msg 275 276 for i := 0; i < len(r.Ctx); i += 2 { 277 k, ok := r.Ctx[i].(string) 278 if !ok { 279 props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i]) 280 } 281 props[k] = formatJSONValue(r.Ctx[i+1]) 282 } 283 284 b, err := jsonMarshal(props) 285 if err != nil { 286 b, _ = jsonMarshal(map[string]string{ 287 errorKey: err.Error(), 288 }) 289 return b 290 } 291 292 if lineSeparated { 293 b = append(b, '\n') 294 } 295 296 return b 297 }) 298 } 299 300 func formatShared(value interface{}) (result interface{}) { 301 defer func() { 302 if err := recover(); err != nil { 303 if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() { 304 result = "nil" 305 } else { 306 panic(err) 307 } 308 } 309 }() 310 311 switch v := value.(type) { 312 case time.Time: 313 return v.Format(timeFormat) 314 315 case error: 316 return v.Error() 317 318 case fmt.Stringer: 319 return v.String() 320 321 default: 322 return v 323 } 324 } 325 326 func formatJSONValue(value interface{}) interface{} { 327 value = formatShared(value) 328 switch value.(type) { 329 case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string: 330 return value 331 default: 332 return fmt.Sprintf("%+v", value) 333 } 334 } 335 336 //FormatValue为序列化设置值的格式 337 func formatLogfmtValue(value interface{}, term bool) string { 338 if value == nil { 339 return "nil" 340 } 341 342 if t, ok := value.(time.Time); ok { 343 //性能优化:由于提供了 344 //TimeFormat没有任何转义字符,转义是 345 //昂贵。 346 return t.Format(timeFormat) 347 } 348 if term { 349 if s, ok := value.(TerminalStringer); ok { 350 //提供自定义终端纵梁,使用 351 return escapeString(s.TerminalString()) 352 } 353 } 354 value = formatShared(value) 355 switch v := value.(type) { 356 case bool: 357 return strconv.FormatBool(v) 358 case float32: 359 return strconv.FormatFloat(float64(v), floatFormat, 3, 64) 360 case float64: 361 return strconv.FormatFloat(v, floatFormat, 3, 64) 362 case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 363 return fmt.Sprintf("%d", value) 364 case string: 365 return escapeString(v) 366 default: 367 return escapeString(fmt.Sprintf("%+v", value)) 368 } 369 } 370 371 var stringBufPool = sync.Pool{ 372 New: func() interface{} { return new(bytes.Buffer) }, 373 } 374 375 func escapeString(s string) string { 376 needsQuotes := false 377 needsEscape := false 378 for _, r := range s { 379 if r <= ' ' || r == '=' || r == '"' { 380 needsQuotes = true 381 } 382 if r == '\\' || r == '"' || r == '\n' || r == '\r' || r == '\t' { 383 needsEscape = true 384 } 385 } 386 if !needsEscape && !needsQuotes { 387 return s 388 } 389 e := stringBufPool.Get().(*bytes.Buffer) 390 e.WriteByte('"') 391 for _, r := range s { 392 switch r { 393 case '\\', '"': 394 e.WriteByte('\\') 395 e.WriteByte(byte(r)) 396 case '\n': 397 e.WriteString("\\n") 398 case '\r': 399 e.WriteString("\\r") 400 case '\t': 401 e.WriteString("\\t") 402 default: 403 e.WriteRune(r) 404 } 405 } 406 e.WriteByte('"') 407 var ret string 408 if needsQuotes { 409 ret = e.String() 410 } else { 411 ret = string(e.Bytes()[1 : e.Len()-1]) 412 } 413 e.Reset() 414 stringBufPool.Put(e) 415 return ret 416 } 417