Skip to content

Commit

Permalink
feat. init
Browse files Browse the repository at this point in the history
  • Loading branch information
PotatoCloud committed Sep 29, 2024
0 parents commit 3880728
Show file tree
Hide file tree
Showing 5 changed files with 443 additions and 0 deletions.
94 changes: 94 additions & 0 deletions colorful.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package log

import (
"log/slog"
"strconv"
)

type Color int32

const (
reset = "\033[0m"

black Color = 30
red Color = 31
green Color = 32
yellow Color = 33
blue Color = 34
magenta Color = 35
cyan Color = 36
lightGray Color = 37
darkGray Color = 90
lightRed Color = 91
lightGreen Color = 92
lightYellow Color = 93
lightBlue Color = 94
lightMagenta Color = 95
lightCyan Color = 96
white Color = 97
)

func (c Color) String() string {
switch c {
case black:
return "30"
case red:
return "31"
case green:
return "32"
case yellow:
return "33"
case blue:
return "34"
case magenta:
return "35"
case cyan:
return "36"
case lightGray:
return "37"
case darkGray:
return "90"
case lightRed:
return "91"
case lightGreen:
return "92"
case lightYellow:
return "93"
case lightBlue:
return "94"
case lightMagenta:
return "95"
case lightCyan:
return "96"
case white:
return "97"
default:
return strconv.Itoa(int(c))
}
}

var (
colorHeader = []byte("\033[")
colorEnd = []byte(reset)
colorAgent byte = 'm'
)

func colorize(c Color, v string) []byte {
b := make([]byte, 0, 7+len(c.String())+len(v))
return append(append(append(append(append(b, colorHeader...), []byte(c.String())...), colorAgent), []byte(v)...), colorEnd...)
}

func getColor(level slog.Level) Color {
switch level {
case slog.LevelDebug:
return darkGray
case slog.LevelInfo:
return cyan
case slog.LevelWarn:
return lightYellow
case slog.LevelError:
return lightRed
default:
return black
}
}
84 changes: 84 additions & 0 deletions common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package log

import (
"bytes"
"encoding/json"
"fmt"
"log/slog"
"sync"
)

var bufferPool = sync.Pool{New: func() any { return &bytes.Buffer{} }}

func buildOutput(bs ...[]byte) []byte {
buf := bufferPool.Get().(*bytes.Buffer)
defer func() {
buf.Reset()
bufferPool.Put(buf)
}()

for _, b := range bs {
buf.Write(b)
buf.WriteByte(' ')
}
buf.WriteByte('\n')

b := buf.Bytes()
return b
}

//func suppressDefaults(
// next func([]string, slog.Attr) slog.Attr,
//) func([]string, slog.Attr) slog.Attr {
// return func(groups []string, a slog.Attr) slog.Attr {
// if a.Method == slog.TimeKey ||
// a.Method == slog.LevelKey ||
// a.Method == slog.MessageKey {
// return slog.Attr{}
// }
// if next == nil {
// return a
// }
// return next(groups, a)
// }
//}

func putAttr(attrs Attrs, attr slog.Attr) {
var v any
switch attr.Value.Kind() {
case slog.KindString:
v = attr.Value.String()
case slog.KindInt64:
v = attr.Value.Int64()
case slog.KindUint64:
v = attr.Value.Uint64()
case slog.KindFloat64:
v = attr.Value.Float64()
case slog.KindBool:
v = attr.Value.Bool()
case slog.KindDuration:
v = attr.Value.Duration()
case slog.KindTime:
v = attr.Value.Time()
case slog.KindAny:
switch x := attr.Value.Any().(type) {
case json.Marshaler:
b, _ := json.Marshal(x)
v = json.RawMessage(b)
case fmt.Stringer:
v = x.String()
case error:
v = x.Error()
default:
b, err := json.Marshal(x)
if err != nil {
panic(fmt.Sprintf("bad kind any: %s", attr.Value.Kind()))
}
v = json.RawMessage(b)
}
default:
panic(fmt.Sprintf("bad kind: %s", attr.Value.Kind()))
}

attrs[attr.Key] = v
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/RealFax/slog

go 1.23
161 changes: 161 additions & 0 deletions handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package log

import (
"context"
"encoding/json"
"io"
"log/slog"
"runtime"
"strconv"
"strings"
"sync"
"time"
)

var TimeFormat = "[15:04:05.000]"

type (
Attrs map[string]any

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

Handler struct {
addSource bool
level slog.Leveler
mu *sync.Mutex
w io.Writer

goas []groupOrAttrs
}

Record struct {
Group string `json:"group,omitempty"`
Level slog.Level `json:"level"`
Time time.Time `json:"time"`
Message string `json:"message"`
Attrs Attrs `json:"attrs"`
Source *string `json:"source,omitempty"`
}
)

func (a Attrs) String() string {
if len(a) == 0 {
return ""
}

if ReleaseMode {
b, _ := json.Marshal(a)
return string(b)
}
b, _ := json.MarshalIndent(a, "", " ")
return string(b)
}

func (r Record) Bytes() []byte {
if ReleaseMode {
b, _ := json.Marshal(r)
return append(b, '\n')
}
bs := [][]byte{
colorize(lightGray, r.Time.Format(TimeFormat)),
}

if r.Group != "" {
bs = append(bs, colorize(yellow, "@"+r.Group+"@"))
}

bs = append(
bs,
colorize(getColor(r.Level), r.Level.String()+":"),
colorize(white, r.Message),
colorize(lightMagenta, r.Attrs.String()),
)
return buildOutput(bs...)
}

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

func (h *Handler) Enabled(_ context.Context, level slog.Level) bool {
return level >= h.level.Level()
}

func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler {
if len(attrs) == 0 {
return h
}
return h.withGroupOrAttrs(groupOrAttrs{attrs: attrs})
}

func (h *Handler) WithGroup(name string) slog.Handler {
if name == "" {
return h
}
return h.withGroupOrAttrs(groupOrAttrs{group: name})
}

func (h *Handler) Handle(_ context.Context, record slog.Record) (err error) {
output := Record{
Level: record.Level,
Time: record.Time,
Message: record.Message,
Attrs: make(Attrs),
}

switch {
case record.NumAttrs() > 0:
record.Attrs(func(attr slog.Attr) bool {
putAttr(output.Attrs, attr)
return true
})
}

group := make([]string, 0, len(h.goas))
for _, goa := range h.goas {
if goa.group != "" {
group = append(group, goa.group)
} else {
for _, a := range goa.attrs {
output.Attrs[a.Key] = a.Value
}
}
}

if len(group) != 0 {
output.Group = strings.Join(group, ".")
}

if h.addSource {
_, filename, line, ok := runtime.Caller(4)
if ok {
src := filename + ":" + strconv.Itoa(line)
output.Source = &src
}
}

_, err = h.w.Write(output.Bytes())
return err
}

func NewHandler(opts *slog.HandlerOptions, w io.Writer) *Handler {
if opts == nil {
opts = &slog.HandlerOptions{}
}

h := &Handler{
addSource: opts.AddSource,
mu: &sync.Mutex{},
w: w,
level: opts.Level,
}

return h
}
Loading

0 comments on commit 3880728

Please sign in to comment.