Skip to content

Instantly share code, notes, and snippets.

@JonCanning
Last active November 10, 2024 20:53
Show Gist options
  • Save JonCanning/ee2737170b974d10bcba5a280d02c0d7 to your computer and use it in GitHub Desktop.
Save JonCanning/ee2737170b974d10bcba5a280d02c0d7 to your computer and use it in GitHub Desktop.
Go OTel and rotating file Logger
package log
import (
"context"
"log/slog"
"os"
"runtime"
"github.com/bakins/slogotlp"
"github.com/covalenthq/lumberjack"
slogmulti "github.com/samber/slog-multi"
"go.opentelemetry.io/otel/trace"
)
type logWithContext func(ctx context.Context, message string, attributes ...attr)
var noopLogWithContext = func(ctx context.Context, message string, attributes ...attr) {}
var (
Debug logWithContext = noopLogWithContext
Info logWithContext = noopLogWithContext
Warn logWithContext = noopLogWithContext
Error func(ctx context.Context, err error, attributes ...attr) = func(ctx context.Context, err error, attributes ...attr) {}
)
type attr struct {
Key string
Value any
}
func NewAttribute(key string, value any) attr {
return attr{Key: key, Value: value}
}
func OTelLogHandler(ctx context.Context, logLevel string) slog.Handler {
var slogLevel slog.Level
err := slogLevel.UnmarshalText([]byte(logLevel))
if err != nil {
panic(err)
}
slogHandler, err := slogotlp.NewHandler(ctx, slogotlp.WithLevel(slogLevel))
if err != nil {
panic(err)
}
return slogHandler
}
func InitLogger(serviceName string, version string, environment string, logLevel string, handlers ...slog.Handler) {
logFile := &lumberjack.Logger{
Filename: "logs/log.log",
MaxAge: 7,
}
var slogLevel slog.Level
err := slogLevel.UnmarshalText([]byte(logLevel))
if err != nil {
panic(err)
}
slogHandlers := make([]slog.Handler, 0)
slogHandlers = append(slogHandlers, slog.NewJSONHandler(logFile, &slog.HandlerOptions{Level: slogLevel}))
if environment == "test" {
slogHandlers = append(slogHandlers, slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
} else {
slogHandlers = append(slogHandlers, slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slogLevel}))
}
hostname, err := os.Hostname()
if err != nil {
panic(err)
}
slogHandlers = append(slogHandlers, handlers...)
fanoutHandler := slogmulti.Fanout(
slogHandlers...,
)
slogger := *slog.New(fanoutHandler)
writeLog := func(ctx context.Context, logF func(ctx context.Context, msg string, args ...any), message string, serviceAttributes ...attr) {
slogAttrs := make([]any, 0)
for _, attribute := range serviceAttributes {
slogAttrs = append(slogAttrs, slog.Any(attribute.Key, attribute.Value))
}
slogAttrs = append(slogAttrs, slog.String("environment", environment))
slogAttrs = append(slogAttrs, slog.String("host.name", hostname))
slogAttrs = append(slogAttrs, slog.String("service.name", serviceName))
slogAttrs = append(slogAttrs, slog.String("service.version", version))
spanContext := trace.SpanFromContext(ctx).SpanContext()
if spanContext.IsValid() {
attr := slog.String("trace_id", spanContext.TraceID().String())
slogAttrs = append(slogAttrs, attr)
}
logF(ctx, message, slogAttrs...)
}
Debug = func(ctx context.Context, message string, attributes ...attr) {
writeLog(ctx, slogger.DebugContext, message, attributes...)
}
Info = func(ctx context.Context, message string, attributes ...attr) {
writeLog(ctx, slogger.InfoContext, message, attributes...)
}
Warn = func(ctx context.Context, message string, attributes ...attr) {
writeLog(ctx, slogger.WarnContext, message, attributes...)
}
Error = func(ctx context.Context, err error, attributes ...attr) {
if _, file, line, ok := runtime.Caller(1); ok {
attributes = append(attributes, NewAttribute("file", file), NewAttribute("line", line))
}
writeLog(ctx, slogger.ErrorContext, err.Error(), attributes...)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment