sloggin

package module
v1.11.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 21, 2024 License: MIT Imports: 10 Imported by: 0

README ΒΆ

slog: Gin middleware

Changes compared to source repo.

slog-gin.NewWithConfig() now takes reference to config. so we can update the config on the runtime. this gets utilized at the webserver so we can put changes at the runtime and print the request/response body which helps in debug purpose.

tag Go Version GoDoc Build Status Go report Coverage Contributors License

Gin middleware to log http requests using slog.

See also:

πŸš€ Install

go get github.com/samber/slog-gin

Compatibility: go >= 1.21

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

πŸ’‘ Usage

Minimal
import (
	"github.com/gin-gonic/gin"
	sloggin "github.com/samber/slog-gin"
	"log/slog"
)

// Create a slog logger, which:
//   - Logs to stdout.
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

router := gin.New()

// Add the sloggin middleware to all routes.
// The middleware will log all requests attributes.
router.Use(sloggin.New(logger))
router.Use(gin.Recovery())

// Example pong request.
router.GET("/pong", func(c *gin.Context) {
    c.String(http.StatusOK, "pong")
})

router.Run(":1234")

// output:
// time=2023-10-15T20:32:58.926+02:00 level=INFO msg="Incoming request" env=production request.time=2023-10-15T20:32:58.626+02:00 request.method=GET request.path=/ request.query="" request.route="" request.ip=127.0.0.1:63932 request.length=0 response.time=2023-10-15T20:32:58.926+02:00 response.latency=100ms response.status=200 response.length=7 id=""
OTEL
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

config := sloggin.Config{
	WithSpanID:  true,
	WithTraceID: true,
}

router := gin.New()
router.Use(sloggin.NewWithConfig(logger, config))
Custom log levels
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

config := sloggin.Config{
	DefaultLevel:     slog.LevelInfo,
	ClientErrorLevel: slog.LevelWarn,
	ServerErrorLevel: slog.LevelError,
}

router := gin.New()
router.Use(sloggin.NewWithConfig(logger, config))
Verbose
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

config := sloggin.Config{
	WithRequestBody: true,
	WithResponseBody: true,
	WithRequestHeader: true,
	WithResponseHeader: true,
}

router := gin.New()
router.Use(sloggin.NewWithConfig(logger, config))
Filters
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

router := gin.New()
router.Use(
	sloggin.NewWithFilters(
		logger,
		sloggin.Accept(func (c *gin.Context) bool {
			return xxx
		}),
		sloggin.IgnoreStatus(401, 404),
	),
)

Available filters:

  • Accept / Ignore
  • AcceptMethod / IgnoreMethod
  • AcceptStatus / IgnoreStatus
  • AcceptStatusGreaterThan / IgnoreStatusLessThan
  • AcceptStatusGreaterThanOrEqual / IgnoreStatusLessThanOrEqual
  • AcceptPath / IgnorePath
  • AcceptPathContains / IgnorePathContains
  • AcceptPathPrefix / IgnorePathPrefix
  • AcceptPathSuffix / IgnorePathSuffix
  • AcceptPathMatch / IgnorePathMatch
  • AcceptHost / IgnoreHost
  • AcceptHostContains / IgnoreHostContains
  • AcceptHostPrefix / IgnoreHostPrefix
  • AcceptHostSuffix / IgnoreHostSuffix
  • AcceptHostMatch / IgnoreHostMatch
Using custom time formatters
import (
	"github.com/gin-gonic/gin"
	sloggin "github.com/samber/slog-gin"
	slogformatter "github.com/samber/slog-formatter"
	"log/slog"
)

// Create a slog logger, which:
//   - Logs to stdout.
//   - RFC3339 with UTC time format.
logger := slog.New(
    slogformatter.NewFormatterHandler(
        slogformatter.TimezoneConverter(time.UTC),
        slogformatter.TimeFormatter(time.DateTime, nil),
    )(
        slog.NewTextHandler(os.Stdout, nil),
    ),
)

router := gin.New()

// Add the sloggin middleware to all routes.
// The middleware will log all requests attributes.
router.Use(sloggin.New(logger))
router.Use(gin.Recovery())

// Example pong request.
router.GET("/pong", func(c *gin.Context) {
    c.String(http.StatusOK, "pong")
})

router.Run(":1234")

