Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding TimeFormat option and other changes #1

Merged
merged 4 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ import (
)

type encoder struct {
nocolor bool
noColor bool
timeFormat string
}

func (e *encoder) NewLine(buf *buffer) {
buf.AppendString("\r\n")
buf.AppendByte('\n')
}

func (e *encoder) withColor(b *buffer, c color, f func()) {
if c == "" || e.nocolor {
if c == "" || e.noColor {
f()
return
}
Expand Down Expand Up @@ -69,7 +70,7 @@ func (e *encoder) writeColoredDuration(w *buffer, d time.Duration, seq color) {
}

func (e *encoder) writeTimestamp(buf *buffer, tt time.Time) {
e.writeColoredTime(buf, tt, time.DateTime, colorTimestamp)
e.writeColoredTime(buf, tt, e.timeFormat, colorTimestamp)
buf.AppendByte(' ')
}

Expand Down Expand Up @@ -125,7 +126,7 @@ func (e *encoder) writeValue(buf *buffer, value slog.Value) {
case slog.KindFloat64:
e.writeColoredFloat(buf, value.Float64(), colorAttrValue)
case slog.KindTime:
e.writeColoredTime(buf, value.Time(), time.RFC3339, colorAttrValue)
e.writeColoredTime(buf, value.Time(), e.timeFormat, colorAttrValue)
case slog.KindUint64:
e.writeColoredUint(buf, value.Uint64(), colorAttrValue)
case slog.KindDuration:
Expand All @@ -150,22 +151,31 @@ func (e *encoder) writeValue(buf *buffer, value slog.Value) {
func (e *encoder) writeLevel(buf *buffer, l slog.Level) {
var style color
var str string
var delta int
switch {
case l >= slog.LevelError:
style = colorLevelError
str = "ERR"
delta = int(l - slog.LevelError)
case l >= slog.LevelWarn:
style = colorLevelWarn
str = "WRN"
delta = int(l - slog.LevelWarn)
case l >= slog.LevelInfo:
style = colorLevelInfo
str = "INF"
delta = int(l - slog.LevelInfo)
case l >= slog.LevelDebug:
style = colorLevelDebug
str = "DBG"
delta = int(l - slog.LevelDebug)
default:
style = bold
str = "???"
str = "DBG"
delta = int(l - slog.LevelDebug)
}
if delta != 0 {
str = fmt.Sprintf("%s%+d", str, delta)
}
e.writeColoredString(buf, str, style)
buf.AppendByte(' ')
Expand Down
9 changes: 8 additions & 1 deletion handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"strings"
"sync"
"time"
)

var bufferPool = &sync.Pool{
Expand All @@ -31,6 +32,9 @@ type HandlerOptions struct {

// Disable colorized output
NoColor bool

// TimeFormat is the format used for time.DateTime
TimeFormat string
}

type Handler struct {
Expand All @@ -53,13 +57,16 @@ func NewHandler(out io.Writer, opts *HandlerOptions) *Handler {
if opts.Level == nil {
opts.Level = slog.LevelInfo
}
if opts.TimeFormat == "" {
opts.TimeFormat = time.DateTime
}
opt := *opts // Copy struct
return &Handler{
opts: &opt,
out: out,
group: "",
context: nil,
enc: &encoder{nocolor: opt.NoColor},
enc: &encoder{noColor: opt.NoColor, timeFormat: opt.TimeFormat},
}
}

Expand Down
45 changes: 29 additions & 16 deletions handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,20 @@ func TestHandler_colors(t *testing.T) {
rec := slog.NewRecord(now, slog.LevelInfo, "foobar", 0)
AssertNoError(t, h.Handle(context.Background(), rec))

expected := fmt.Sprintf("\x1b[90m%s\x1b[0m \x1b[92mINF\x1b[0m \x1b[97mfoobar\x1b[0m\r\n", now.Format(time.DateTime))
expected := fmt.Sprintf("\x1b[90m%s\x1b[0m \x1b[92mINF\x1b[0m \x1b[97mfoobar\x1b[0m\n", now.Format(time.DateTime))
AssertEqual(t, expected, buf.String())
}

func TestHandler_TimeFormat(t *testing.T) {
buf := bytes.Buffer{}
h := NewHandler(&buf, &HandlerOptions{TimeFormat: time.RFC3339Nano, NoColor: true})
now := time.Now()
rec := slog.NewRecord(now, slog.LevelInfo, "foobar", 0)
endTime := now.Add(time.Second)
rec.AddAttrs(slog.Time("endtime", endTime))
AssertNoError(t, h.Handle(context.Background(), rec))

expected := fmt.Sprintf("%s INF foobar endtime=%s\n", now.Format(time.RFC3339Nano), endTime.Format(time.RFC3339Nano))
AssertEqual(t, expected, buf.String())
}

Expand All @@ -31,7 +44,7 @@ func TestHandler_NoColor(t *testing.T) {
rec := slog.NewRecord(now, slog.LevelInfo, "foobar", 0)
AssertNoError(t, h.Handle(context.Background(), rec))

expected := fmt.Sprintf("%s INF foobar\r\n", now.Format(time.DateTime))
expected := fmt.Sprintf("%s INF foobar\n", now.Format(time.DateTime))
AssertEqual(t, expected, buf.String())
}

Expand Down Expand Up @@ -63,7 +76,7 @@ func TestHandler_Attr(t *testing.T) {
)
AssertNoError(t, h.Handle(context.Background(), rec))

expected := fmt.Sprintf("%s INF foobar bool=true int=-12 uint=12 float=3.14 foo=bar time=%s dur=1s group.foo=bar group.subgroup.foo=bar err=the error stringer=stringer nostringer={bar}\r\n", now.Format(time.DateTime), now.Format(time.RFC3339))
expected := fmt.Sprintf("%s INF foobar bool=true int=-12 uint=12 float=3.14 foo=bar time=%s dur=1s group.foo=bar group.subgroup.foo=bar err=the error stringer=stringer nostringer={bar}\n", now.Format(time.DateTime), now.Format(time.DateTime))
AssertEqual(t, expected, buf.String())
}

Expand All @@ -84,12 +97,12 @@ func TestHandler_WithAttr(t *testing.T) {
})
AssertNoError(t, h2.Handle(context.Background(), rec))

expected := fmt.Sprintf("%s INF foobar bool=true int=-12 uint=12 float=3.14 foo=bar time=%s dur=1s group.foo=bar group.subgroup.foo=bar\r\n", now.Format(time.DateTime), now.Format(time.RFC3339))
expected := fmt.Sprintf("%s INF foobar bool=true int=-12 uint=12 float=3.14 foo=bar time=%s dur=1s group.foo=bar group.subgroup.foo=bar\n", now.Format(time.DateTime), now.Format(time.DateTime))
AssertEqual(t, expected, buf.String())

buf.Reset()
AssertNoError(t, h.Handle(context.Background(), rec))
AssertEqual(t, fmt.Sprintf("%s INF foobar\r\n", now.Format(time.DateTime)), buf.String())
AssertEqual(t, fmt.Sprintf("%s INF foobar\n", now.Format(time.DateTime)), buf.String())
}

func TestHandler_WithGroup(t *testing.T) {
Expand All @@ -100,31 +113,31 @@ func TestHandler_WithGroup(t *testing.T) {
rec.Add("int", 12)
h2 := h.WithGroup("group1").WithAttrs([]slog.Attr{slog.String("foo", "bar")})
AssertNoError(t, h2.Handle(context.Background(), rec))
expected := fmt.Sprintf("%s INF foobar group1.foo=bar group1.int=12\r\n", now.Format(time.DateTime))
expected := fmt.Sprintf("%s INF foobar group1.foo=bar group1.int=12\n", now.Format(time.DateTime))
AssertEqual(t, expected, buf.String())
buf.Reset()

h3 := h2.WithGroup("group2")
AssertNoError(t, h3.Handle(context.Background(), rec))
expected = fmt.Sprintf("%s INF foobar group1.foo=bar group1.group2.int=12\r\n", now.Format(time.DateTime))
expected = fmt.Sprintf("%s INF foobar group1.foo=bar group1.group2.int=12\n", now.Format(time.DateTime))
AssertEqual(t, expected, buf.String())

buf.Reset()
AssertNoError(t, h.Handle(context.Background(), rec))
AssertEqual(t, fmt.Sprintf("%s INF foobar int=12\r\n", now.Format(time.DateTime)), buf.String())
AssertEqual(t, fmt.Sprintf("%s INF foobar int=12\n", now.Format(time.DateTime)), buf.String())
}

func TestHandler_Levels(t *testing.T) {
levels := map[slog.Level]string{
slog.LevelDebug - 1: "???",
slog.LevelDebug - 1: "DBG-1",
slog.LevelDebug: "DBG",
slog.LevelDebug + 1: "DBG",
slog.LevelDebug + 1: "DBG+1",
slog.LevelInfo: "INF",
slog.LevelInfo + 1: "INF",
slog.LevelInfo + 1: "INF+1",
slog.LevelWarn: "WRN",
slog.LevelWarn + 1: "WRN",
slog.LevelWarn + 1: "WRN+1",
slog.LevelError: "ERR",
slog.LevelError + 1: "ERR",
slog.LevelError + 1: "ERR+1",
}

for l := range levels {
Expand All @@ -137,7 +150,7 @@ func TestHandler_Levels(t *testing.T) {
rec := slog.NewRecord(now, ll, "foobar", 0)
if ll >= l {
AssertNoError(t, h.Handle(context.Background(), rec))
AssertEqual(t, fmt.Sprintf("%s %s foobar\r\n", now.Format(time.DateTime), s), buf.String())
AssertEqual(t, fmt.Sprintf("%s %s foobar\n", now.Format(time.DateTime), s), buf.String())
buf.Reset()
}
}
Expand All @@ -155,10 +168,10 @@ func TestHandler_Source(t *testing.T) {
AssertNoError(t, h.Handle(context.Background(), rec))
cwd, _ := os.Getwd()
file, _ = filepath.Rel(cwd, file)
AssertEqual(t, fmt.Sprintf("%s INF %s:%d > foobar\r\n", now.Format(time.DateTime), file, line), buf.String())
AssertEqual(t, fmt.Sprintf("%s INF %s:%d > foobar\n", now.Format(time.DateTime), file, line), buf.String())
buf.Reset()
AssertNoError(t, h2.Handle(context.Background(), rec))
AssertEqual(t, fmt.Sprintf("%s INF foobar\r\n", now.Format(time.DateTime)), buf.String())
AssertEqual(t, fmt.Sprintf("%s INF foobar\n", now.Format(time.DateTime)), buf.String())
}

func TestHandler_Err(t *testing.T) {
Expand Down
Loading