Skip to content

Commit

Permalink
Added Options.AddSource (#14)
Browse files Browse the repository at this point in the history
* added `Options.AddSource`

* improved doc & minor refactoring

---------

Co-authored-by: lmittmann <[email protected]>
  • Loading branch information
lmittmann and lmittmann authored Apr 15, 2023
1 parent c1e1897 commit 33d6906
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 16 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

Package `tint` provides a [`slog.Handler`](https://pkg.go.dev/golang.org/x/exp/slog#Handler) that writes tinted logs. The output format is inspired by the `zerolog.ConsoleWriter`.

The output format can be customized using [`Options`](https://pkg.go.dev/github.com/lmittmann/tint#Options) which is a drop-in replacement for [`slog.HandlerOptions`](https://pkg.go.dev/golang.org/x/exp/slog#HandlerOptions).

```
go get github.com/lmittmann/tint
```
Expand Down
76 changes: 60 additions & 16 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
Package tint provides a [slog.Handler] that writes tinted logs. The output
format is inspired by the [zerolog.ConsoleWriter].
The output format can be customized using [Options], which is a drop-in
replacement for [slog.HandlerOptions].
[zerolog.ConsoleWriter]: https://pkg.go.dev/github.com/rs/zerolog#ConsoleWriter
*/
package tint
Expand All @@ -11,6 +14,8 @@ import (
"encoding"
"fmt"
"io"
"path/filepath"
"runtime"
"strconv"
"sync"
"time"
Expand All @@ -32,37 +37,49 @@ const (

const keyErr = "err"

var defaultTimeFormat = time.StampMilli
var (
defaultTimeFormat = time.StampMilli
defaultLevel = slog.LevelInfo
)

// Options for a slog.Handler that writes tinted logs. A zero Options consists
// entirely of default values.
//
// Options can be used as a drop-in replacement for [slog.HandlerOptions].
type Options struct {
// Enable source code location (Default: false)
AddSource bool

// Minimum level to log (Default: slog.LevelInfo)
Level slog.Level
Level slog.Leveler

// ReplaceAttr is called to rewrite each non-group attribute before it is logged.
// See https://pkg.go.dev/golang.org/x/exp/slog#HandlerOptions for details.
ReplaceAttr func(groups []string, attr slog.Attr) slog.Attr

// Time format (Default: time.StampMilli)
TimeFormat string

// Disable color (Default: false)
NoColor bool

// ReplaceAttr is called to rewrite each non-group attribute before it is logged.
// See https://pkg.go.dev/golang.org/x/exp/slog#HandlerOptions for details.
ReplaceAttr func(groups []string, attr slog.Attr) slog.Attr
}

// NewHandler creates a [slog.Handler] that writes tinted logs to Writer w with
// the given options.
func (opts Options) NewHandler(w io.Writer) slog.Handler {
h := &handler{
w: w,
level: opts.Level,
timeFormat: opts.TimeFormat,
noColor: opts.NoColor,
addSource: opts.AddSource,
level: defaultLevel,
replaceAttr: opts.ReplaceAttr,
timeFormat: defaultTimeFormat,
noColor: opts.NoColor,
}
if h.timeFormat == "" {
h.timeFormat = defaultTimeFormat
if opts.Level != nil {
h.level = opts.Level.Level()
}
if opts.TimeFormat != "" {
h.timeFormat = opts.TimeFormat
}
return h
}
Expand All @@ -80,12 +97,13 @@ type handler struct {
groupsSlice []string

mu sync.Mutex
w io.Writer // Output writer
w io.Writer

level slog.Level // Minimum level to log (Default: slog.LevelInfo)
timeFormat string // Time format (Default: time.StampMilli)
noColor bool // Disable color (Default: false)
addSource bool
level slog.Level
replaceAttr func([]string, slog.Attr) slog.Attr
timeFormat string
noColor bool
}

func (h *handler) clone() *handler {
Expand All @@ -94,10 +112,11 @@ func (h *handler) clone() *handler {
groups: h.groups,
groupsSlice: h.groupsSlice,
w: h.w,
addSource: h.addSource,
level: h.level,
replaceAttr: h.replaceAttr,
timeFormat: h.timeFormat,
noColor: h.noColor,
replaceAttr: h.replaceAttr,
}
}

Expand Down Expand Up @@ -141,6 +160,21 @@ func (h *handler) Handle(_ context.Context, r slog.Record) error {
buf.WriteByte(' ')
}

// write source
if h.addSource {
fs := runtime.CallersFrames([]uintptr{r.PC})
f, _ := fs.Next()
if f.File != "" {
if rep == nil {
h.appendSource(buf, f)
buf.WriteByte(' ')
} else if a := rep(h.groupsSlice, slog.Any(slog.SourceKey, f)); a.Key != "" {
appendValue(buf, a.Value, false)
buf.WriteByte(' ')
}
}
}

// write message
if rep == nil {
buf.WriteString(r.Message)
Expand Down Expand Up @@ -242,6 +276,16 @@ func (h *handler) appendLevel(buf *buffer, level slog.Level) {
}
}

func (h *handler) appendSource(buf *buffer, f runtime.Frame) {
dir, file := filepath.Split(f.File)

buf.WriteStringIf(!h.noColor, ansiFaint)
buf.WriteString(filepath.Join(filepath.Base(dir), file))
buf.WriteByte(':')
buf.WriteString(strconv.Itoa(f.Line))
buf.WriteStringIf(!h.noColor, ansiReset)
}

func (h *handler) appendAttr(buf *buffer, attr slog.Attr, groups string) {
if attr.Key == "" {
return
Expand Down
9 changes: 9 additions & 0 deletions handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ func TestHandler(t *testing.T) {
},
Want: `Nov 11 00:00:00.000 INF test slice="[a b c]" map="map[a:1 b:2 c:3]"`,
},
{
Opts: tint.Options{
AddSource: true,
},
F: func(l *slog.Logger) {
l.Info("test", "key", "val")
},
Want: `Nov 11 00:00:00.000 INF tint/handler_test.go:99 test key=val`,
},
{
Opts: tint.Options{
TimeFormat: time.Kitchen,
Expand Down

0 comments on commit 33d6906

Please sign in to comment.