Skip to content

Commit

Permalink
Merge pull request #3 from sourcenetwork/nasdf/feat/tint-handler
Browse files Browse the repository at this point in the history
feat: Tint handler
  • Loading branch information
nasdf authored Apr 23, 2024
2 parents 5570804 + 6864db3 commit bfd0a2c
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 43 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ Default config values can be set via environment variables.
| `LOG_SOURCE` | enables source location | `true` `false` |
| `LOG_OUTPUT` | sets the output path | `stderr` `stdout` |
| `LOG_OVERRIDES` | logger specific overrides | `net,level=info;core,output=stdout` |
| `LOG_NO_COLOR` | disable color text output | `true` `false` |
6 changes: 6 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ type Config struct {
EnableSource bool
// Output specifies the output path for the logger.
Output string
// DisableColor specifies if colored output is disabled.
DisableColor bool
}

// DefaultConfig returns a config with default values.
Expand All @@ -53,13 +55,15 @@ type Config struct {
func DefaultConfig() Config {
enableSource, _ := strconv.ParseBool(os.Getenv("LOG_SOURCE"))
enableStacktrace, _ := strconv.ParseBool(os.Getenv("LOG_STACKTRACE"))
disableColor, _ := strconv.ParseBool(os.Getenv("LOG_NO_COLOR"))

return Config{
Level: strings.ToLower(os.Getenv("LOG_LEVEL")),
Output: strings.ToLower(os.Getenv("LOG_OUTPUT")),
Format: strings.ToLower(os.Getenv("LOG_FORMAT")),
EnableSource: enableSource,
EnableStackTrace: enableStacktrace,
DisableColor: disableColor,
}
}

Expand Down Expand Up @@ -124,6 +128,8 @@ func SetConfigOverrides(text string) {
config.EnableStackTrace, _ = strconv.ParseBool(val)
case "source":
config.EnableSource, _ = strconv.ParseBool(val)
case "no-color":
config.DisableColor, _ = strconv.ParseBool(val)
}
}
SetConfigOverride(name, config)
Expand Down
12 changes: 11 additions & 1 deletion config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package corelog

