initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
github: [samber]
|
||||
@@ -0,0 +1,44 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
branches:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.20.1
|
||||
stable: false
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
||||
version: latest
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
working-directory: ./
|
||||
|
||||
# Optional: golangci-lint command line arguments.
|
||||
args: --timeout 60s --max-same-issues 50
|
||||
|
||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||
# only-new-issues: true
|
||||
|
||||
# Optional: if set to true then the action will use pre-installed Go.
|
||||
# skip-go-installation: true
|
||||
|
||||
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
|
||||
# skip-pkg-cache: true
|
||||
|
||||
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
|
||||
# skip-build-cache: true
|
||||
|
||||
# optionally use a specific version of Go rather than the latest one
|
||||
go_version: '1.20.1'
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
semver:
|
||||
type: string
|
||||
description: 'Semver (eg: v1.2.3)'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: github.triggering_actor == 'samber'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.20.1
|
||||
stable: false
|
||||
|
||||
- name: Test
|
||||
run: make test
|
||||
|
||||
# remove tests in order to clean dependencies
|
||||
- name: Remove xxx_test.go files
|
||||
run: rm -rf *_test.go ./examples ./images
|
||||
|
||||
# cleanup test dependencies
|
||||
- name: Cleanup dependencies
|
||||
run: go mod tidy
|
||||
|
||||
- name: List files
|
||||
run: tree -Cfi
|
||||
- name: Write new go.mod into logs
|
||||
run: cat go.mod
|
||||
- name: Write new go.sum into logs
|
||||
run: cat go.sum
|
||||
|
||||
- name: Create tag
|
||||
run: |
|
||||
git config --global user.name '${{ github.triggering_actor }}'
|
||||
git config --global user.email "${{ github.triggering_actor}}@users.noreply.github.com"
|
||||
|
||||
git add .
|
||||
git commit --allow-empty -m 'bump ${{ inputs.semver }}'
|
||||
git tag ${{ inputs.semver }}
|
||||
git push origin ${{ inputs.semver }}
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
name: ${{ inputs.semver }}
|
||||
tag_name: ${{ inputs.semver }}
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
branches:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.20.1
|
||||
stable: false
|
||||
|
||||
- name: Build
|
||||
run: make build
|
||||
|
||||
- name: Test
|
||||
run: make test
|
||||
|
||||
- name: Test
|
||||
run: make coverage
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: ./cover.out
|
||||
flags: unittests
|
||||
verbose: true
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/go
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=go
|
||||
|
||||
### Go ###
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
### Go Patch ###
|
||||
/vendor/
|
||||
/Godeps/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/go
|
||||
|
||||
cover.out
|
||||
cover.html
|
||||
.vscode
|
||||
|
||||
.idea/
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Samuel Berthe
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
BIN=go
|
||||
|
||||
build:
|
||||
${BIN} build -v ./...
|
||||
|
||||
test:
|
||||
go test -race -v ./...
|
||||
watch-test:
|
||||
reflex -t 50ms -s -- sh -c 'gotest -race -v ./...'
|
||||
|
||||
bench:
|
||||
go test -benchmem -count 3 -bench ./...
|
||||
watch-bench:
|
||||
reflex -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...'
|
||||
|
||||
coverage:
|
||||
${BIN} test -v -coverprofile=cover.out -covermode=atomic .
|
||||
${BIN} tool cover -html=cover.out -o cover.html
|
||||
|
||||
tools:
|
||||
${BIN} install github.com/cespare/reflex@latest
|
||||
${BIN} install github.com/rakyll/gotest@latest
|
||||
${BIN} install github.com/psampaz/go-mod-outdated@latest
|
||||
${BIN} install github.com/jondot/goweight@latest
|
||||
${BIN} install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
${BIN} get -t -u golang.org/x/tools/cmd/cover
|
||||
${BIN} install github.com/sonatype-nexus-community/nancy@latest
|
||||
go mod tidy
|
||||
|
||||
lint:
|
||||
golangci-lint run --timeout 60s --max-same-issues 50 ./...
|
||||
lint-fix:
|
||||
golangci-lint run --timeout 60s --max-same-issues 50 --fix ./...
|
||||
|
||||
audit: tools
|
||||
${BIN} list -json -m all | nancy sleuth
|
||||
|
||||
outdated: tools
|
||||
${BIN} list -u -m -json all | go-mod-outdated -update -direct
|
||||
|
||||
weight: tools
|
||||
goweight
|
||||
@@ -0,0 +1,287 @@
|
||||
|
||||
# slog: Gin middleware
|
||||
|
||||
[](https://github.com/samber/slog-gin/releases)
|
||||

|
||||
[](https://pkg.go.dev/github.com/samber/slog-gin)
|
||||

|
||||
[](https://goreportcard.com/report/github.com/samber/slog-gin)
|
||||
[](https://codecov.io/gh/samber/slog-gin)
|
||||
[](https://github.com/samber/slog-gin/graphs/contributors)
|
||||
[](./LICENSE)
|
||||
|
||||
Gin middleware to log http requests using [slog](https://pkg.go.dev/golang.org/x/exp/slog).
|
||||
|
||||
**See also:**
|
||||
|
||||
- [slog-multi](https://github.com/samber/slog-multi): workflows of `slog` handlers (pipeline, fanout, ...)
|
||||
- [slog-formatter](https://github.com/samber/slog-formatter): `slog` attribute formatting
|
||||
- [slog-datadog](https://github.com/samber/slog-datadog): A `slog` handler for `Datadog`
|
||||
- [slog-logstash](https://github.com/samber/slog-logstash): A `slog` handler for `Logstash`
|
||||
- [slog-slack](https://github.com/samber/slog-slack): A `slog` handler for `Slack`
|
||||
- [slog-loki](https://github.com/samber/slog-loki): A `slog` handler for `Loki`
|
||||
- [slog-sentry](https://github.com/samber/slog-sentry): A `slog` handler for `Sentry`
|
||||
- [slog-fluentd](https://github.com/samber/slog-fluentd): A `slog` handler for `Fluentd`
|
||||
- [slog-syslog](https://github.com/samber/slog-syslog): A `slog` handler for `Syslog`
|
||||
- [slog-graylog](https://github.com/samber/slog-graylog): A `slog` handler for `Graylog`
|
||||
|
||||
## 🚀 Install
|
||||
|
||||
```sh
|
||||
go get github.com/samber/slog-gin
|
||||
```
|
||||
|
||||
**Compatibility**: go >= 1.20.1
|
||||
|
||||
This library is v0 and follows SemVer strictly. On `slog` final release (go 1.21), this library will go v1.
|
||||
|
||||
No breaking changes will be made to exported APIs before v1.0.0.
|
||||
|
||||
## 💡 Usage
|
||||
|
||||
### Minimal
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
sloggin "github.com/samber/slog-gin"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// Create a slog logger, which:
|
||||
// - Logs to stdout.
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout))
|
||||
|
||||
router := gin.New()
|
||||
|
||||
// Add the sloggin middleware to all routes.
|
||||
// The middleware will log all requests attributes.
|
||||
router.Use(sloggin.New(logger))
|
||||
|
||||
// Example pong request.
|
||||
router.GET("/pong", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "pong")
|
||||
})
|
||||
|
||||
router.Run(":1234")
|
||||
|
||||
// output:
|
||||
// time=2023-04-10T14:00:0.000000+02:00 level=INFO msg="HTTP Request" status=200 method=GET path=/pong ip=127.0.0.1 latency=25.5µs user-agent=curl/7.77.0 time=2023-04-10T14:00:00.000+02:00
|
||||
```
|
||||
|
||||
### Using custom time formatters
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
sloggin "github.com/samber/slog-gin"
|
||||
"golang.org/x/exp/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),
|
||||
),
|
||||
)
|
||||
|
||||
router := gin.New()
|
||||
|
||||
// Add the sloggin middleware to all routes.
|
||||
// The middleware will log all requests attributes.
|
||||
router.Use(sloggin.New(logger))
|
||||
|
||||
// Example pong request.
|
||||
router.GET("/pong", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "pong")
|
||||
})
|
||||
|
||||
router.Run(":1234")
|
||||
|
||||
// output:
|
||||
// time="2023-04-10 14:00:00" level=INFO msg="HTTP Request" status=200 method=GET path=/pong ip=127.0.0.1 latency=25.5µs user-agent=curl/7.77.0 time="2023-04-10 14:00:00"
|
||||
```
|
||||
|
||||
### Using custom logger sub-group
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
sloggin "github.com/samber/slog-gin"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// Create a slog logger, which:
|
||||
// - Logs to stdout.
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout))
|
||||
|
||||
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")))
|
||||
|
||||
// Example pong request.
|
||||
router.GET("/pong", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "pong")
|
||||
})
|
||||
|
||||
router.Run(":1234")
|
||||
|
||||
// output:
|
||||
// time=2023-04-10T14:00:0.000000+02:00 level=INFO msg="HTTP Request" http.status=200 http.method=GET http.path=/pong http.ip=127.0.0.1 http.latency=20.125µs http.user-agent=curl/7.77.0 time=2023-04-10T14:00:00.000+02:00
|
||||
```
|
||||
|
||||
### Add logger to a single route
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
sloggin "github.com/samber/slog-gin"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// Create a slog logger, which:
|
||||
// - Logs to stdout.
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout))
|
||||
|
||||
router := gin.New()
|
||||
|
||||
// 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")
|
||||
|
||||
// output:
|
||||
// time="2023-04-10 14:00:00" level=INFO msg="HTTP Request" status=200 method=GET path=/pong ip=127.0.0.1 latency=25.5µs user-agent=curl/7.77.0 time="2023-04-10 14:00:00"
|
||||
```
|
||||
|
||||
### Adding custom attributes
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
sloggin "github.com/samber/slog-gin"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// Create a slog logger, which:
|
||||
// - Logs to stdout.
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout)).
|
||||
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))
|
||||
|
||||
// Example pong request.
|
||||
router.GET("/pong", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "pong")
|
||||
})
|
||||
|
||||
router.Run(":1234")
|
||||
|
||||
// output:
|
||||
// time=2023-04-10T14:00:0.000000+02:00 level=INFO msg="HTTP Request" environment=production server=gin/1.9.0 gin_mode=release server_start_time=2023-04-10T10:00:00.000+02:00 status=200 method=GET path=/pong ip=127.0.0.1 latency=25.5µs user-agent=curl/7.77.0 time=2023-04-10T14:00:00.000+02:00
|
||||
```
|
||||
|
||||
### JSON output
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
sloggin "github.com/samber/slog-gin"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// Create a slog logger, which:
|
||||
// - Logs to stdout.
|
||||
logger := slog.New(slog.NewJSONHandler(os.Stdout))
|
||||
|
||||
router := gin.New()
|
||||
|
||||
// Add the sloggin middleware to all routes.
|
||||
// The middleware will log all requests attributes.
|
||||
router.Use(sloggin.New(logger))
|
||||
|
||||
// Example pong request.
|
||||
router.GET("/pong", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "pong")
|
||||
})
|
||||
|
||||
router.Run(":1234")
|
||||
|
||||
// output:
|
||||
// {"time":"2023-04-10T14:00:0.000000+02:00","level":"INFO","msg":"HTTP Request","gin_mode":"GIN_MODE","status":200,"method":"GET","path":"/pong","ip":"127.0.0.1","latency":15542,"user-agent":"curl/7.77.0","time":"2023-04-10T14:00:0.000000+02:00"}
|
||||
```
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
- Ping me on twitter [@samuelberthe](https://twitter.com/samuelberthe) (DMs, mentions, whatever :))
|
||||
- Fork the [project](https://github.com/samber/slog-gin)
|
||||
- Fix [open issues](https://github.com/samber/slog-gin/issues) or request new features
|
||||
|
||||
Don't hesitate ;)
|
||||
|
||||
```bash
|
||||
# Install some dev dependencies
|
||||
make tools
|
||||
|
||||
# Run tests
|
||||
make test
|
||||
# or
|
||||
make watch-test
|
||||
```
|
||||
|
||||
## 👤 Contributors
|
||||
|
||||

|
||||
|
||||
## 💫 Show your support
|
||||
|
||||
Give a ⭐️ if this project helped you!
|
||||
|
||||
[](https://github.com/sponsors/samber)
|
||||
|
||||
## 📝 License
|
||||
|
||||
Copyright © 2023 [Samuel Berthe](https://github.com/samber).
|
||||
|
||||
This project is [MIT](./LICENSE) licensed.
|
||||
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
slogformatter "github.com/samber/slog-formatter"
|
||||
sloggin "github.com/samber/slog-gin"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 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.RFC3339, nil),
|
||||
)(
|
||||
slog.NewJSONHandler(os.Stdout),
|
||||
),
|
||||
)
|
||||
|
||||
// Add an attribute to all log entries made through this logger.
|
||||
logger = logger.With("gin_mode", gin.EnvGinMode)
|
||||
|
||||
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")))
|
||||
|
||||
// Example pong request.
|
||||
router.GET("/pong", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "pong")
|
||||
})
|
||||
|
||||
logger.Info("Starting server")
|
||||
if err := router.Run(":1234"); err != nil {
|
||||
logger.Error("can' start server with 1234 port")
|
||||
}
|
||||
|
||||
// output:
|
||||
// time=2023-04-10T14:00:0.000000+00:00 level=ERROR msg="HTTP Request" gin_mode=GIN_MODE http.status=200 http.method=GET http.path=/pong http.ip=127.0.0.1 http.latency=25.5µs http.user-agent=curl/7.77.0 http.time=2023-04-10T14:00:00.000+00:00
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
module github.com/samber/slog-gin
|
||||
|
||||
go 1.20
|
||||
|
||||
require golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.8.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.2 // indirect
|
||||
github.com/goccy/go-json v0.10.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/samber/lo v1.38.1 // indirect
|
||||
github.com/samber/slog-multi v0.4.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/crypto v0.5.0 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.0
|
||||
github.com/samber/slog-formatter v0.3.2
|
||||
)
|
||||
@@ -0,0 +1,88 @@
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
|
||||
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
|
||||
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
|
||||
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
|
||||
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
||||
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
|
||||
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
github.com/samber/slog-formatter v0.3.2 h1:yOxgpBmiLyMYqx8txnwOekfPEHR3yZ9JtHSc9Ef+lQY=
|
||||
github.com/samber/slog-formatter v0.3.2/go.mod h1:YKAVaCNPbyEaGpRWrbhkGUlu7n2J+4P54XAER8TxzVU=
|
||||
github.com/samber/slog-multi v0.4.0 h1:QTQAo+9AP295irccqKdNwJ/2XflRMuL/aHqk7RblOhE=
|
||||
github.com/samber/slog-multi v0.4.0/go.mod h1:QDicB1R5oTcbSSqlYmskphC7fNcjHgdxqZdns1eAvDc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU=
|
||||
github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
@@ -0,0 +1,59 @@
|
||||
package sloggin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DefaultLevel slog.Level
|
||||
ClientErrorLevel slog.Level
|
||||
ServerErrorLevel slog.Level
|
||||
}
|
||||
|
||||
// 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 New(logger *slog.Logger) gin.HandlerFunc {
|
||||
return NewWithConfig(logger, Config{
|
||||
DefaultLevel: slog.LevelInfo,
|
||||
ClientErrorLevel: slog.LevelWarn,
|
||||
ServerErrorLevel: slog.LevelError,
|
||||
})
|
||||
}
|
||||
|
||||
func NewWithConfig(logger *slog.Logger, config Config) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
start := time.Now()
|
||||
path := c.Request.URL.Path
|
||||
|
||||
c.Next()
|
||||
|
||||
end := time.Now()
|
||||
latency := end.Sub(start)
|
||||
|
||||
attributes := []slog.Attr{
|
||||
slog.Int("status", c.Writer.Status()),
|
||||
slog.String("method", c.Request.Method),
|
||||
slog.String("path", path),
|
||||
slog.String("ip", c.ClientIP()),
|
||||
slog.Duration("latency", latency),
|
||||
slog.String("user-agent", c.Request.UserAgent()),
|
||||
slog.Time("time", end),
|
||||
}
|
||||
|
||||
switch {
|
||||
case c.Writer.Status() >= http.StatusBadRequest && c.Writer.Status() < http.StatusInternalServerError:
|
||||
logger.LogAttrs(context.Background(), config.ClientErrorLevel, c.Errors.String(), attributes...)
|
||||
case c.Writer.Status() >= http.StatusInternalServerError:
|
||||
logger.LogAttrs(context.Background(), config.ServerErrorLevel, c.Errors.String(), attributes...)
|
||||
default:
|
||||
logger.LogAttrs(context.Background(), config.DefaultLevel, "HTTP Request", attributes...)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user