grim/convey

Port from logrus to log/slog
default tip
5 months ago, Elliott Sales de Andrade
c588f9b3f559
Port from logrus to log/slog

This doesn't really take much advantage of structured logging beyond what is already done (`id` and `idColor`), and consequently the log handler does not try to do any handling of anything more than that (i.e., grouping, or arbitrary attributes beyond those defined).

One should maybe have a `Context` available to pass in, but there isn't one, and anyway, the log handler doesn't use it, so I've passed in a `TODO` instead.

Everything else is just normal import/rename changes.

Testing Done:
Ran `go run . run`

Reviewed at https://reviews.imfreedom.org/r/2871/
// Convey
// Copyright 2016-2018 Gary Kramlich <grim@reaperworld.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package logging
import (
"bytes"
"context"
"fmt"
"log/slog"
"os"
"sync"
"time"
"github.com/mgutz/ansi"
)
var (
baseTime time.Time = time.Now()
levelColors map[slog.Level]string = map[slog.Level]string{
slog.LevelError: ansi.ColorCode("red"),
slog.LevelWarn: ansi.ColorCode("yellow"),
slog.LevelInfo: ansi.ColorCode("green"),
slog.LevelDebug: ansi.ColorCode("cyan"),
}
)
type Handler struct {
level slog.Level
disableColors bool
mtx *sync.Mutex
recordId string
recordColor string
ignoreRemainingAttrs bool
}
func NewHandler(color bool, level slog.Level) slog.Handler {
disableColors := false
if color {
switch os.Getenv("TERM") {
case "win", "dumb":
disableColors = true
}
} else {
disableColors = true
}
return &Handler{
disableColors: disableColors,
level: level,
mtx: &sync.Mutex{},
}
}
func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool {
return level >= h.level
}
func (h *Handler) Handle(ctx context.Context, record slog.Record) error {
b := &bytes.Buffer{}
var miniTimestamp int
if record.Time.IsZero() {
miniTimestamp = int(time.Since(baseTime) / time.Second)
} else {
miniTimestamp = int(record.Time.Sub(baseTime) / time.Second)
}
fmt.Fprintf(b, "[%04d] ", miniTimestamp)
// Find some attributes from handler context or the record.
id := h.recordId
idColor := h.recordColor
if !h.ignoreRemainingAttrs {
record.Attrs(func(a slog.Attr) bool {
switch a.Key {
case "id":
id = a.Value.String()
case "idColor":
idColor = a.Value.String()
}
return true
})
}
if id != "" {
if h.disableColors && idColor != "" {
fmt.Fprintf(b, "%s: ", id)
} else {
fmt.Fprintf(b, "%s%s%s: ", idColor, id, ansi.Reset)
}
}
if record.Message != "" {
if h.disableColors {
fmt.Fprintf(b, "%s", record.Message)
} else {
fmt.Fprintf(b, "%s%s%s", levelColors[record.Level], record.Message, ansi.Reset)
}
}
b.WriteByte('\n')
h.mtx.Lock()
defer h.mtx.Unlock()
_, err := os.Stdout.Write(b.Bytes())
return err
}
func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler {
if len(attrs) == 0 {
return h
}
h2 := *h
// We only care about a few attributes for now.
for _, a := range attrs {
switch a.Key {
case "id":
h2.recordId = a.Value.String()
case "idColor":
h2.recordColor = a.Value.String()
}
}
return &h2
}
func (h *Handler) WithGroup(name string) slog.Handler {
if name == "" {
return h
}
h2 := *h
// Once a group is added, the attributes become group.attr, so we want to ignore the rest.
h2.ignoreRemainingAttrs = true
return &h2
}