2021-03-11 03:19:39 +00:00
|
|
|
package logging
|
|
|
|
|
|
|
|
import (
|
2021-08-06 23:23:51 +00:00
|
|
|
"context"
|
2021-03-11 03:19:39 +00:00
|
|
|
"encoding/json"
|
|
|
|
"os"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2021-03-14 20:49:58 +00:00
|
|
|
color "git.handmade.network/hmn/hmn/src/ansicolor"
|
2021-05-06 09:12:18 +00:00
|
|
|
"git.handmade.network/hmn/hmn/src/config"
|
2021-03-11 03:39:24 +00:00
|
|
|
"git.handmade.network/hmn/hmn/src/oops"
|
2021-03-11 03:19:39 +00:00
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
zerolog.ErrorStackMarshaler = oops.ZerologStackMarshaler
|
2021-08-06 23:23:51 +00:00
|
|
|
log.Logger = log.Output(NewPrettyZerologWriter()).With().Stack().Logger()
|
2021-05-06 09:12:18 +00:00
|
|
|
zerolog.SetGlobalLevel(config.Config.LogLevel)
|
2021-03-11 03:19:39 +00:00
|
|
|
}
|
|
|
|
|
2021-03-21 20:38:37 +00:00
|
|
|
func GlobalLogger() *zerolog.Logger {
|
|
|
|
return &log.Logger
|
|
|
|
}
|
|
|
|
|
2021-03-11 03:19:39 +00:00
|
|
|
func Trace() *zerolog.Event {
|
|
|
|
return log.Trace().Timestamp().Stack()
|
|
|
|
}
|
|
|
|
|
|
|
|
func Debug() *zerolog.Event {
|
|
|
|
return log.Debug().Timestamp().Stack()
|
|
|
|
}
|
|
|
|
|
|
|
|
func Info() *zerolog.Event {
|
|
|
|
return log.Info().Timestamp().Stack()
|
|
|
|
}
|
|
|
|
|
|
|
|
func Warn() *zerolog.Event {
|
|
|
|
return log.Warn().Timestamp().Stack()
|
|
|
|
}
|
|
|
|
|
|
|
|
func Error() *zerolog.Event {
|
|
|
|
return log.Error().Timestamp().Stack()
|
|
|
|
}
|
|
|
|
|
2021-03-14 20:49:58 +00:00
|
|
|
func Panic() *zerolog.Event {
|
|
|
|
return log.Panic().Timestamp().Stack()
|
|
|
|
}
|
|
|
|
|
|
|
|
func Fatal() *zerolog.Event {
|
|
|
|
return log.Fatal().Timestamp().Stack()
|
|
|
|
}
|
|
|
|
|
2021-03-18 02:14:06 +00:00
|
|
|
func With() zerolog.Context {
|
2021-03-21 20:38:37 +00:00
|
|
|
return log.With().Stack()
|
2021-03-18 02:14:06 +00:00
|
|
|
}
|
|
|
|
|
2021-03-11 03:19:39 +00:00
|
|
|
type PrettyZerologWriter struct {
|
2021-04-06 06:06:33 +00:00
|
|
|
wd string
|
|
|
|
wasLastLogMultiline bool
|
2021-03-11 03:19:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type PrettyLogEntry struct {
|
|
|
|
Timestamp string
|
|
|
|
Level string
|
|
|
|
Message string
|
|
|
|
Error string
|
|
|
|
StackTrace []interface{}
|
|
|
|
|
|
|
|
OtherFields []PrettyField
|
|
|
|
}
|
|
|
|
|
|
|
|
type PrettyField struct {
|
|
|
|
Name string
|
|
|
|
Value interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
var ColorFromLevel = map[string]string{
|
|
|
|
"trace": color.Gray,
|
|
|
|
"debug": color.Gray,
|
|
|
|
"info": color.BgBlue,
|
|
|
|
"warn": color.BgYellow,
|
|
|
|
"error": color.BgRed,
|
|
|
|
"fatal": color.BgRed,
|
|
|
|
"panic": color.BgRed,
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewPrettyZerologWriter() *PrettyZerologWriter {
|
|
|
|
wd, _ := os.Getwd()
|
|
|
|
return &PrettyZerologWriter{
|
2021-04-06 06:06:33 +00:00
|
|
|
wd: wd,
|
|
|
|
wasLastLogMultiline: false,
|
2021-03-11 03:19:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *PrettyZerologWriter) Write(p []byte) (int, error) {
|
|
|
|
var fields map[string]interface{}
|
|
|
|
err := json.Unmarshal(p, &fields)
|
|
|
|
if err != nil {
|
|
|
|
return os.Stderr.Write(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
var pretty PrettyLogEntry
|
|
|
|
for name, val := range fields {
|
|
|
|
switch name {
|
|
|
|
case zerolog.TimestampFieldName:
|
|
|
|
pretty.Timestamp = val.(string)
|
|
|
|
case zerolog.LevelFieldName:
|
|
|
|
pretty.Level = val.(string)
|
|
|
|
case zerolog.MessageFieldName:
|
|
|
|
pretty.Message = val.(string)
|
|
|
|
case zerolog.ErrorFieldName:
|
|
|
|
pretty.Error = val.(string)
|
|
|
|
case zerolog.ErrorStackFieldName:
|
|
|
|
pretty.StackTrace = val.([]interface{})
|
|
|
|
default:
|
|
|
|
pretty.OtherFields = append(pretty.OtherFields, PrettyField{
|
|
|
|
Name: name,
|
|
|
|
Value: val,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Slice(pretty.OtherFields, func(i, j int) bool {
|
|
|
|
return strings.Compare(pretty.OtherFields[i].Name, pretty.OtherFields[j].Name) < 0
|
|
|
|
})
|
|
|
|
|
2021-04-06 06:06:33 +00:00
|
|
|
isMultiline := (pretty.Error != "" || pretty.StackTrace != nil || pretty.OtherFields != nil)
|
|
|
|
|
2021-03-11 03:19:39 +00:00
|
|
|
var b strings.Builder
|
2021-04-06 06:06:33 +00:00
|
|
|
if isMultiline || w.wasLastLogMultiline {
|
|
|
|
b.WriteString("---------------------------------------\n")
|
|
|
|
}
|
2021-03-11 03:19:39 +00:00
|
|
|
b.WriteString(pretty.Timestamp)
|
|
|
|
b.WriteString(" ")
|
|
|
|
if pretty.Level != "" {
|
|
|
|
b.WriteString(ColorFromLevel[pretty.Level])
|
|
|
|
b.WriteString(color.Bold)
|
|
|
|
b.WriteString(strings.ToUpper(pretty.Level))
|
|
|
|
b.WriteString(color.Reset)
|
|
|
|
b.WriteString(": ")
|
|
|
|
}
|
|
|
|
b.WriteString(pretty.Message)
|
|
|
|
b.WriteString("\n")
|
|
|
|
if pretty.Error != "" {
|
|
|
|
b.WriteString(" " + color.Bold + color.Red + "ERROR:" + color.Reset + " ")
|
|
|
|
b.WriteString(pretty.Error)
|
|
|
|
b.WriteString("\n")
|
|
|
|
}
|
|
|
|
if len(pretty.OtherFields) > 0 {
|
|
|
|
b.WriteString(" " + color.Bold + color.Blue + "Fields:" + color.Reset + "\n")
|
|
|
|
for _, field := range pretty.OtherFields {
|
|
|
|
valuePretty, _ := json.MarshalIndent(field.Value, " ", " ")
|
|
|
|
b.WriteString(" ")
|
|
|
|
b.WriteString(field.Name)
|
|
|
|
b.WriteString(": ")
|
|
|
|
b.WriteString(string(valuePretty))
|
|
|
|
b.WriteString("\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if pretty.StackTrace != nil {
|
|
|
|
b.WriteString(" " + color.Bold + color.Blue + "Stack trace:" + color.Reset + "\n")
|
|
|
|
for _, frame := range pretty.StackTrace {
|
|
|
|
frameMap := frame.(map[string]interface{})
|
|
|
|
file := frameMap["file"].(string)
|
|
|
|
file = strings.Replace(file, w.wd, ".", 1)
|
|
|
|
|
|
|
|
b.WriteString(" ")
|
|
|
|
b.WriteString(frameMap["function"].(string))
|
|
|
|
b.WriteString(" (")
|
|
|
|
b.WriteString(file)
|
|
|
|
b.WriteString(":")
|
|
|
|
b.WriteString(strconv.Itoa(int(frameMap["line"].(float64))))
|
|
|
|
b.WriteString(")\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-06 06:06:33 +00:00
|
|
|
w.wasLastLogMultiline = isMultiline
|
|
|
|
|
2021-03-11 03:19:39 +00:00
|
|
|
return os.Stderr.Write([]byte(b.String()))
|
|
|
|
}
|
2021-03-11 05:02:43 +00:00
|
|
|
|
2021-03-21 20:38:37 +00:00
|
|
|
func LogPanics(logger *zerolog.Logger) {
|
2021-03-11 05:02:43 +00:00
|
|
|
if r := recover(); r != nil {
|
2021-03-21 20:38:37 +00:00
|
|
|
LogPanicValue(logger, r, "recovered from panic")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func LogPanicValue(logger *zerolog.Logger, val interface{}, msg string) {
|
|
|
|
if logger == nil {
|
|
|
|
logger = GlobalLogger()
|
|
|
|
}
|
|
|
|
|
|
|
|
if err, ok := val.(error); ok {
|
2021-09-06 00:00:25 +00:00
|
|
|
logger.Error().Err(err).Msg(msg)
|
2021-03-21 20:38:37 +00:00
|
|
|
} else {
|
2021-03-22 03:07:18 +00:00
|
|
|
logger.Error().
|
|
|
|
Interface("recovered", val).
|
|
|
|
Interface(zerolog.ErrorStackFieldName, oops.Trace()).
|
|
|
|
Msg(msg)
|
2021-03-11 05:02:43 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-06 23:23:51 +00:00
|
|
|
|
|
|
|
const LoggerContextKey = "logger"
|
|
|
|
|
|
|
|
func AttachLoggerToContext(logger *zerolog.Logger, ctx context.Context) context.Context {
|
|
|
|
return context.WithValue(ctx, LoggerContextKey, logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExtractLogger(ctx context.Context) *zerolog.Logger {
|
|
|
|
ilogger := ctx.Value(LoggerContextKey)
|
|
|
|
if ilogger == nil {
|
|
|
|
return GlobalLogger()
|
|
|
|
}
|
|
|
|
return ilogger.(*zerolog.Logger)
|
|
|
|
}
|