package log import ( "bytes" "fmt" "io" "path" "runtime" "strings" "sync" "time" ) var patternMap = map[string]func(map[string]interface{}) string{ "T": longTime, "t": shortTime, "D": longDate, "d": shortDate, "L": keyFactory(_level), "f": keyFactory(_source), "i": keyFactory(_instanceID), "e": keyFactory(_deplyEnv), "z": keyFactory(_zone), "S": longSource, "s": shortSource, "M": message, } // newPatternRender new pattern render func newPatternRender(format string) Render { p := &pattern{ bufPool: sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}, } b := make([]byte, 0, len(format)) for i := 0; i < len(format); i++ { if format[i] != '%' { b = append(b, format[i]) continue } if i+1 >= len(format) { b = append(b, format[i]) continue } f, ok := patternMap[string(format[i+1])] if !ok { b = append(b, format[i]) continue } if len(b) != 0 { p.funcs = append(p.funcs, textFactory(string(b))) b = b[:0] } p.funcs = append(p.funcs, f) i++ } if len(b) != 0 { p.funcs = append(p.funcs, textFactory(string(b))) } return p } type pattern struct { funcs []func(map[string]interface{}) string bufPool sync.Pool } // Render implemet Formater func (p *pattern) Render(w io.Writer, d map[string]interface{}) error { buf := p.bufPool.Get().(*bytes.Buffer) defer func() { buf.Reset() p.bufPool.Put(buf) }() for _, f := range p.funcs { buf.WriteString(f(d)) } _, err := buf.WriteTo(w) return err } // Render implemet Formater as string func (p *pattern) RenderString(d map[string]interface{}) string { // TODO strings.Builder buf := p.bufPool.Get().(*bytes.Buffer) defer func() { buf.Reset() p.bufPool.Put(buf) }() for _, f := range p.funcs { buf.WriteString(f(d)) } return buf.String() } func textFactory(text string) func(map[string]interface{}) string { return func(map[string]interface{}) string { return text } } func keyFactory(key string) func(map[string]interface{}) string { return func(d map[string]interface{}) string { if v, ok := d[key]; ok { if s, ok := v.(string); ok { return s } return fmt.Sprint(v) } return "" } } func longSource(map[string]interface{}) string { if _, file, lineNo, ok := runtime.Caller(6); ok { return fmt.Sprintf("%s:%d", file, lineNo) } return "unknown:0" } func shortSource(map[string]interface{}) string { if _, file, lineNo, ok := runtime.Caller(6); ok { return fmt.Sprintf("%s:%d", path.Base(file), lineNo) } return "unknown:0" } func longTime(map[string]interface{}) string { return time.Now().Format("15:04:05.000") } func shortTime(map[string]interface{}) string { return time.Now().Format("15:04") } func longDate(map[string]interface{}) string { return time.Now().Format("2006/01/02") } func shortDate(map[string]interface{}) string { return time.Now().Format("01/02") } func isInternalKey(k string) bool { switch k { case _level, _levelValue, _time, _source, _instanceID, _appID, _deplyEnv, _zone: return true } return false } func message(d map[string]interface{}) string { var m string var s []string for k, v := range d { if k == _log { m = fmt.Sprint(v) continue } if isInternalKey(k) { continue } s = append(s, fmt.Sprintf("%s=%v", k, v)) } s = append(s, m) return strings.Join(s, " ") }