130 lines
3.1 KiB
Go
130 lines
3.1 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"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var Default = New(os.Stderr, true)
|
|
var bold = color2.New(color2.Bold)
|
|
var boldred = color2.New(color2.Bold, color2.FgRed)
|
|
|
|
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}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
if err, ok := e.Fields.Get("error").(error); ok {
|
|
// Attach the stacktrace if it is missing at this point, but don't point
|
|
// it specifically to this line since that is irrelevant.
|
|
err = errors.WithStackDepthIf(err, 4)
|
|
formatted := fmt.Sprintf("\n%s\n%+v\n\n", boldred.Sprintf("Stacktrace:"), err)
|
|
|
|
if !strings.Contains(formatted, "runtime.goexit") {
|
|
_, _ = fmt.Fprint(h.Writer, formatted)
|
|
break
|
|
}
|
|
|
|
// Inserts a new-line between sections of a stack.
|
|
// When wrapping errors, you get multiple separate stacks that start with their message,
|
|
// this allows us to separate them with a new-line and view them more easily.
|
|
//
|
|
// For example:
|
|
//
|
|
// Stacktrace:
|
|
// readlink test: no such file or directory
|
|
// failed to read symlink target for 'test'
|
|
// github.com/pterodactyl/wings/server/filesystem.(*Archive).addToArchive
|
|
// github.com/pterodactyl/wings/server/filesystem/archive.go:166
|
|
// ... (Truncated the stack for easier reading)
|
|
// runtime.goexit
|
|
// runtime/asm_amd64.s:1374
|
|
// **NEW LINE INSERTED HERE**
|
|
// backup: error while generating server backup
|
|
// github.com/pterodactyl/wings/server.(*Server).Backup
|
|
// github.com/pterodactyl/wings/server/backup.go:84
|
|
// ... (Truncated the stack for easier reading)
|
|
// runtime.goexit
|
|
// runtime/asm_amd64.s:1374
|
|
//
|
|
var b strings.Builder
|
|
var endOfStack bool
|
|
for _, s := range strings.Split(formatted, "\n") {
|
|
b.WriteString(s + "\n")
|
|
|
|
if s == "runtime.goexit" {
|
|
endOfStack = true
|
|
continue
|
|
}
|
|
|
|
if !endOfStack {
|
|
continue
|
|
}
|
|
|
|
b.WriteString("\n")
|
|
endOfStack = false
|
|
}
|
|
|
|
_, _ = fmt.Fprint(h.Writer, b.String())
|
|
}
|
|
|
|
// Only one key with the name "error" can be in the map.
|
|
break
|
|
}
|
|
|
|
return nil
|
|
}
|