1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
// Package delta provides a log handler which times the delta
// between each log call, useful for debug output for command-line
// programs.
package delta
import (
"fmt"
"io"
"os"
"time"
"github.com/apex/log"
"github.com/aybabtme/rgbterm"
"github.com/tj/go-spin"
)
// TODO: move colors and share in text handler etc
// color function.
type colorFunc func(string) string
// gray string.
func gray(s string) string {
return rgbterm.FgString(s, 150, 150, 150)
}
// blue string.
func blue(s string) string {
return rgbterm.FgString(s, 77, 173, 247)
}
// cyan string.
func cyan(s string) string {
return rgbterm.FgString(s, 34, 184, 207)
}
// green string.
func green(s string) string {
return rgbterm.FgString(s, 0, 200, 255)
}
// red string.
func red(s string) string {
return rgbterm.FgString(s, 194, 37, 92)
}
// yellow string.
func yellow(s string) string {
return rgbterm.FgString(s, 252, 196, 25)
}
// Colors mapping.
var Colors = [...]colorFunc{
log.DebugLevel: gray,
log.InfoLevel: blue,
log.WarnLevel: yellow,
log.ErrorLevel: red,
log.FatalLevel: red,
}
// Strings mapping.
var Strings = [...]string{
log.DebugLevel: "DEBU",
log.InfoLevel: "INFO",
log.WarnLevel: "WARN",
log.ErrorLevel: "ERRO",
log.FatalLevel: "FATA",
}
// Default handler.
var Default = New(os.Stderr)
// Handler implementation.
type Handler struct {
entries chan *log.Entry
start time.Time
spin *spin.Spinner
prev *log.Entry
done chan struct{}
w io.Writer
}
// New handler.
func New(w io.Writer) *Handler {
h := &Handler{
entries: make(chan *log.Entry),
done: make(chan struct{}),
start: time.Now(),
spin: spin.New(),
w: w,
}
go h.loop()
return h
}
// Close the handler.
func (h *Handler) Close() error {
h.done <- struct{}{}
close(h.done)
close(h.entries)
return nil
}
// loop for rendering.
func (h *Handler) loop() {
ticker := time.NewTicker(100 * time.Millisecond)
for {
select {
case e := <-h.entries:
if h.prev != nil {
h.render(h.prev, true)
}
h.render(e, false)
h.prev = e
case <-ticker.C:
if h.prev != nil {
h.render(h.prev, false)
}
h.spin.Next()
case <-h.done:
ticker.Stop()
if h.prev != nil {
h.render(h.prev, true)
}
return
}
}
}
func (h *Handler) render(e *log.Entry, done bool) {
color := Colors[e.Level]
level := Strings[e.Level]
names := e.Fields.Names()
// delta and spinner
if done {
fmt.Fprintf(h.w, "\r %-7s", time.Since(h.start).Round(time.Millisecond))
} else {
fmt.Fprintf(h.w, "\r %s %-7s", h.spin.Current(), time.Since(h.start).Round(time.Millisecond))
}
// message
fmt.Fprintf(h.w, " %s %s", color(level), color(e.Message))
// fields
for _, name := range names {
v := e.Fields.Get(name)
if v == "" {
continue
}
fmt.Fprintf(h.w, " %s%s%v", color(name), gray("="), v)
}
// newline
if done {
fmt.Fprintf(h.w, "\n")
h.start = time.Now()
}
}
// HandleLog implements log.Handler.
func (h *Handler) HandleLog(e *log.Entry) error {
h.entries <- e
return nil
}
|