github.com/webonyx/up@v0.7.4-0.20180808230834-91b94e551323/internal/logs/text/text.go (about) 1 // Package text implements a development-friendly textual handler. 2 package text 3 4 import ( 5 "bytes" 6 "fmt" 7 "io" 8 "sync" 9 "time" 10 11 "github.com/apex/log" 12 "github.com/dustin/go-humanize" 13 14 "github.com/apex/up/internal/colors" 15 "github.com/apex/up/internal/util" 16 ) 17 18 var ( 19 spacerPlaceholderBytes = []byte("{{spacer}}") 20 spacerBytes = []byte(colors.Gray(":")) 21 newlineBytes = []byte("\n") 22 emptyBytes = []byte("") 23 ) 24 25 // color function. 26 type colorFunc func(string) string 27 28 // omit fields. 29 var omit = map[string]bool{ 30 "app": true, 31 "stage": true, 32 "region": true, 33 "plugin": true, 34 "commit": true, 35 "version": true, 36 } 37 38 // Colors mapping. 39 var Colors = [...]colorFunc{ 40 log.DebugLevel: colors.Gray, 41 log.InfoLevel: colors.Purple, 42 log.WarnLevel: colors.Yellow, 43 log.ErrorLevel: colors.Red, 44 log.FatalLevel: colors.Red, 45 } 46 47 // Strings mapping. 48 var Strings = [...]string{ 49 log.DebugLevel: "DEBU", 50 log.InfoLevel: "INFO", 51 log.WarnLevel: "WARN", 52 log.ErrorLevel: "ERRO", 53 log.FatalLevel: "FATA", 54 } 55 56 // Handler implementation. 57 type Handler struct { 58 mu sync.Mutex 59 Writer io.Writer 60 expand bool 61 layout string 62 } 63 64 // New handler. 65 func New(w io.Writer) *Handler { 66 return &Handler{ 67 Writer: w, 68 } 69 } 70 71 // WithExpandedFields sets the expanded field state. 72 func (h *Handler) WithExpandedFields(v bool) *Handler { 73 h.expand = v 74 return h 75 } 76 77 // HandleLog implements log.Handler. 78 func (h *Handler) HandleLog(e *log.Entry) error { 79 switch { 80 case h.expand: 81 return h.handleExpanded(e) 82 default: 83 return h.handleInline(e) 84 } 85 } 86 87 // handleExpanded fields. 88 func (h *Handler) handleExpanded(e *log.Entry) error { 89 color := Colors[e.Level] 90 level := Strings[e.Level] 91 names := e.Fields.Names() 92 93 h.mu.Lock() 94 defer h.mu.Unlock() 95 96 ts := formatDate(e.Timestamp.Local()) 97 fmt.Fprintf(h.Writer, " %s %s %s\n", colors.Gray(ts), bold(color(level)), colors.Purple(e.Message)) 98 99 for _, name := range names { 100 v := e.Fields.Get(name) 101 102 if v == "" { 103 continue 104 } 105 106 fmt.Fprintf(h.Writer, " %s%s%v\n", color(name), colors.Gray(": "), value(name, v)) 107 } 108 109 if len(names) > 0 { 110 fmt.Fprintf(h.Writer, "\n") 111 } 112 113 return nil 114 } 115 116 // handleInline fields. 117 func (h *Handler) handleInline(e *log.Entry) error { 118 var buf bytes.Buffer 119 var fields int 120 121 color := Colors[e.Level] 122 level := Strings[e.Level] 123 names := e.Fields.Names() 124 ts := formatDate(e.Timestamp.Local()) 125 126 if stage, ok := e.Fields.Get("stage").(string); ok && stage != "" { 127 fmt.Fprintf(&buf, " %s %s %s %s %s{{spacer}}", colors.Gray(ts), bold(color(level)), colors.Gray(stage), colors.Gray(version(e)), colors.Purple(e.Message)) 128 } else { 129 fmt.Fprintf(&buf, " %s %s %s{{spacer}}", colors.Gray(ts), bold(color(level)), colors.Purple(e.Message)) 130 } 131 132 for _, name := range names { 133 if omit[name] { 134 continue 135 } 136 137 v := e.Fields.Get(name) 138 139 if v == "" { 140 continue 141 } 142 143 fields++ 144 fmt.Fprintf(&buf, " %s%s%v", color(name), colors.Gray("="), value(name, v)) 145 } 146 147 b := buf.Bytes() 148 149 if fields > 0 { 150 b = bytes.Replace(b, spacerPlaceholderBytes, spacerBytes, 1) 151 } else { 152 b = bytes.Replace(b, spacerPlaceholderBytes, emptyBytes, 1) 153 } 154 155 h.mu.Lock() 156 h.Writer.Write(b) 157 h.Writer.Write(newlineBytes) 158 h.mu.Unlock() 159 160 return nil 161 } 162 163 // value returns the formatted value. 164 func value(name string, v interface{}) interface{} { 165 switch name { 166 case "size": 167 return humanize.Bytes(uint64(util.ToFloat(v))) 168 case "duration": 169 return time.Millisecond * time.Duration(util.ToFloat(v)) 170 default: 171 return v 172 } 173 } 174 175 // day duration. 176 var day = time.Hour * 24 177 178 // formatDate formats t relative to now. 179 func formatDate(t time.Time) string { 180 return t.Format(`Jan 2` + util.DateSuffix(t) + ` 03:04:05pm`) 181 } 182 183 // version returns the entry version via GIT commit or lambda version. 184 func version(e *log.Entry) string { 185 if s, ok := e.Fields.Get("commit").(string); ok && s != "" { 186 return s 187 } 188 189 if s, ok := e.Fields.Get("version").(string); ok && s != "" { 190 return s 191 } 192 193 return "" 194 } 195 196 // bold string. 197 func bold(s string) string { 198 return fmt.Sprintf("\033[1m%s\033[0m", s) 199 }