• Stars
    star
    79
  • Rank 393,763 (Top 8 %)
  • Language
    Go
  • License
    MIT License
  • Created about 1 year ago
  • Updated about 1 month ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🚨 slog: Attribute formatting

slog: Attribute formatting

tag Go Version GoDoc Build Status Go report Coverage Contributors License

Common formatters for slog library + helpers for building your own.

Handlers:

Common formatters:

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:

HTTP middlewares:

Loggers:

Log sinks:

πŸš€ Install

go get github.com/samber/slog-formatter

Compatibility: go >= 1.21

No breaking changes will be made to exported APIs before v2.0.0.

⚠️ Warnings:

  • in some case, you should consider implementing slog.LogValuer instead of using this library.
  • use this library carefully, log processing can be very costly (!)

πŸš€ Getting started

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 {
    return slog.StringValue("***********")
})
formatter2 := slogformatter.ErrorFormatter("error")
formatter3 := slogformatter.FormatByType(func(u User) slog.Value {
	return slog.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="********"

πŸ’‘ Spec

GoDoc: https://pkg.go.dev/github.com/samber/slog-formatter

NewFormatterHandler

Returns a slog.Handler that applies formatters to.

import (
	slogformatter "github.com/samber/slog-formatter"
	"log/slog"
)

type User struct {
	email     string
	firstname string
	lastname  string
}

formatter1 := slogformatter.FormatByKey("very_private_data", func(v slog.Value) slog.Value {
    return slog.StringValue("***********")
})
formatter2 := slogformatter.ErrorFormatter("error")
formatter3 := slogformatter.FormatByType(func(u User) slog.Value {
	return slog.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="********"

NewFormatterMiddleware

Returns a slog-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 {
    return slog.StringValue("***********")
})
formatter2 := slogformatter.ErrorFormatter("error")
formatter3 := slogformatter.FormatByType(func(u User) slog.Value {
	return slog.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="********"

TimeFormatter

Transforms a time.Time into a readable string.

slogformatter.NewFormatterHandler(
    slogformatter.TimeFormatter(time.DateTime, time.UTC),
)

UnixTimestampFormatter

Transforms a time.Time into a unix timestamp.

slogformatter.NewFormatterHandler(
    slogformatter.UnixTimestampFormatter(time.Millisecond),
)

TimezoneConverter

Set a time.Time to a different timezone.

slogformatter.NewFormatterHandler(
    slogformatter.TimezoneConverter(time.UTC),
)

ErrorFormatter

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"
//   }
// }

HTTPRequestFormatter and HTTPResponseFormatter

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))

PIIFormatter

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", "[email protected]"),
            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": "*******"
//     }
//   }
// }

IPAddressFormatter

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": "*******",
// }

FlattenFormatterMiddleware

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", "[email protected]").
    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": "[email protected]",
//   "attrs.environment": "dev",
//   "attrs.group1.hello": "world",
//   "attrs.group1.group2.hello": "world",
//   "foo": "bar"
// }

Format

Pass every attributes into a formatter.

slogformatter.NewFormatterHandler(
    slogformatter.Format(func(groups []string, key string, value slog.Value) slog.Value {
        // hide everything under "user" group
        if lo.Contains(groups, "user") {
            return slog.StringValue("****")
        }

        return value
    }),
)

FormatByKind

Pass attributes matching slog.Kind into a formatter.

slogformatter.NewFormatterHandler(
    slogformatter.FormatByKind(slog.KindDuration, func(value slog.Value) slog.Value {
        return ...
    }),
)

FormatByType

Pass attributes matching generic type into a formatter.

slogformatter.NewFormatterHandler(
    // format a custom error type
    slogformatter.FormatByType[*customError](func(err *customError) slog.Value {
        return slog.GroupValue(
            slog.Int("code", err.code),
            slog.String("message", err.msg),
        )
    }),
    // format other errors
    slogformatter.FormatByType[error](func(err error) slog.Value {
        return slog.GroupValue(
            slog.Int("code", err.Error()),
            slog.String("type", reflect.TypeOf(err).String()),
        )
    }),
)

⚠️ Consider implementing slog.LogValuer when possible:

type customError struct {
    ...
}

func (customError) Error() string {
    ...
}

// implements slog.LogValuer
func (customError) LogValue() slog.Value {
	return slog.StringValue(...)
}

FormatByKey

Pass attributes matching key into a formatter.

slogformatter.NewFormatterHandler(
    slogformatter.FormatByKey("abcd", func(value slog.Value) slog.Value {
        return ...
    }),
)

FormatByFieldType

Pass attributes matching both key and generic type into a formatter.

slogformatter.NewFormatterHandler(
    slogformatter.FormatByFieldType[User]("user", func(u User) slog.Value {
        return ...
    }),
)

FormatByGroup

Pass attributes under a group into a formatter.

slogformatter.NewFormatterHandler(
    slogformatter.FormatByGroup([]{"user", "address"}, func(attr []slog.Attr) slog.Value {
        return ...
    }),
)

FormatByGroupKey

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 ...
    }),
)

