Skip to content

Commit

Permalink
Merge pull request #3 from googlecloudrobotics/therjak/log
Browse files Browse the repository at this point in the history
switch log output to json format
  • Loading branch information
therjak authored Jan 12, 2024
2 parents bb7d7c1 + c326ff1 commit 2efd642
Showing 1 changed file with 16 additions and 114 deletions.
130 changes: 16 additions & 114 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,124 +15,26 @@
package ilog

import (
"bytes"
"context"
"fmt"
"io"
"log/slog"
"sync"
"time"
)

type groupOrAttrs struct {
group string
attrs []slog.Attr
}

// LogHandler implements slog.Handler.
// Use NewLogHandler to create instances with correct internal state.
type LogHandler struct {
level slog.Level
goas []groupOrAttrs
mu *sync.Mutex
writer io.Writer
}

// Enabled is part of the slog.Handler interface
func (h *LogHandler) Enabled(_ context.Context, level slog.Level) bool {
return level >= h.level
}

// Handle is part of the slog.Handler interface
func (h *LogHandler) Handle(_ context.Context, r slog.Record) error {
var buf bytes.Buffer
buf.Grow(1024) // should cover most log messages
if !r.Time.IsZero() {
buf.WriteString(r.Time.UTC().Format(time.RFC3339))
buf.WriteRune(' ')
}
buf.WriteString(r.Level.String())
buf.WriteRune(' ')
buf.WriteString(r.Message)
buf.WriteRune(' ')
// TODO: The layout of groups and extra attrs needs some work. So far they
// have not been used so it does not matter.
for _, goa := range h.goas {
if goa.group != "" {
buf.WriteString(goa.group)
buf.WriteString(": ")
} else {
for _, a := range goa.attrs {
h.appendAttr(&buf, a)
}
}
}

r.Attrs(func(a slog.Attr) bool {
h.appendAttr(&buf, a)
return true
})
buf.WriteRune('\n')
h.mu.Lock()
defer h.mu.Unlock()
_, err := h.writer.Write(buf.Bytes())
return err
}

func (h *LogHandler) appendAttr(w io.Writer, a slog.Attr) (int, error) {
if a.Equal(slog.Attr{}) {
return 0, nil
}
switch a.Value.Kind() {
case slog.KindString:
return fmt.Fprintf(w, "%q=%q ", a.Key, a.Value.String())
case slog.KindTime:
return fmt.Fprintf(w, "%q=%q ", a.Key, a.Value.Time().UTC().Format(time.RFC3339))
case slog.KindGroup:
// TODO: how to handle KindGroup?
default:
// slog.KindAny:
// slog.KindBool:
// slog.KindDuration:
// slog.KindFloat64:
// slog.KindInt64:
// slog.KindUint64:
// slog.KindLogValuer:
return fmt.Fprintf(w, "%q=%q ", a.Key, a.Value)
}
return 0, nil
}

func (h *LogHandler) withGroupOrAttrs(goa groupOrAttrs) *LogHandler {
h2 := *h
h2.goas = make([]groupOrAttrs, len(h.goas)+1)
copy(h2.goas, h.goas)
h2.goas[len(h2.goas)-1] = goa
return &h2
}

// WithGroup is part of the slog.Handler interface
func (h *LogHandler) WithGroup(name string) slog.Handler {
if name == "" {
return h
}
return h.withGroupOrAttrs(groupOrAttrs{group: name})
}

// WithAttrs is part of the slog.Handler interface
func (h *LogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
if len(attrs) == 0 {
return h
}
return h.withGroupOrAttrs(groupOrAttrs{attrs: attrs})
}

// NewLogHandler creates a new LogHandler. Only log messages with log level l or
// higher will get written to the writer.
func NewLogHandler(l slog.Level, w io.Writer) *LogHandler {
return &LogHandler{
level: l,
mu: &sync.Mutex{},
writer: w,
}
func NewLogHandler(l slog.Level, w io.Writer) *slog.JSONHandler {
return slog.NewJSONHandler(w, &slog.HandlerOptions{
AddSource: true,
Level: l,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
switch a.Key {
case slog.MessageKey:
a.Key = "message"
case slog.TimeKey:
a.Key = "timestamp"
case slog.LevelKey:
a.Key = "severity"
}
return a
},
})
}

0 comments on commit 2efd642

Please sign in to comment.