Redmage/pkg/log/log.go

296 lines
7.6 KiB
Go
Raw Normal View History

2024-04-06 01:22:00 +07:00
package log
import (
"context"
"fmt"
"io"
"log/slog"
2024-04-12 22:47:22 +07:00
"net/http"
2024-04-06 01:22:00 +07:00
"os"
"strings"
"time"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/tigorlazuardi/redmage/config"
2024-04-06 22:09:17 +07:00
"github.com/tigorlazuardi/redmage/pkg/caller"
"go.opentelemetry.io/otel/trace"
2024-04-12 22:47:22 +07:00
slogmulti "github.com/samber/slog-multi"
2024-04-06 01:22:00 +07:00
)
var handler slog.Handler = NullHandler{}
func NewHandler(cfg *config.Config) slog.Handler {
2024-04-12 22:47:22 +07:00
var handlers []slog.Handler
if cfg.Bool("log.enable") {
handlers = append(handlers, createStandardLogger(cfg))
}
if cfg.Bool("telemetry.openobserve.enable") && cfg.Bool("telemetry.openobserve.log.enable") {
handlers = append(handlers, createO2Logger(cfg))
}
if len(handlers) == 0 {
2024-04-06 01:22:00 +07:00
return NullHandler{}
}
2024-04-12 22:47:22 +07:00
return slogmulti.Fanout(handlers...)
}
func createStandardLogger(cfg *config.Config) slog.Handler {
2024-04-06 01:22:00 +07:00
var output io.Writer
2024-04-12 22:47:22 +07:00
2024-04-06 01:22:00 +07:00
if strings.ToLower(cfg.String("log.output")) == "stdout" {
output = colorable.NewColorableStdout()
} else {
output = colorable.NewColorableStderr()
}
var lvl slog.Level
_ = lvl.UnmarshalText(cfg.Bytes("log.level"))
opts := &slog.HandlerOptions{
AddSource: cfg.Bool("log.source"),
Level: lvl,
}
format := strings.ToLower(cfg.String("log.format"))
if isatty.IsTerminal(os.Stdout.Fd()) && format == "pretty" {
return NewPrettyHandler(output, opts)
} else {
return slog.NewJSONHandler(output, opts)
}
}
2024-04-12 22:47:22 +07:00
func createO2Logger(cfg *config.Config) slog.Handler {
var lvl slog.Level
_ = lvl.UnmarshalText(cfg.Bytes("telemetry.openobserve.log.level"))
opts := &slog.HandlerOptions{
AddSource: cfg.Bool("telemetry.openobserve.log.source"),
Level: lvl,
}
return NewOpenObserveHandler(OpenObserveHandlerOptions{
HandlerOptions: opts,
BufferSize: cfg.Int("telemetry.openobserve.log.buffer.size"),
BufferTimeout: cfg.Duration("telemetry.openobserve.log.buffer.timeout"),
Concurrency: cfg.Int("telemetry.openobserve.log.concurrency"),
Endpoint: cfg.String("telemetry.openobserve.log.endpoint"),
HTTPClient: http.DefaultClient,
Username: cfg.String("telemetry.openobserve.log.username"),
Password: cfg.String("telemetry.openobserve.log.password"),
})
}
2024-04-06 01:22:00 +07:00
type Entry struct {
2024-04-08 15:48:45 +07:00
ctx context.Context
handler slog.Handler
caller caller.Caller
time time.Time
err error
level slog.Level
withAttrs []slog.Attr
2024-04-06 01:22:00 +07:00
}
2024-04-08 15:48:45 +07:00
// New prepares a new entry to write logs.
func New(ctx context.Context) *Entry {
2024-04-06 01:22:00 +07:00
h := FromContext(ctx)
if h == nil {
h = handler
}
return &Entry{ctx: ctx, handler: h, time: time.Now()}
}
2024-04-06 20:42:01 +07:00
func (entry *Entry) Caller(caller caller.Caller) *Entry {
entry.caller = caller
2024-04-06 01:22:00 +07:00
return entry
}
2024-04-07 16:06:33 +07:00
func (entry *Entry) Err(err error) *Entry {
entry.err = err
return entry
}
2024-04-08 15:48:45 +07:00
func (entry *Entry) Level(lvl slog.Level) *Entry {
entry.level = lvl
return entry
}
// With adds fields to top level of the log entry.
func (entry *Entry) With(fields ...any) *Entry {
entry.withAttrs = append(entry.withAttrs, argsToAttrSlice(fields)...)
return entry
}
const badKey = "!BADKEY"
func argsToAttrSlice(args []any) []slog.Attr {
var (
attr slog.Attr
attrs []slog.Attr
)
for len(args) > 0 {
attr, args = argsToAttr(args)
attrs = append(attrs, attr)
}
return attrs
}
func argsToAttr(args []any) (slog.Attr, []any) {
switch x := args[0].(type) {
case string:
if len(args) == 1 {
return slog.String(badKey, x), nil
}
return slog.Any(x, args[1]), args[2:]
case slog.Attr:
return x, args[1:]
default:
return slog.Any(badKey, x), args[1:]
}
}
func (entry *Entry) Log(message string, fields ...any) {
2024-04-08 16:20:16 +07:00
if !entry.handler.Enabled(entry.ctx, entry.level) {
return
}
2024-04-08 15:48:45 +07:00
record := slog.NewRecord(entry.time, entry.level, message, entry.getCaller().PC)
record.AddAttrs(entry.getExtra()...)
record.AddAttrs(slog.Group("details", fields...))
if entry.err != nil {
record.AddAttrs(slog.Any("error", entry.err))
}
_ = entry.handler.Handle(entry.ctx, record)
}
2024-04-06 01:22:00 +07:00
func (entry *Entry) Info(message string, fields ...any) {
2024-04-08 16:20:16 +07:00
if !entry.handler.Enabled(entry.ctx, slog.LevelInfo) {
return
}
2024-04-06 20:42:01 +07:00
record := slog.NewRecord(entry.time, slog.LevelInfo, message, entry.getCaller().PC)
2024-04-06 01:22:00 +07:00
record.AddAttrs(entry.getExtra()...)
2024-04-07 16:06:33 +07:00
record.AddAttrs(slog.Group("details", fields...))
if entry.err != nil {
record.AddAttrs(slog.Any("error", entry.err))
}
2024-04-06 01:22:00 +07:00
_ = entry.handler.Handle(entry.ctx, record)
}
func (entry *Entry) Infof(format string, args ...any) {
2024-04-08 16:20:16 +07:00
if !entry.handler.Enabled(entry.ctx, slog.LevelInfo) {
return
}
2024-04-06 01:22:00 +07:00
message := fmt.Sprintf(format, args...)
2024-04-06 20:42:01 +07:00
record := slog.NewRecord(entry.time, slog.LevelInfo, message, entry.getCaller().PC)
2024-04-06 01:22:00 +07:00
record.AddAttrs(entry.getExtra()...)
2024-04-07 16:06:33 +07:00
if entry.err != nil {
record.AddAttrs(slog.Any("error", entry.err))
}
2024-04-06 01:22:00 +07:00
_ = entry.handler.Handle(entry.ctx, record)
}
func (entry *Entry) Error(message string, fields ...any) {
2024-04-08 16:20:16 +07:00
if !entry.handler.Enabled(entry.ctx, slog.LevelError) {
return
}
2024-04-06 20:42:01 +07:00
record := slog.NewRecord(entry.time, slog.LevelError, message, entry.getCaller().PC)
2024-04-06 01:22:00 +07:00
record.AddAttrs(entry.getExtra()...)
2024-04-07 16:06:33 +07:00
record.AddAttrs(slog.Group("details", fields...))
if entry.err != nil {
record.AddAttrs(slog.Any("error", entry.err))
}
2024-04-06 01:22:00 +07:00
_ = entry.handler.Handle(entry.ctx, record)
}
func (entry *Entry) Errorf(format string, args ...any) {
2024-04-08 16:20:16 +07:00
if !entry.handler.Enabled(entry.ctx, slog.LevelError) {
return
}
2024-04-06 01:22:00 +07:00
message := fmt.Sprintf(format, args...)
2024-04-06 20:42:01 +07:00
record := slog.NewRecord(entry.time, slog.LevelError, message, entry.getCaller().PC)
2024-04-06 01:22:00 +07:00
record.AddAttrs(entry.getExtra()...)
2024-04-07 16:06:33 +07:00
if entry.err != nil {
record.AddAttrs(slog.Any("details", entry.err))
}
2024-04-06 01:22:00 +07:00
_ = entry.handler.Handle(entry.ctx, record)
}
func (entry *Entry) Debug(message string, fields ...any) {
2024-04-08 16:20:16 +07:00
if !entry.handler.Enabled(entry.ctx, slog.LevelDebug) {
return
}
2024-04-06 20:42:01 +07:00
record := slog.NewRecord(entry.time, slog.LevelDebug, message, entry.getCaller().PC)
2024-04-06 01:22:00 +07:00
record.AddAttrs(entry.getExtra()...)
2024-04-07 16:06:33 +07:00
record.AddAttrs(slog.Group("details", fields...))
if entry.err != nil {
record.AddAttrs(slog.Any("error", entry.err))
}
2024-04-06 01:22:00 +07:00
_ = entry.handler.Handle(entry.ctx, record)
}
func (entry *Entry) Debugf(format string, args ...any) {
2024-04-08 16:20:16 +07:00
if !entry.handler.Enabled(entry.ctx, slog.LevelDebug) {
return
}
2024-04-06 01:22:00 +07:00
message := fmt.Sprintf(format, args...)
2024-04-06 20:42:01 +07:00
record := slog.NewRecord(entry.time, slog.LevelDebug, message, entry.getCaller().PC)
2024-04-06 01:22:00 +07:00
record.AddAttrs(entry.getExtra()...)
2024-04-07 16:06:33 +07:00
if entry.err != nil {
record.AddAttrs(slog.Any("error", entry.err))
}
2024-04-06 01:22:00 +07:00
_ = entry.handler.Handle(entry.ctx, record)
}
func (entry *Entry) Warn(message string, fields ...any) {
2024-04-08 16:20:16 +07:00
if !entry.handler.Enabled(entry.ctx, slog.LevelWarn) {
return
}
2024-04-06 20:42:01 +07:00
record := slog.NewRecord(entry.time, slog.LevelWarn, message, entry.getCaller().PC)
2024-04-06 01:22:00 +07:00
record.AddAttrs(entry.getExtra()...)
2024-04-07 16:06:33 +07:00
record.AddAttrs(slog.Group("details", fields...))
if entry.err != nil {
record.AddAttrs(slog.Any("error", entry.err))
}
2024-04-06 01:22:00 +07:00
_ = entry.handler.Handle(entry.ctx, record)
}
func (entry *Entry) Warnf(format string, args ...any) {
2024-04-08 16:20:16 +07:00
if !entry.handler.Enabled(entry.ctx, slog.LevelWarn) {
return
}
2024-04-06 01:22:00 +07:00
message := fmt.Sprintf(format, args...)
2024-04-06 20:42:01 +07:00
record := slog.NewRecord(entry.time, slog.LevelWarn, message, entry.getCaller().PC)
2024-04-06 01:22:00 +07:00
record.AddAttrs(entry.getExtra()...)
2024-04-07 16:06:33 +07:00
if entry.err != nil {
record.AddAttrs(slog.Any("error", entry.err))
}
2024-04-06 01:22:00 +07:00
_ = entry.handler.Handle(entry.ctx, record)
}
2024-04-06 20:42:01 +07:00
func (entry *Entry) getCaller() caller.Caller {
if entry.caller.PC != 0 {
2024-04-06 01:22:00 +07:00
return entry.caller
}
2024-04-06 20:42:01 +07:00
return caller.New(4)
2024-04-06 01:22:00 +07:00
}
func (entry *Entry) getExtra() []slog.Attr {
2024-04-08 15:48:45 +07:00
out := make([]slog.Attr, 0, 4)
if span := trace.SpanFromContext(entry.ctx); span.IsRecording() {
out = append(out,
slog.String("trace.id", span.SpanContext().TraceID().String()),
slog.String("span.id", span.SpanContext().SpanID().String()),
)
2024-04-06 01:22:00 +07:00
}
2024-04-08 15:48:45 +07:00
out = append(out, entry.withAttrs...)
2024-04-06 01:22:00 +07:00
return out
}
func SetDefault(h slog.Handler) {
handler = h
}