Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork4
🚨 slog: Attribute formatting
License
samber/slog-formatter
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Common formatters forslog library + helpers for building your own.
Sponsored by:

Handlers:
- NewFormatterHandler: main handler
- NewFormatterMiddleware: compatible with
slog-multi
middlewares - RecoverHandlerError: catch panics and error from handlers
Common formatters:
- TimeFormatter: transforms a
time.Time
into a readable string - UnixTimestampFormatter: transforms a
time.Time
into a unix timestamp. - TimezoneConverter: set a
time.Time
to a different timezone - ErrorFormatter: transforms a go error into a readable error
- HTTPRequestFormatter: transforms a *http.Request into a readable object
- HTTPResponseFormatter: transforms a *http.Response into a readable object
- PIIFormatter: hide private Personal Identifiable Information (PII)
- IPAddressFormatter: hide ip address from logs
- FlattenFormatterMiddleware: returns a formatter middleware that flatten attributes recursively
Custom formatter:
- Format: pass any attribute into a formatter
- FormatByKind: pass attributes matching
slog.Kind
into a formatter - FormatByType: pass attributes matching generic type into a formatter
- FormatByKey: pass attributes matching key into a formatter
- FormatByFieldType: pass attributes matching both key and generic type into a formatter
- FormatByGroup: pass attributes under a group into a formatter
- FormatByGroupKey: pass attributes under a group and matching key, into a formatter
- FormatByGroupKeyType: pass attributes under a group, matching key and matching a generic type, into a formatter
See also:
- slog-multi:
slog.Handler
chaining, fanout, routing, failover, load balancing... - slog-formatter:
slog
attribute formatting - slog-sampling:
slog
sampling policy - slog-mock:
slog.Handler
for test purposes
HTTP middlewares:
- slog-gin: Gin middleware for
slog
logger - slog-echo: Echo middleware for
slog
logger - slog-fiber: Fiber middleware for
slog
logger - slog-chi: Chi middleware for
slog
logger - slog-http:
net/http
middleware forslog
logger
Loggers:
- slog-zap: A
slog
handler forZap
- slog-zerolog: A
slog
handler forZerolog
- slog-logrus: A
slog
handler forLogrus
Log sinks:
- slog-datadog: A
slog
handler forDatadog
- slog-betterstack: A
slog
handler forBetterstack
- slog-rollbar: A
slog
handler forRollbar
- slog-loki: A
slog
handler forLoki
- slog-sentry: A
slog
handler forSentry
- slog-syslog: A
slog
handler forSyslog
- slog-logstash: A
slog
handler forLogstash
- slog-fluentd: A
slog
handler forFluentd
- slog-graylog: A
slog
handler forGraylog
- slog-quickwit: A
slog
handler forQuickwit
- slog-slack: A
slog
handler forSlack
- slog-telegram: A
slog
handler forTelegram
- slog-mattermost: A
slog
handler forMattermost
- slog-microsoft-teams: A
slog
handler forMicrosoft Teams
- slog-webhook: A
slog
handler forWebhook
- slog-kafka: A
slog
handler forKafka
- slog-nats: A
slog
handler forNATS
- slog-parquet: A
slog
handler forParquet
+Object Storage
- slog-channel: A
slog
handler for Go channels
go get github.com/samber/slog-formatter
Compatibility: go >= 1.21
No breaking changes will be made to exported APIs before v2.0.0.
- in some case, you should consider implementing
slog.LogValuer
instead of using this library. - use this library carefully, log processing can be very costly (!)
The following example has 3 formatters that anonymize data, format errors and format user. 👇
import (slogformatter"github.com/samber/slog-formatter""log/slog")formatter1:=slogformatter.FormatByKey("very_private_data",func(v slog.Value) slog.Value {returnslog.StringValue("***********")})formatter2:=slogformatter.ErrorFormatter("error")formatter3:=slogformatter.FormatByType(func(uUser) slog.Value {returnslog.StringValue(fmt.Sprintf("%s %s",u.firstname,u.lastname))})logger:=slog.New(slogformatter.NewFormatterHandler(formatter1,formatter2,formatter3)(slog.NewTextHandler(os.Stdout,nil), ),)err:=fmt.Errorf("an error")logger.Error("a message",slog.Any("very_private_data","abcd"),slog.Any("user",user),slog.Any("err",err))// outputs:// time=2023-04-10T14:00:0.000000+00:00 level=ERROR msg="a message" error.message="an error" error.type="*errors.errorString" user="John doe" very_private_data="********"
GoDoc:https://pkg.go.dev/github.com/samber/slog-formatter
Returns a slog.Handler that applies formatters to.
import (slogformatter"github.com/samber/slog-formatter""log/slog")typeUserstruct {emailstringfirstnamestringlastnamestring}formatter1:=slogformatter.FormatByKey("very_private_data",func(v slog.Value) slog.Value {returnslog.StringValue("***********")})formatter2:=slogformatter.ErrorFormatter("error")formatter3:=slogformatter.FormatByType(func(uUser) slog.Value {returnslog.StringValue(fmt.Sprintf("%s %s",u.firstname,u.lastname))})logger:=slog.New(slogformatter.NewFormatterHandler(formatter1,formatter2,formatter3)(slog.NewTextHandler(os.StdErr,nil), ),)err:=fmt.Errorf("an error")logger.Error("a message",slog.Any("very_private_data","abcd"),slog.Any("user",user),slog.Any("err",err))// outputs:// time=2023-04-10T14:00:0.000000+00:00 level=ERROR msg="a message" error.message="an error" error.type="*errors.errorString" user="John doe" very_private_data="********"
Returns aslog-multi
middleware that applies formatters to.
import (slogformatter"github.com/samber/slog-formatter"slogmulti"github.com/samber/slog-multi""log/slog")formatter1:=slogformatter.FormatByKey("very_private_data",func(v slog.Value) slog.Value {returnslog.StringValue("***********")})formatter2:=slogformatter.ErrorFormatter("error")formatter3:=slogformatter.FormatByType(func(uUser) slog.Value {returnslog.StringValue(fmt.Sprintf("%s %s",u.firstname,u.lastname))})formattingMiddleware:=slogformatter.NewFormatterHandler(formatter1,formatter2,formatter3)sink:=slog.NewJSONHandler(os.Stderr, slog.HandlerOptions{})logger:=slog.New(slogmulti.Pipe(formattingMiddleware).Handler(sink),)err:=fmt.Errorf("an error")logger.Error("a message",slog.Any("very_private_data","abcd"),slog.Any("user",user),slog.Any("err",err))// outputs:// time=2023-04-10T14:00:0.000000+00:00 level=ERROR msg="a message" error.message="an error" error.type="*errors.errorString" user="John doe" very_private_data="********"
Returns aslog.Handler
that recovers from panics or error of the chain of handlers.
import (slogformatter"github.com/samber/slog-formatter"slogmulti"github.com/samber/slog-multi""log/slog")recovery:=slogformatter.RecoverHandlerError(func(ctx context.Context,record slog.Record,errerror) {// will be called only if subsequent handlers fail or return an errorlog.Println(err.Error()) },)sink:=NewSinkHandler(...)logger:=slog.New(slogmulti.Pipe(recovery).Handler(sink),)err:=fmt.Errorf("an error")logger.Error("a message",slog.Any("very_private_data","abcd"),slog.Any("user",user),slog.Any("err",err))// outputs:// time=2023-04-10T14:00:0.000000+00:00 level=ERROR msg="a message" error.message="an error" error.type="*errors.errorString" user="John doe" very_private_data="********"
Transforms atime.Time
into a readable string.
slogformatter.NewFormatterHandler(slogformatter.TimeFormatter(time.DateTime,time.UTC),)
Transforms atime.Time
into a unix timestamp.
slogformatter.NewFormatterHandler(slogformatter.UnixTimestampFormatter(time.Millisecond),)
Set atime.Time
to a different timezone.
slogformatter.NewFormatterHandler(slogformatter.TimezoneConverter(time.UTC),)
Transforms a Go error into a readable error.
import (slogformatter"github.com/samber/slog-formatter""log/slog")logger:=slog.New(slogformatter.NewFormatterHandler(slogformatter.ErrorFormatter("error"), )(slog.NewTextHandler(os.Stdout,nil), ),)err:=fmt.Errorf("an error")logger.Error("a message",slog.Any("error",err))// outputs:// {// "time":"2023-04-10T14:00:0.000000+00:00",// "level": "ERROR",// "msg": "a message",// "error": {// "message": "an error",// "type": "*errors.errorString"// "stacktrace": "main.main()\n\t/Users/samber/src/github.com/samber/slog-formatter/example/example.go:108 +0x1c\n"// }// }
Transforms *http.Request and *http.Response into readable objects.
import (slogformatter"github.com/samber/slog-formatter""log/slog")logger:=slog.New(slogformatter.NewFormatterHandler(slogformatter.HTTPRequestFormatter(false),slogformatter.HTTPResponseFormatter(false), )(slog.NewJSONHandler(os.Stdout,nil), ),)req,_:=http.NewRequest(http.MethodGet,"https://api.screeb.app",nil)req.Header.Set("Content-Type","application/json")req.Header.Set("X-TOKEN","1234567890")res,_:=http.DefaultClient.Do(req)logger.Error("a message",slog.Any("request",req),slog.Any("response",res))
Hides private Personal Identifiable Information (PII).
IDs are kept as is. Values longer than 5 characters have a plain text prefix.
import (slogformatter"github.com/samber/slog-formatter""log/slog")logger:=slog.New(slogformatter.NewFormatterHandler(slogformatter.PIIFormatter("user"), )(slog.NewTextHandler(os.Stdout,nil), ),)logger.With(slog.Group("user",slog.String("id","bd57ffbd-8858-4cc4-a93b-426cef16de61"),slog.String("email","foobar@example.com"),slog.Group("address",slog.String("street","1st street"),slog.String("city","New York"),slog.String("country","USA"),slog.Int("zip",12345), ), ), ).Error("an error")// outputs:// {// "time":"2023-04-10T14:00:0.000000+00:00",// "level": "ERROR",// "msg": "an error",// "user": {// "id": "bd57ffbd-8858-4cc4-a93b-426cef16de61",// "email": "foob*******",// "address": {// "street": "1st *******",// "city": "New *******",// "country": "*******",// "zip": "*******"// }// }// }
Transforms an IP address into "********".
import (slogformatter"github.com/samber/slog-formatter""log/slog")logger:=slog.New(slogformatter.NewFormatterHandler(slogformatter.IPAddressFormatter("ip_address"), )(slog.NewTextHandler(os.Stdout,nil), ),)logger.With("ip_address","1.2.3.4").Error("an error")// outputs:// {// "time":"2023-04-10T14:00:0.000000+00:00",// "level": "ERROR",// "msg": "an error",// "ip_address": "*******",// }
A formatter middleware that flatten attributes recursively.
import (slogformatter"github.com/samber/slog-formatter"slogmulti"github.com/samber/slog-multi""log/slog")logger:=slog.New(slogmulti.Pipe(slogformatter.FlattenFormatterMiddlewareOptions{Separator:".",Prefix:"attrs",IgnorePath:false}.NewFlattenFormatterMiddlewareOptions()).Handler(slog.NewJSONHandler(os.Stdout,nil)),)logger.With("email","samuel@acme.org").With("environment","dev").WithGroup("group1").With("hello","world").WithGroup("group2").With("hello","world").Error("A message","foo","bar")// outputs:// {// "time": "2023-05-20T22:14:55.857065+02:00",// "level": "ERROR",// "msg": "A message",// "attrs.email": "samuel@acme.org",// "attrs.environment": "dev",// "attrs.group1.hello": "world",// "attrs.group1.group2.hello": "world",// "foo": "bar"// }
Pass every attributes into a formatter.
slogformatter.NewFormatterHandler(slogformatter.Format(func(groups []string,keystring,value slog.Value) slog.Value {// hide everything under "user" groupiflo.Contains(groups,"user") {returnslog.StringValue("****") }returnvalue }),)
Pass attributes matchingslog.Kind
into a formatter.
slogformatter.NewFormatterHandler(slogformatter.FormatByKind(slog.KindDuration,func(value slog.Value) slog.Value {return... }),)
Pass attributes matching generic type into a formatter.
slogformatter.NewFormatterHandler(// format a custom error type slogformatter.FormatByType[*customError](func(err*customError) slog.Value {returnslog.GroupValue(slog.Int("code",err.code),slog.String("message",err.msg), ) }),// format other errors slogformatter.FormatByType[error](func(errerror) slog.Value {returnslog.GroupValue(slog.Int("code",err.Error()),slog.String("type",reflect.TypeOf(err).String()), ) }),)
slog.LogValuer
when possible:
typecustomErrorstruct {...}func (customError)Error()string {...}// implements slog.LogValuerfunc (customError)LogValue() slog.Value {return slog.StringValue(...)}
Pass attributes matching key into a formatter.
slogformatter.NewFormatterHandler(slogformatter.FormatByKey("abcd",func(value slog.Value) slog.Value {return... }),)
Pass attributes matching both key and generic type into a formatter.
slogformatter.NewFormatterHandler(slogformatter.FormatByFieldType[User]("user",func(uUser) slog.Value {return... }),)
Pass attributes under a group into a formatter.
slogformatter.NewFormatterHandler(slogformatter.FormatByGroup([]{"user","address"},func(attr []slog.Attr) slog.Value {return... }),)
Pass attributes under a group and matching key, into a formatter.
slogformatter.NewFormatterHandler(slogformatter.FormatByGroupKey([]{"user","address"},"country",func(value slog.Value) slog.Value {return... }),)
Pass attributes under a group, matching key and matching a generic type, into a formatter.
slogformatter.NewFormatterHandler(slogformatter.FormatByGroupKeyType[string]([]{"user","address"},"country",func(valuestring) slog.Value {return... }),)
- Ping me on twitter@samuelberthe (DMs, mentions, whatever :))
- Fork theproject
- Fixopen issues or request new features
Don't hesitate ;)
# Install some dev dependenciesmake tools# Run testsmaketest# ormake watch-test
Give a ⭐️ if this project helped you!
Copyright © 2023Samuel Berthe.
This project isMIT licensed.
About
🚨 slog: Attribute formatting
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Contributors2
Uh oh!
There was an error while loading.Please reload this page.