// output:
// time=2023-10-15T20:32:58.926+02:00 level=INFO msg="Incoming request" env=production request.time=2023-10-15T20:32:58Z request.method=GET request.path=/ request.query="" request.route="" request.ip=127.0.0.1:63932 request.length=0 response.time=2023-10-15T20:32:58Z response.latency=100ms response.status=200 response.length=7 id=""
Using custom logger sub-group
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

router := gin.New()

// Add the sloggin middleware to all routes.
// The middleware will log all requests attributes under a "http" group.
router.Use(sloggin.New(logger.WithGroup("http")))
router.Use(gin.Recovery())

// Example pong request.
router.GET("/pong", func(c *gin.Context) {
    c.String(http.StatusOK, "pong")
})

router.Run(":1234")

// output:
// time=2023-10-15T20:32:58.926+02:00 level=INFO msg="Incoming request" env=production http.request.time=2023-10-15T20:32:58.626+02:00 http.request.method=GET http.request.path=/ request.query="" http.request.route="" http.request.ip=127.0.0.1:63932 http.request.length=0 http.response.time=2023-10-15T20:32:58.926+02:00 http.response.latency=100ms http.response.status=200 http.response.length=7 http.id=""
Add logger to a single route
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

router := gin.New()
router.Use(gin.Recovery())

// Example pong request.
// Add the sloggin middleware to a single routes.
router.GET("/pong", sloggin.New(logger), func(c *gin.Context) {
    c.String(http.StatusOK, "pong")
})

router.Run(":1234")
Adding custom attributes
logger := slog.New(slog.NewTextHandler(os.Stdout, nil)).
    With("environment", "production").
    With("server", "gin/1.9.0").
    With("server_start_time", time.Now()).
    With("gin_mode", gin.EnvGinMode)

router := gin.New()

// Add the sloggin middleware to all routes.
// The middleware will log all requests attributes.
router.Use(sloggin.New(logger))
router.Use(gin.Recovery())

// Example pong request.
router.GET("/pong", func(c *gin.Context) {
	// Add an attribute to a single log entry.
	sloggin.AddCustomAttributes(c, slog.String("foo", "bar"))
    c.String(http.StatusOK, "pong")
})

router.Run(":1234")

// output:
// time=2023-10-15T20:32:58.926+02:00 level=INFO msg="Incoming request" environment=production server=gin/1.9.0 gin_mode=release request.time=2023-10-15T20:32:58.626+02:00 request.method=GET request.path=/ request.query="" request.route="" request.ip=127.0.0.1:63932 request.length=0 response.time=2023-10-15T20:32:58.926+02:00 response.latency=100ms response.status=200 response.length=7 id="" foo=bar
JSON output
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

router := gin.New()

// Add the sloggin middleware to all routes.
// The middleware will log all requests attributes.
router.Use(sloggin.New(logger))
router.Use(gin.Recovery())

// Example pong request.
router.GET("/pong", func(c *gin.Context) {
    c.String(http.StatusOK, "pong")
})

router.Run(":1234")

// output:
// {"time":"2023-10-15T20:32:58.926+02:00","level":"INFO","msg":"Incoming request","gin_mode":"GIN_MODE","env":"production","http":{"request":{"time":"2023-10-15T20:32:58.626+02:00","method":"GET","path":"/","query":"","route":"","ip":"127.0.0.1:55296","length":0},"response":{"time":"2023-10-15T20:32:58.926+02:00","latency":100000,"status":200,"length":7},"id":""}}

🀝 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.

Documentation ΒΆ

Index ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

View Source
var (
	RequestBodyMaxSize  = 64 * 1024 // 64KB
	ResponseBodyMaxSize = 64 * 1024 // 64KB

	HiddenRequestHeaders = map[string]struct{}{
		"authorization": {},
		"cookie":        {},
		"set-cookie":    {},
		"x-auth-token":  {},
		"x-csrf-token":  {},
		"x-xsrf-token":  {},
	}
	HiddenResponseHeaders = map[string]struct{}{
		"set-cookie": {},
	}

	// Formatted with http.CanonicalHeaderKey
	RequestIDHeaderKey = "X-Request-Id"
)

Functions ΒΆ

func AddCustomAttributes ΒΆ

func AddCustomAttributes(c *gin.Context, attr slog.Attr)

func GetRequestID ΒΆ

func GetRequestID(c *gin.Context) string

