github.com/phuslu/log@v1.0.100/console.go (about) 1 package log 2 3 import ( 4 "fmt" 5 "io" 6 "runtime" 7 "strconv" 8 ) 9 10 // IsTerminal returns whether the given file descriptor is a terminal. 11 func IsTerminal(fd uintptr) bool { 12 return isTerminal(fd, runtime.GOOS, runtime.GOARCH) 13 } 14 15 // ConsoleWriter parses the JSON input and writes it in a colorized, human-friendly format to Writer. 16 // IMPORTANT: Don't use ConsoleWriter on critical path of a high concurrency and low latency application. 17 // 18 // Default output format: 19 // 20 // {Time} {Level} {Goid} {Caller} > {Message} {Key}={Value} {Key}={Value} 21 // 22 // Note: The performance of ConsoleWriter is not good enough, because it will 23 // parses JSON input into structured records, then output in a specific order. 24 // Roughly 2x faster than logrus.TextFormatter, 0.8x fast as zap.ConsoleEncoder, 25 // and 5x faster than zerolog.ConsoleWriter. 26 type ConsoleWriter struct { 27 // ColorOutput determines if used colorized output. 28 ColorOutput bool 29 30 // QuoteString determines if quoting string values. 31 QuoteString bool 32 33 // EndWithMessage determines if output message in the end. 34 EndWithMessage bool 35 36 // Formatter specifies an optional text formatter for creating a customized output, 37 // If it is set, ColorOutput, QuoteString and EndWithMessage will be ignore. 38 Formatter func(w io.Writer, args *FormatterArgs) (n int, err error) 39 40 // Writer is the output destination. using os.Stderr if empty. 41 Writer io.Writer 42 } 43 44 // Close implements io.Closer, will closes the underlying Writer if not empty. 45 func (w *ConsoleWriter) Close() (err error) { 46 if w.Writer != nil { 47 if closer, ok := w.Writer.(io.Closer); ok { 48 err = closer.Close() 49 } 50 } 51 return 52 } 53 54 func (w *ConsoleWriter) write(out io.Writer, p []byte) (int, error) { 55 b := bbpool.Get().(*bb) 56 b.B = b.B[:0] 57 defer bbpool.Put(b) 58 59 b.B = append(b.B, p...) 60 61 var args FormatterArgs 62 parseFormatterArgs(b.B, &args) 63 64 switch { 65 case args.Time == "": 66 return out.Write(p) 67 case w.Formatter != nil: 68 return w.Formatter(out, &args) 69 default: 70 return w.format(out, &args) 71 } 72 73 } 74 75 func (w *ConsoleWriter) format(out io.Writer, args *FormatterArgs) (n int, err error) { 76 b := bbpool.Get().(*bb) 77 b.B = b.B[:0] 78 defer bbpool.Put(b) 79 80 const ( 81 Reset = "\x1b[0m" 82 Black = "\x1b[30m" 83 Red = "\x1b[31m" 84 Green = "\x1b[32m" 85 Yellow = "\x1b[33m" 86 Blue = "\x1b[34m" 87 Magenta = "\x1b[35m" 88 Cyan = "\x1b[36m" 89 White = "\x1b[37m" 90 Gray = "\x1b[90m" 91 ) 92 93 // colorful level string 94 var color, three string 95 switch args.Level { 96 case "trace": 97 color, three = Magenta, "TRC" 98 case "debug": 99 color, three = Yellow, "DBG" 100 case "info": 101 color, three = Green, "INF" 102 case "warn": 103 color, three = Red, "WRN" 104 case "error": 105 color, three = Red, "ERR" 106 case "fatal": 107 color, three = Red, "FTL" 108 case "panic": 109 color, three = Red, "PNC" 110 default: 111 color, three = Gray, "???" 112 } 113 114 // pretty console writer 115 if w.ColorOutput { 116 // header 117 fmt.Fprintf(b, "%s%s%s %s%s%s ", Gray, args.Time, Reset, color, three, Reset) 118 if args.Caller != "" { 119 fmt.Fprintf(b, "%s %s %s>%s", args.Goid, args.Caller, Cyan, Reset) 120 } else { 121 fmt.Fprintf(b, "%s>%s", Cyan, Reset) 122 } 123 if !w.EndWithMessage { 124 fmt.Fprintf(b, " %s", args.Message) 125 } 126 // key and values 127 for _, kv := range args.KeyValues { 128 if w.QuoteString && kv.ValueType == 's' { 129 kv.Value = strconv.Quote(kv.Value) 130 } 131 if kv.Key == "error" { 132 fmt.Fprintf(b, " %s%s=%s%s", Red, kv.Key, kv.Value, Reset) 133 } else { 134 fmt.Fprintf(b, " %s%s=%s%s%s", Cyan, kv.Key, Gray, kv.Value, Reset) 135 } 136 } 137 // message 138 if w.EndWithMessage { 139 fmt.Fprintf(b, "%s %s", Reset, args.Message) 140 } 141 } else { 142 // header 143 fmt.Fprintf(b, "%s %s ", args.Time, three) 144 if args.Caller != "" { 145 fmt.Fprintf(b, "%s %s >", args.Goid, args.Caller) 146 } else { 147 fmt.Fprint(b, ">") 148 } 149 if !w.EndWithMessage { 150 fmt.Fprintf(b, " %s", args.Message) 151 } 152 // key and values 153 for _, kv := range args.KeyValues { 154 if w.QuoteString && kv.ValueType == 's' { 155 b.B = append(b.B, ' ') 156 b.B = append(b.B, kv.Key...) 157 b.B = append(b.B, '=') 158 b.B = strconv.AppendQuote(b.B, kv.Value) 159 } else { 160 fmt.Fprintf(b, " %s=%s", kv.Key, kv.Value) 161 } 162 } 163 // message 164 if w.EndWithMessage { 165 fmt.Fprintf(b, " %s", args.Message) 166 } 167 } 168 169 // add line break if needed 170 if b.B[len(b.B)-1] != '\n' { 171 b.B = append(b.B, '\n') 172 } 173 174 // stack 175 if args.Stack != "" { 176 b.B = append(b.B, args.Stack...) 177 if args.Stack[len(args.Stack)-1] != '\n' { 178 b.B = append(b.B, '\n') 179 } 180 } 181 182 return out.Write(b.B) 183 } 184 185 type LogfmtFormatter struct { 186 TimeField string 187 } 188 189 func (f LogfmtFormatter) Formatter(out io.Writer, args *FormatterArgs) (n int, err error) { 190 b := bbpool.Get().(*bb) 191 b.B = b.B[:0] 192 defer bbpool.Put(b) 193 194 fmt.Fprintf(b, "%s=%s ", f.TimeField, args.Time) 195 if args.Level != "" && args.Level[0] != '?' { 196 fmt.Fprintf(b, "level=%s ", args.Level) 197 } 198 if args.Caller != "" { 199 fmt.Fprintf(b, "goid=%s caller=", args.Goid) 200 b.B = strconv.AppendQuote(b.B, args.Caller) 201 b.B = append(b.B, ' ') 202 } 203 if args.Stack != "" { 204 b.B = append(b.B, "stack="...) 205 b.B = strconv.AppendQuote(b.B, args.Stack) 206 b.B = append(b.B, ' ') 207 } 208 // key and values 209 for _, kv := range args.KeyValues { 210 switch kv.ValueType { 211 case 't': 212 fmt.Fprintf(b, "%s ", kv.Key) 213 case 'f': 214 fmt.Fprintf(b, "%s=false ", kv.Key) 215 case 'n': 216 fmt.Fprintf(b, "%s=%s ", kv.Key, kv.Value) 217 case 'S': 218 fmt.Fprintf(b, "%s=%s ", kv.Key, kv.Value) 219 case 's': 220 fallthrough 221 default: 222 b.B = append(b.B, kv.Key...) 223 b.B = append(b.B, '=') 224 b.B = strconv.AppendQuote(b.B, kv.Value) 225 b.B = append(b.B, ' ') 226 } 227 } 228 // message 229 b.B = strconv.AppendQuote(b.B, args.Message) 230 b.B = append(b.B, '\n') 231 232 return out.Write(b.B) 233 } 234 235 var _ Writer = (*ConsoleWriter)(nil)