FormatByGroupKeyType

Pass attributes under a group, matching key and matching a generic type, into a formatter.

slogformatter.NewFormatterHandler(
    slogformatter.FormatByGroupKeyType[string]([]{"user", "address"}, "country", func(value string) slog.Value {
        return ...
    }),
)

🀝 Contributing

Don't hesitate ;)

# Install some dev dependencies
make tools

# Run tests
make test
# or
make watch-test

πŸ‘€ Contributors

Contributors

πŸ’« Show your support

Give a ⭐️ if this project helped you!

GitHub Sponsors

πŸ“ License

Copyright Β© 2023 Samuel Berthe.

This project is MIT licensed.

More Repositories

1

lo

πŸ’₯ A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...)
Go
15,102
star
2

awesome-prometheus-alerts

🚨 Collection of Prometheus alerting rules
HTML
5,944
star
3

mo

πŸ¦„ Monads and popular FP abstractions, powered by Go 1.18+ Generics (Option, Result, Either...)
Go
2,207
star
4

do

βš™οΈ A dependency injection toolkit based on Go 1.18+ Generics.
Go
1,555
star
5

slog-multi

🚨 Design workflows of slog handlers: pipeline, middleware, fanout, routing, failover, load balancing...
Go
234
star
6

invoice-as-a-service

πŸ’° Simple invoicing service (REST API): from JSON to PDF
PHP
181
star
7

oops

πŸ”₯ Error handling library with context, assertion, stack trace and source fragments
Go
164
star
8

sync-ssh-keys

πŸ” Sync public ssh keys to ~/.ssh/authorized_keys, based on Github/Gitlab organization membership.
Go
134
star
9

chartjs-plugin-datasource-prometheus

πŸ“Š Chart.js plugin for Prometheus
TypeScript
95
star
10

go-gpt-3-encoder

Go BPE tokenizer (Encoder+Decoder) for GPT2 and GPT3
Go
77
star
11

slog-echo

🚨 Echo middleware for slog logger
Go
72
star
12

slog-gin

🚨 Gin middleware for slog logger
Go
65
star
13

the-great-gpt-firewall

πŸ€– A curated list of websites that restrict access to AI Agents, AI crawlers and GPTs
Python
65
star
14

prometheus-query-js

πŸ“Š A Javascript client for Prometheus query API
TypeScript
60
star
15

github-actions-runner

βœ… Docker images for starting self-hosted Github Actions runner(s).
Dockerfile
57
star
16

grafana-flamegraph-panel

πŸ“Š Flame graph panels for Grafana
JavaScript
37
star
17

slog-fiber

🚨 Fiber middleware for slog logger
Go
35
star
18

slog-sampling

🚨 slog sampling: drop repetitive log records
Go
35
star
19

workshop-prometheus-grafana

πŸ“Š Prometheus and Grafana 101
JavaScript
30
star
20

slog-sentry

🚨 slog: Sentry handler
Go
30
star
21

slog-chi

🚨 Chi middleware for slog logger
Go
22
star
22

awesome-olap

A curated list of awesome Online Analytical Processing databases, frameworks, ressources and other awesomeness.
16
star
23

go-amqp-pubsub

Fault tolerant Pub/Sub library for RabbitMQ
Go
16
star
24

pg_cron

⏰ PostgreSQL extension for running periodic jobs
C
15
star
25

slog-loki

🚨 slog: Loki handler
Go
14
star
26

arp-spoofing

πŸ’₯ Simple implementation of arp poisoning attack ;)
C
14
star
27

slog-slack

🚨 slog: Slack handler
Go
14
star
28

slog-zap

🚨 slog: Zap handler
Go
12
star
29

slog-zerolog

🚨 slog: Zerolog handler
Go
12
star
30

go-tcp-pool

✨ Drop-in replacement to net.Conn with pooling and auto-reconnect
Go
11
star
31

refined-hn

JavaScript
11
star
32

slog-logrus

🚨 slog: Logrus handler
Go
11
star
33

slog-http

🚨 net/http middleware for slog logger
Go
10
star
34

free_proxy_list

Free proxy list [NOT MAINTAINED ANYMORE - please fork]
Shell
9
star
35

slog-syslog