GetRequestID returns the request identifier

func New ΒΆ

func New(logger *slog.Logger) gin.HandlerFunc

New returns a gin.HandlerFunc (middleware) that logs requests using slog.

Requests with errors are logged using slog.Error(). Requests without errors are logged using slog.Info().

func NewWithConfig ΒΆ

func NewWithConfig(logger *slog.Logger, config *Config) gin.HandlerFunc

NewWithConfig returns a gin.HandlerFunc (middleware) that logs requests using slog.

func NewWithFilters ΒΆ

func NewWithFilters(logger *slog.Logger, filters ...Filter) gin.HandlerFunc

NewWithFilters returns a gin.HandlerFunc (middleware) that logs requests using slog.

Requests with errors are logged using slog.Error(). Requests without errors are logged using slog.Info().

Types ΒΆ

type Config ΒΆ

type Config struct {
	DefaultLevel     slog.Level
	ClientErrorLevel slog.Level
	ServerErrorLevel slog.Level

	WithUserAgent      bool
	WithRequestID      bool
	WithRequestBody    bool
	WithRequestHeader  bool
	WithResponseBody   bool
	WithResponseHeader bool
	WithSpanID         bool
	WithTraceID        bool

	Filters []Filter
}

type Filter ΒΆ

type Filter func(ctx *gin.Context) bool

func Accept ΒΆ

func Accept(filter Filter) Filter

Basic

func AcceptHost ΒΆ

func AcceptHost(hosts ...string) Filter

Host

func AcceptHostContains ΒΆ

func AcceptHostContains(parts ...string) Filter

func AcceptHostMatch ΒΆ

func AcceptHostMatch(regs ...regexp.Regexp) Filter

func AcceptHostPrefix ΒΆ

func AcceptHostPrefix(prefixs ...string) Filter

func AcceptHostSuffix ΒΆ

func AcceptHostSuffix(prefixs ...string) Filter

func AcceptMethod ΒΆ

func AcceptMethod(methods ...string) Filter

Method

func AcceptPath ΒΆ

func AcceptPath(urls ...string) Filter

Path

func AcceptPathContains ΒΆ

func AcceptPathContains(parts ...string) Filter

func AcceptPathMatch ΒΆ

func AcceptPathMatch(regs ...regexp.Regexp) Filter

func AcceptPathPrefix ΒΆ

func AcceptPathPrefix(prefixs ...string) Filter

func AcceptPathSuffix ΒΆ

func AcceptPathSuffix(prefixs ...string) Filter

func AcceptStatus ΒΆ

func AcceptStatus(statuses ...int) Filter

Status

func AcceptStatusGreaterThan ΒΆ

func AcceptStatusGreaterThan(status int) Filter

func AcceptStatusGreaterThanOrEqual ΒΆ

func AcceptStatusGreaterThanOrEqual(status int) Filter

func Ignore ΒΆ

func Ignore(filter Filter) Filter

func IgnoreHost ΒΆ

func IgnoreHost(hosts ...string) Filter

func IgnoreHostContains ΒΆ

func IgnoreHostContains(parts ...string) Filter

func IgnoreHostMatch ΒΆ

func IgnoreHostMatch(regs ...regexp.Regexp) Filter

func IgnoreHostPrefix ΒΆ

func IgnoreHostPrefix(prefixs ...string) Filter

func IgnoreHostSuffix ΒΆ

func IgnoreHostSuffix(suffixs ...string) Filter

func IgnoreMethod ΒΆ

func IgnoreMethod(methods ...string) Filter

func IgnorePath ΒΆ

func IgnorePath(urls ...string) Filter

func IgnorePathContains ΒΆ

func IgnorePathContains(parts ...string) Filter

func IgnorePathMatch ΒΆ

func IgnorePathMatch(regs ...regexp.Regexp) Filter

func IgnorePathPrefix ΒΆ

func IgnorePathPrefix(prefixs ...string) Filter

func IgnorePathSuffix ΒΆ

func IgnorePathSuffix(suffixs ...string) Filter

func IgnoreStatus ΒΆ

func IgnoreStatus(statuses ...int) Filter

func IgnoreStatusLessThan ΒΆ

func IgnoreStatusLessThan(status int) Filter

func IgnoreStatusLessThanOrEqual ΒΆ

func IgnoreStatusLessThanOrEqual(status int) Filter

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL