122 lines
2.7 KiB
Go
122 lines
2.7 KiB
Go
package cli
|
|
|
|
import (
|
|
"emperror.dev/errors"
|
|
"fmt"
|
|
"github.com/apex/log"
|
|
"github.com/apex/log/handlers/cli"
|
|
color2 "github.com/fatih/color"
|
|
"github.com/mattn/go-colorable"
|
|
"io"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var Default = New(os.Stderr, true)
|
|
|
|
var bold = color2.New(color2.Bold)
|
|
|
|
var Strings = [...]string{
|
|
log.DebugLevel: "DEBUG",
|
|
log.InfoLevel: " INFO",
|
|
log.WarnLevel: " WARN",
|
|
log.ErrorLevel: "ERROR",
|
|
log.FatalLevel: "FATAL",
|
|
}
|
|
|
|
type Handler struct {
|
|
mu sync.Mutex
|
|
Writer io.Writer
|
|
Padding int
|
|
}
|
|
|
|
func New(w io.Writer, useColors bool) *Handler {
|
|
if f, ok := w.(*os.File); ok {
|
|
if useColors {
|
|
return &Handler{Writer: colorable.NewColorable(f), Padding: 2}
|
|
}
|
|
}
|
|
|
|
return &Handler{Writer: colorable.NewNonColorable(w), Padding: 2}
|
|
}
|
|
|
|
type tracer interface {
|
|
StackTrace() errors.StackTrace
|
|
}
|
|
|
|
// HandleLog implements log.Handler.
|
|
func (h *Handler) HandleLog(e *log.Entry) error {
|
|
color := cli.Colors[e.Level]
|
|
level := Strings[e.Level]
|
|
names := e.Fields.Names()
|
|
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
|
|
color.Fprintf(h.Writer, "%s: [%s] %-25s", bold.Sprintf("%*s", h.Padding+1, level), time.Now().Format(time.StampMilli), e.Message)
|
|
|
|
for _, name := range names {
|
|
if name == "source" {
|
|
continue
|
|
}
|
|
|
|
fmt.Fprintf(h.Writer, " %s=%v", color.Sprint(name), e.Fields.Get(name))
|
|
}
|
|
|
|
fmt.Fprintln(h.Writer)
|
|
|
|
for _, name := range names {
|
|
if name != "error" {
|
|
continue
|
|
}
|
|
|
|
var br = color2.New(color2.Bold, color2.FgRed)
|
|
if err, ok := e.Fields.Get("error").(error); ok {
|
|
fmt.Fprintf(h.Writer, "\n%s%+v\n\n", br.Sprintf("Stacktrace:"), getErrorStack(err, false))
|
|
} else {
|
|
fmt.Fprintf(h.Writer, "\n%s%+v\n\n", br.Sprintf("Invalid Error:"), err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getErrorStack(err error, i bool) errors.StackTrace {
|
|
e, ok := err.(tracer)
|
|
if !ok {
|
|
if i {
|
|
// Just abort out of this and return a stacktrace leading up to this point. It isn't perfect
|
|
// but it'll at least include what function lead to this being called which we can then handle.
|
|
if e, ok = errors.WrapIf(err, "failed to generate stacktrace for caught error").(tracer); ok {
|
|
return e.StackTrace()
|
|
}
|
|
|
|
// The errors.WrapIf did not return a interface compatible with `tracer`, so
|
|
// we don't have an easy way to get the stacktrace, this should probably be changed
|
|
// at some point, but without this the application may panic when handling some errors.
|
|
return nil
|
|
}
|
|
|
|
return getErrorStack(errors.WrapIf(err, err.Error()), true)
|
|
}
|
|
|
|
st := e.StackTrace()
|
|
|
|
l := len(st)
|
|
// If this was an internal stack generation we're going to skip over the top four items in the stack
|
|
// trace since they'll point to the error that was generated by this function.
|
|
f := 0
|
|
if i {
|
|
f = 5
|
|
}
|
|
|
|
if i && l > 9 {
|
|
l = 9
|
|
} else if !i && l > 5 {
|
|
l = 5
|
|
}
|
|
|
|
return st[f:l]
|
|
}
|