import (
"os"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -13,6 +14,7 @@ func TestDefaultConfigWithEnv(t *testing.T) {
os.Setenv("LOG_FORMAT", FormatJSON)
os.Setenv("LOG_SOURCE", "true")
os.Setenv("LOG_STACKTRACE", "true")
os.Setenv("LOG_NO_COLOR", "true")
t.Cleanup(os.Clearenv)

cfg := DefaultConfig()
Expand All @@ -21,29 +23,37 @@ func TestDefaultConfigWithEnv(t *testing.T) {
assert.Equal(t, FormatJSON, cfg.Format)
assert.Equal(t, true, cfg.EnableStackTrace)
assert.Equal(t, true, cfg.EnableSource)
assert.Equal(t, true, cfg.DisableColor)
}

func TestSetConfigOverrides(t *testing.T) {
SetConfigOverrides("net,level=error,source=true,format=json,invalid;core,output=stdout,stacktrace=true")
overrides := []string{
"net,level=error,source=true,format=json,invalid",
"core,output=stdout,stacktrace=true,no-color=true",
}
SetConfigOverrides(strings.Join(overrides, ";"))

cfg := GetConfig("")
assert.Equal(t, "", cfg.Level)
assert.Equal(t, "", cfg.Output)
assert.Equal(t, "", cfg.Format)
assert.Equal(t, false, cfg.EnableStackTrace)
assert.Equal(t, false, cfg.EnableSource)
assert.Equal(t, false, cfg.DisableColor)

net := GetConfig("net")
assert.Equal(t, LevelError, net.Level)
assert.Equal(t, "", net.Output)
assert.Equal(t, FormatJSON, net.Format)
assert.Equal(t, false, net.EnableStackTrace)
assert.Equal(t, true, net.EnableSource)
assert.Equal(t, false, net.DisableColor)

core := GetConfig("core")
assert.Equal(t, "", core.Level)
assert.Equal(t, OutputStdout, core.Output)
assert.Equal(t, "", core.Format)
assert.Equal(t, true, core.EnableStackTrace)
assert.Equal(t, false, core.EnableSource)
assert.Equal(t, true, core.DisableColor)
}
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ module github.com/sourcenetwork/corelog

go 1.21.3

require github.com/stretchr/testify v1.8.4
require (
github.com/lmittmann/tint v1.0.4
github.com/stretchr/testify v1.8.4
golang.org/x/term v0.19.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.19.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc=
github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
85 changes: 60 additions & 25 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,26 @@ package corelog

import (
"context"
"io"
"log/slog"
"os"

"github.com/lmittmann/tint"
"golang.org/x/term"
)

const (
// nameKey is the key for the logger name attribute
nameKey = "$name"
// stackKey is the key for the logger stack attribute
stackKey = "$stack"
// msgKey is the key for the logger message attribute
msgKey = "$msg"
// timeKey is the key for the logger time attribute
timeKey = "$time"
// levelKey is the key for the logger level attribute
levelKey = "$level"
// sourceKey is the key for the logger source attribute
sourceKey = "$source"
)

type namedHandler struct {
Expand Down Expand Up @@ -35,27 +52,9 @@ func (h namedHandler) WithGroup(name string) slog.Handler {

func (h namedHandler) Handle(ctx context.Context, record slog.Record) error {
config := GetConfig(h.name)
leveler := namedLeveler(h.name)
opts := &slog.HandlerOptions{
AddSource: config.EnableSource,
Level: leveler,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
switch a.Key {
case slog.MessageKey, slog.LevelKey, slog.TimeKey:
// ignore these built in attributes
// so that we can customize the order
return slog.Attr{}
case slog.SourceKey:
a.Key = "$source"
}
return a
},
}

var output io.Writer
var output *os.File
switch config.Output {
case OutputStderr:
output = os.Stderr
case OutputStdout:
output = os.Stdout
default:
Expand All @@ -66,14 +65,14 @@ func (h namedHandler) Handle(ctx context.Context, record slog.Record) error {

var handler slog.Handler
switch config.Format {
case FormatText:
handler = slog.NewTextHandler(output, opts)
case FormatJSON:
handler = slog.NewJSONHandler(output, opts)
handler = newJSONHandler(config, h.name, output)
default:
// default to slog.TextHandler if no value is set
// default to tint.Handler if no value is set
// or the set value is invalid
handler = slog.NewTextHandler(output, opts)
handler = newTintHandler(config, h.name, output)
// prepend logger name to message
record.Message = h.name + " " + record.Message
}

if len(h.attrs) > 0 {
Expand All @@ -84,3 +83,39 @@ func (h namedHandler) Handle(ctx context.Context, record slog.Record) error {
}
return handler.Handle(ctx, record)
}

func newTintHandler(config Config, name string, output *os.File) slog.Handler {
isTerminal := term.IsTerminal(int(output.Fd()))
return tint.NewHandler(output, &tint.Options{
AddSource: config.EnableSource,
Level: namedLeveler(name),
NoColor: !isTerminal || config.DisableColor,
ReplaceAttr: func(groups []string, attr slog.Attr) slog.Attr {
// ignore name as it is prended to message
if attr.Key == nameKey {
return slog.Attr{}
}
return attr
},
})
}

func newJSONHandler(config Config, name string, output *os.File) *slog.JSONHandler {
return slog.NewJSONHandler(output, &slog.HandlerOptions{
AddSource: config.EnableSource,
Level: namedLeveler(name),
ReplaceAttr: func(groups []string, attr slog.Attr) slog.Attr {
switch attr.Key {
case slog.TimeKey:
attr.Key = timeKey
case slog.LevelKey:
attr.Key = levelKey
case slog.MessageKey:
attr.Key = msgKey
case slog.SourceKey:
attr.Key = sourceKey
}
return attr
},
})
}
9 changes: 3 additions & 6 deletions logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,14 @@ func (l *Logger) log(ctx context.Context, level slog.Level, err error, msg strin
}

r := slog.NewRecord(time.Now(), level, msg, pcs[0])
// add front attributes in specific order
r.Add(slog.String("$time", r.Time.Format(time.DateTime)))
r.Add(slog.String("$name", l.name))
r.Add(slog.Any("$level", r.Level))
r.Add(slog.String("$msg", r.Message))
// add logger name
r.Add(nameKey, l.name)
// add remaining attributes
r.AddAttrs(args...)

// add stack trace if enabled
if err != nil && config.EnableStackTrace {
r.Add("$stack", fmt.Sprintf("%+v", err))
r.Add(stackKey, fmt.Sprintf("%+v", err))
}

_ = l.handler.Handle(ctx, r)
Expand Down
14 changes: 4 additions & 10 deletions logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,9 @@ func TestLoggerLogWithConfigOverride(t *testing.T) {
assert.Equal(t, "test", handler.records[0].Message)

attrs := []slog.Attr{
slog.Any("$name", "test"),
slog.Any("$level", slog.LevelInfo),
slog.Any("$msg", "test"),
slog.Any(nameKey, "test"),
slog.Any("arg1", "val1"),
slog.Any("$stack", fmt.Sprintf("%+v", err)),
slog.Any(stackKey, fmt.Sprintf("%+v", err)),
}
assertRecordAttrs(t, handler.records[0], attrs...)
}
Expand All @@ -75,9 +73,7 @@ func TestLoggerInfoWithEnableSource(t *testing.T) {
assert.NotEqual(t, uintptr(0x00), handler.records[0].PC)

attrs := []slog.Attr{
slog.Any("$name", "test"),
slog.Any("$level", slog.LevelInfo),
slog.Any("$msg", "test"),
slog.Any(nameKey, "test"),
slog.Any("arg1", "val1"),
}
assertRecordAttrs(t, handler.records[0], attrs...)
Expand Down Expand Up @@ -118,9 +114,7 @@ func assertRecordAttrs(
) {
var actual []slog.Attr
record.Attrs(func(a slog.Attr) bool {
if a.Key != "$time" {
actual = append(actual, a)
}
actual = append(actual, a)
return true
})
assert.Equal(t, expected, actual)
Expand Down

0 comments on commit bfd0a2c

Please sign in to comment.