🚨 slog: Syslog handler
Go
9
star
36

slog-parquet

🚨 slog: Parquet handler + Object Storage
Go
9
star
37

go-type-to-string

πŸ•΅οΈβ€β™‚οΈ Extract a string representation of Go type
Go
8
star
38

git-contrib-graph

πŸ“Š Displays a github-like contribution graph, of every contributors of a repository
Go
8
star
39

powEUr

Python
7
star
40

node-promfiler

Expose a http endpoint for exporting node.js v8 profiling
JavaScript
7
star
41

slog-datadog

🚨 slog: Datadog handler
Go
7
star
42

slog-channel

🚨 slog: Go channel handler
Go
5
star
43

go-singleflightx

🧬 x/sync/singleflight but with generics, batching and nullable result
Go
5
star
44

slog-nats

🚨 slog: NATS handler
Go
5
star
45

slog-kafka

🚨 slog: Kafka handler
Go
4
star
46

remote-dev-environment

πŸ‘¨β€πŸ’» My development environment is too slow, let's fix that !
4
star
47

GoogleCalendarNotifier-FitbitTracker

Google Calendar notifier for Fitbit Tracker
Gosu
4
star
48

ansible-role-airbyte

Ansible role for Airbyte
4
star
49

criterion-rpm-package

RPM package for Criterion (C unit testing)
Shell
3
star
50

dagobert

A simple Go client for the clip-as-service server
Go
3
star
51

rabbitmq-flooding

Cluster recovery testing. Floods RabbitMQ with random data.
Python
3
star
52

slog-graylog

🚨 slog: Graylog handler
Go
3
star
53

go-psi

πŸ₯΅ Pressure Stall Informations (PSI) and starvation notifier
Go
3
star
54

slog-telegram

🚨 slog: Telegram handler
Go
3
star
55

hot

🌢️ In-memory caching library for Go
Go
3
star
56

llvm_dart_binding

Binding Dart/LLVM (using LLVM bytecode from Dart)
Dart
3
star
57

slog-webhook

🚨 slog: Webhook handler
Go
3
star
58

slog-common

Common toolchain for slog
Go
2
star
59

slog-logstash

🚨 slog: Logstash handler
Go
2
star
60

lab-langchain-getting-started

Python
2
star
61

BTCC_api

A basic API wrapper for the BTCC Trading and Market FIX API.
JavaScript
2
star
62

github-stackoverflow-email-scrapping

Scrape top Github and Stack-Overflow users to find email address
Go
2
star
63

ngx-domarrow

Declarative and template-driven DOMArrow integration for Angular2+
TypeScript
2
star
64

celery_demonstration

Async worker + scheduling
Python
2
star
65

go-metered-io

πŸ“ A drop-in replacement to io.Reader and io.Writer with the total number of bytes transfered.
Go
2
star
66

dotfiles

@samber's dotfiles
JavaScript
1
star
67

grafana-dashboard-nomad

Grafana dashboards for Nomad (Docker orchestrator from Hashicorp)
1
star
68

slog-fluentd

🚨 slog: Fluentd handler
Go
1
star
69

go-clevercloud-api

Go library for Clever-Cloud api
Go
1
star
70

lab-langchain

Python
1
star
71

slog-mattermost

🚨 slog: Mattermost handler
Go
1
star
72

dockerfiles

Dockerfile
1
star
73

jitsi-virtual-background

JavaScript
1
star
74

SaaS-Cookbook-List

List of Cookbook about SaaS development (ENG/FR)
1
star
75

raw-ip-udp-sockets-chap

Simple implementation of CHAP protocol, with raw socket layers (3+4)
C
1
star
76

lab-parquet

Go
1
star
77

nft-http-api

🚦 NFT over HTTP API
Go
1
star
78

canvas-to-bmp

TypeScript
1
star
79

refined-cycle-app

JavaScript
1
star
80

azure-ad-oauth2-proxy

Dockerfile
1
star
81

packer-qemu-debian

Builds Debian 8 image for Qemu
Shell
1
star
82

poc-selenium-unit-test-css

Python
1
star
83

maxscale-experiments

Demonstration step-by-step of MaxScale for master/slave query spliting/routing #mysql #docker
Shell
1
star
84

messenger-bot-clock

Messenger bot replying with current time
JavaScript
1
star
85

hello-world-node-pg-redis

Simple health check with NodeJS + Redis + PostgreSQL
JavaScript
1
star
86

slog-microsoft-teams

🚨 slog: Microsoft Teams handler
Go
1
star
87

fb-messenger-bot-psychologist

πŸ€– A Messenger bot talking like a psychologist
Emacs Lisp
1
star