Skip to content

Commit

Permalink
enable log colorization in development
Browse files Browse the repository at this point in the history
I'm waiting on upstream to merge two merge requests.

Merge-Request: https://gitlab.com/greyxor/slogor/-/merge_requests/7
Merge-Request: https://gitlab.com/greyxor/slogor/-/merge_requests/8
  • Loading branch information
dmke committed Oct 31, 2024
1 parent c64d20d commit d680742
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 36 deletions.
2 changes: 1 addition & 1 deletion cmd/texd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ func setupLogger() (xlog.Logger, error) {
xlog.WithSource(),
}
if texd.Development() {
o = append(o, xlog.WriteTo(os.Stderr), xlog.AsText())
o = append(o, xlog.WriteTo(os.Stderr), xlog.AsText(), xlog.Color())
} else {
o = append(o, xlog.WriteTo(os.Stdout), xlog.AsJSON())
}
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/docker/go-units v0.5.0
github.com/gorilla/handlers v1.5.2
github.com/gorilla/mux v1.8.1
github.com/mattn/go-isatty v0.0.20
github.com/moby/term v0.5.0
github.com/oklog/ulid/v2 v2.1.0
github.com/opencontainers/image-spec v1.1.0
Expand All @@ -17,6 +18,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
github.com/thediveo/enumflag v0.10.1
gitlab.com/greyxor/slogor v1.4.1
)

require (
Expand Down Expand Up @@ -61,3 +63,5 @@ require (
)

replace github.com/spf13/afero => github.com/digineo/afero v1.11.1-0.20240128222722-ade8094005cb

replace gitlab.com/greyxor/slogor => gitlab.com/dmke/slogor v1.4.2-0.20241030183918-8885575df1e5
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1456,6 +1456,8 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
Expand Down Expand Up @@ -1588,6 +1590,8 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
gitlab.com/dmke/slogor v1.4.2-0.20241030183918-8885575df1e5 h1:oIZBFZcMCiRwi1CdWSf62YF4XKq0GyhKIBsxZo24AWw=
gitlab.com/dmke/slogor v1.4.2-0.20241030183918-8885575df1e5/go.mod h1:RvD/RqmEGpqVhL8tqqfVduuxtcik6uXCnzfikaFCwto=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
Expand Down
5 changes: 2 additions & 3 deletions service/middleware/logging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ func TestLogging(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)

assert.Equal(t, strings.Join([]string{
"time=2022-04-15T05:20:00.000Z",
"level=INFO",
`msg=""`,
"[05:20:00.000] INFO logging.go:68",
"",
"method=GET",
"status=200",
"bytes=0",
Expand Down
5 changes: 2 additions & 3 deletions service/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,8 @@ func TestHandleStatus_withFailIO(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.code)
assert.Equal(t, mimeTypeJSON, rec.h.Get("Content-Type"))
assert.Equal(t, strings.Join([]string{
"time=2022-04-15T05:20:00.000Z",
"level=ERROR",
`msg="failed to write response"`,
"[05:20:00.000] ERROR status.go:46",
"failed to write response",
`error="io: read/write on closed pipe"`,
}, " ")+"\n", buf.String())
}
7 changes: 7 additions & 0 deletions xlog/attrs.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,18 @@ const ErrorKey = "error"
// ErrorValue holds an error value.
type ErrorValue struct{ error }

var _ slog.LogValuer = (*ErrorValue)(nil)

// Value extracts the error message.
func (err ErrorValue) Value() slog.Value {
return slog.StringValue(err.Error())
}

// LogValue implements [slog.LogValuer].
func (err ErrorValue) LogValue() slog.Value {
return slog.StringValue(err.Error())
}

// Error constructs a first-class error log attribute.
//
// Not to be confused with (xlog.Logger).Error() or (log/slog).Error(),
Expand Down
44 changes: 31 additions & 13 deletions xlog/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,33 @@ package xlog
import (
"io"
"log/slog"
"os"
"time"

"github.com/digineo/texd/internal"
"github.com/mattn/go-isatty"
"gitlab.com/greyxor/slogor"
)

// An Option represents a functional configuration option. These are
// used to configure new logger instances.
type Option func(*options) error

type options struct {
discard bool
output io.Writer
clock internal.Clock
discard bool
output io.Writer
color bool
clock internal.Clock
level slog.Leveler
source bool

buildHandler func(o *options) slog.Handler
handlerOpts *slog.HandlerOptions
}

// Leveled sets the log level.
func Leveled(l slog.Level) Option {
return func(o *options) error {
o.handlerOpts.Level = l
o.level = l
return nil
}
}
Expand All @@ -36,7 +42,7 @@ func LeveledString(s string) Option {
if err != nil {
return err
}
o.handlerOpts.Level = l
o.level = l
return nil
}
}
Expand All @@ -60,16 +66,17 @@ func MockClock(t time.Time) Option {
// WithSource enables source code positions in log messages.
func WithSource() Option {
return func(o *options) error {
o.handlerOpts.AddSource = true
o.source = true
return nil
}
}

// WithAttrReplacer configures an attribute replacer.
// See (slog.HandlerOptions).ReplaceAtrr for details.
func WithAttrReplacer(f func(groups []string, a slog.Attr) slog.Attr) Option {
// Color enables colorful log output. If the output writer set by
// [WriteTo] isn't a TTY, or messages are output [AsJSON], enabling
// colors won't have an effect.
func Color() Option {
return func(o *options) error {
o.handlerOpts.ReplaceAttr = f
o.color = true
return nil
}
}
Expand All @@ -79,7 +86,10 @@ func WithAttrReplacer(f func(groups []string, a slog.Attr) slog.Attr) Option {
func AsJSON() Option {
return func(o *options) error {
o.buildHandler = func(o *options) slog.Handler {
return slog.NewJSONHandler(o.output, o.handlerOpts)
return slog.NewJSONHandler(o.output, &slog.HandlerOptions{
AddSource: o.source,
Level: o.level,
})
}
return nil
}
Expand All @@ -90,7 +100,15 @@ func AsJSON() Option {
func AsText() Option {
return func(o *options) error {
o.buildHandler = func(o *options) slog.Handler {
return slog.NewTextHandler(o.output, o.handlerOpts)
opts := []slogor.OptionFn{
slogor.SetLevel(o.level.Level()),
slogor.SetTimeFormat("[15:04:05.000]"),
slogor.ShowSource(),
}
if f, isFile := o.output.(*os.File); !isFile || !isatty.IsTerminal(f.Fd()) {
opts = append(opts, slogor.DisableColor())
}
return slogor.NewHandler(o.output, opts...)
}
return nil
}
Expand Down
37 changes: 21 additions & 16 deletions xlog/xlog.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package xlog provides a very thin wrapper aroud log/slog.
// Package xlog provides a thin wrapper around [log/slog].
package xlog

import (
Expand All @@ -9,6 +9,8 @@ import (
"runtime"
"strings"
"time"

"github.com/digineo/texd/internal"
)

// A Logger allows writing messages with various severities.
Expand Down Expand Up @@ -37,8 +39,8 @@ type logger struct {
// written to stdout, and the log level is INFO.
func New(opt ...Option) (Logger, error) {
opts := options{
output: os.Stdout,
handlerOpts: &slog.HandlerOptions{},
level: slog.LevelInfo,
output: os.Stdout,
}

for _, o := range opt {
Expand All @@ -53,22 +55,15 @@ func New(opt ...Option) (Logger, error) {
}

// setup mock time
h := opts.buildHandler(&opts)
if opts.clock != nil {
repl := opts.handlerOpts.ReplaceAttr
opts.handlerOpts.ReplaceAttr = func(groups []string, a slog.Attr) slog.Attr {
if len(groups) == 0 && a.Key == slog.TimeKey {
a.Value = slog.TimeValue(opts.clock.Now())
}
if repl == nil {
return a
}
return repl(groups, a)
h = &mockTimeHandler{
clock: opts.clock,
Handler: h,
}
}

return &logger{
l: slog.New(opts.buildHandler(&opts)),
}, nil
return &logger{l: slog.New(h)}, nil
}

// log creates a log record. It is called by Debug, Info, etc.
Expand All @@ -77,7 +72,7 @@ func (log *logger) log(level slog.Level, msg string, a ...slog.Attr) {
return
}
var pcs [1]uintptr
runtime.Callers(3, pcs[:]) // skip runtime.Callers, log, and our caller
runtime.Callers(3, pcs[:]) //nolint:mnd // skip runtime.Callers, log, and our caller
r := slog.NewRecord(time.Now(), level, msg, pcs[0])
r.AddAttrs(a...)
_ = log.l.Handler().Handle(context.Background(), r)
Expand Down Expand Up @@ -129,3 +124,13 @@ func ParseLevel(s string) (l slog.Level, err error) {
}
return
}

type mockTimeHandler struct {
clock internal.Clock
slog.Handler
}

func (h *mockTimeHandler) Handle(ctx context.Context, a slog.Record) error {
a.Time = h.clock.Now()
return h.Handler.Handle(ctx, a)
}

0 comments on commit d680742

Please sign in to comment.