Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
6d5599e3f2 | |||
c04f470209 | |||
ec981ac676 | |||
2dcbf79078 | |||
2b863c0b43 | |||
c2faabe3fd | |||
908422fb76 | |||
b446320fce | |||
95cd0a13f3 | |||
bc41d0d7de | |||
78893a70a2 | |||
38e4720367 | |||
1a4780ee4d | |||
ff3011b6eb | |||
3c7fb65673 | |||
893b1cfd48 | |||
0038d85083 | |||
1433429574 | |||
63537e13dc | |||
aed5b142ec | |||
9e49701dd9 | |||
89b13247b1 | |||
2432b5bcab | |||
bd9657b489 | |||
8f902152b1 | |||
0bbf45fb93 | |||
00d88cf83c | |||
62e35913a0 | |||
225af787c7 | |||
f3cffb5054 | |||
b514b7639c |
36
.drone.yml
36
.drone.yml
|
@ -1,36 +0,0 @@
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: test
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: test
|
|
||||||
image: golang:1.19
|
|
||||||
commands:
|
|
||||||
- go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- pull_request
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: check-go-mod
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: check go.mod
|
|
||||||
image: golang:1.19
|
|
||||||
commands:
|
|
||||||
- go mod tidy
|
|
||||||
- git diff --exit-code go.mod
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- pull_request
|
|
||||||
branch:
|
|
||||||
- master
|
|
124
.golangci.yml
Normal file
124
.golangci.yml
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
run:
|
||||||
|
tests: true
|
||||||
|
timeout: 5m
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- asasalint
|
||||||
|
- asciicheck
|
||||||
|
- bodyclose
|
||||||
|
- bidichk
|
||||||
|
- exportloopref
|
||||||
|
- depguard
|
||||||
|
- errcheck
|
||||||
|
- gocritic
|
||||||
|
- gosec
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- prealloc
|
||||||
|
- staticcheck
|
||||||
|
- typecheck
|
||||||
|
- unconvert
|
||||||
|
- unused
|
||||||
|
- lll
|
||||||
|
- nestif
|
||||||
|
- thelper
|
||||||
|
- nonamedreturns
|
||||||
|
- gocyclo
|
||||||
|
- gomnd
|
||||||
|
- tenv
|
||||||
|
- testpackage
|
||||||
|
- noctx
|
||||||
|
- tparallel
|
||||||
|
- usestdlibvars
|
||||||
|
- unconvert
|
||||||
|
- makezero
|
||||||
|
- grouper
|
||||||
|
- errname
|
||||||
|
- exhaustive
|
||||||
|
- tagliatelle
|
||||||
|
- contextcheck
|
||||||
|
- gocheckcompilerdirectives
|
||||||
|
- errname
|
||||||
|
- forcetypeassert
|
||||||
|
- durationcheck
|
||||||
|
- predeclared
|
||||||
|
- promlinter
|
||||||
|
- wastedassign
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
lll:
|
||||||
|
line-length: 150
|
||||||
|
gocyclo:
|
||||||
|
min-complexity: 10
|
||||||
|
depguard:
|
||||||
|
rules:
|
||||||
|
main:
|
||||||
|
files:
|
||||||
|
- "$all"
|
||||||
|
deny:
|
||||||
|
- pkg: reflect
|
||||||
|
desc: Please don't use reflect package
|
||||||
|
- pkg: github.com/pkg/errors
|
||||||
|
desc: Should be replaced by standard lib errors package
|
||||||
|
govet:
|
||||||
|
enable:
|
||||||
|
- asmdecl
|
||||||
|
- assign
|
||||||
|
- atomic
|
||||||
|
- atomicalign
|
||||||
|
- bools
|
||||||
|
- buildtag
|
||||||
|
- cgocall
|
||||||
|
- composites
|
||||||
|
- copylocks
|
||||||
|
- deepequalerrors
|
||||||
|
- errorsas
|
||||||
|
- findcall
|
||||||
|
- framepointer
|
||||||
|
- httpresponse
|
||||||
|
- ifaceassert
|
||||||
|
- loopclosure
|
||||||
|
- lostcancel
|
||||||
|
- nilfunc
|
||||||
|
- nilness
|
||||||
|
- printf
|
||||||
|
- reflectvaluecompare
|
||||||
|
- shadow
|
||||||
|
- shift
|
||||||
|
- sigchanyzer
|
||||||
|
- sortslice
|
||||||
|
- stdmethods
|
||||||
|
- stringintconv
|
||||||
|
- structtag
|
||||||
|
- testinggoroutine
|
||||||
|
- tests
|
||||||
|
- unmarshal
|
||||||
|
- unreachable
|
||||||
|
- unsafeptr
|
||||||
|
- unusedresult
|
||||||
|
- unusedwrite
|
||||||
|
gomnd:
|
||||||
|
ignored-functions:
|
||||||
|
- strconv.FormatInt
|
||||||
|
- strconv.ParseInt
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
# Exclude some linters from running on tests files.
|
||||||
|
- path: _test\.go
|
||||||
|
linters:
|
||||||
|
- dupl
|
||||||
|
- gocyclo
|
||||||
|
- linters:
|
||||||
|
- lll
|
||||||
|
source: "^//go:generate "
|
||||||
|
- linters:
|
||||||
|
- lll
|
||||||
|
source: "^// @Param"
|
31
.woodpecker/test.yml
Normal file
31
.woodpecker/test.yml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
when:
|
||||||
|
- event: [pull_request]
|
||||||
|
- event: push
|
||||||
|
branch:
|
||||||
|
- ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
|
||||||
|
variables:
|
||||||
|
- &go_image 'golang:1.21'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
test:
|
||||||
|
image: *go_image
|
||||||
|
group: test
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
- go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
|
||||||
|
lint:
|
||||||
|
image: golangci/golangci-lint:v1.58
|
||||||
|
pull: true
|
||||||
|
group: test
|
||||||
|
commands:
|
||||||
|
- golangci-lint run
|
||||||
|
|
||||||
|
check-go-mod:
|
||||||
|
image: *go_image
|
||||||
|
group: test
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
- go mod tidy
|
||||||
|
- git diff --exit-code go.mod
|
85
README.md
85
README.md
|
@ -1,13 +1,86 @@
|
||||||
# chizap [![Build Status](https://drone.dwysokinski.me/api/badges/Kichiyaki/chizap/status.svg?ref=refs/heads/master)](https://drone.dwysokinski.me/Kichiyaki/chizap)
|
# chizap [![Build Status](https://woodpecker.dwysokinski.me/api/badges/4/status.svg)](https://woodpecker.dwysokinski.me/repos/4)
|
||||||
|
|
||||||
Request logging middleware for [go-chi](https://github.com/go-chi/chi) using [zap](https://github.com/uber-go/zap).
|
A request logging middleware for [go-chi](https://github.com/go-chi/chi) using [zap](https://github.com/uber-go/zap).
|
||||||
|
|
||||||
## Installation
|
## Getting started
|
||||||
|
|
||||||
```bash
|
### Installation
|
||||||
|
|
||||||
|
```shell
|
||||||
go get gitea.dwysokinski.me/Kichiyaki/chizap
|
go get gitea.dwysokinski.me/Kichiyaki/chizap
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
### Usage
|
||||||
|
|
||||||
See the [example](./_example/basic/main.go).
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/Kichiyaki/chizap"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
logger, err := zap.NewDevelopment()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("zap.NewDevelopment", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ListenAndServe(":8080", newRouter(logger))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRouter(logger *zap.Logger) *chi.Mux {
|
||||||
|
router := chi.NewRouter()
|
||||||
|
|
||||||
|
router.Route("/", func(r chi.Router) {
|
||||||
|
r.Use(chizap.Logger(
|
||||||
|
logger,
|
||||||
|
chizap.WithFilter(func(r *http.Request) bool {
|
||||||
|
return r.URL.Path != "/excluded"
|
||||||
|
}),
|
||||||
|
chizap.WithFilter(func(r *http.Request) bool {
|
||||||
|
return r.URL.Path != "/excluded2"
|
||||||
|
}),
|
||||||
|
chizap.WithAdditionalFieldExtractor(func(r *http.Request) []zap.Field {
|
||||||
|
if !strings.HasPrefix(r.URL.Path, "/delete") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return []zap.Field{
|
||||||
|
zap.String("id", chi.URLParam(r, "id")),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
r.Get("/info", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
|
r.Get("/warn", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
})
|
||||||
|
r.Get("/error", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
})
|
||||||
|
r.Get("/excluded", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
|
r.Delete("/delete/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Distributed under the MIT License. See ``LICENSE`` for more information.
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
Dawid Wysokiński - [contact@dwysokinski.me](mailto:contact@dwysokinski.me)
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
module gitea.dwysokinski.me/Kichiyaki/chizap/_example/basic
|
|
||||||
|
|
||||||
go 1.18
|
|
||||||
|
|
||||||
replace gitea.dwysokinski.me/Kichiyaki/chizap => ../..
|
|
||||||
|
|
||||||
require (
|
|
||||||
gitea.dwysokinski.me/Kichiyaki/chizap v0.0.0-00010101000000-000000000000
|
|
||||||
github.com/go-chi/chi/v5 v5.0.7
|
|
||||||
go.uber.org/zap v1.23.0
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
|
||||||
go.uber.org/multierr v1.7.0 // indirect
|
|
||||||
)
|
|
|
@ -1,25 +0,0 @@
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
|
||||||
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/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
|
||||||
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|
||||||
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
|
||||||
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
|
|
||||||
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
|
||||||
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
|
|
||||||
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
|
@ -1,68 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.dwysokinski.me/Kichiyaki/chizap"
|
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
logger, err := zap.NewDevelopment()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("zap.NewDevelopment", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
http.ListenAndServe(":8080", newRouter(logger))
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRouter(logger *zap.Logger) *chi.Mux {
|
|
||||||
router := chi.NewRouter()
|
|
||||||
|
|
||||||
router.Route("/", func(r chi.Router) {
|
|
||||||
r.Use(chizap.Logger(
|
|
||||||
logger,
|
|
||||||
chizap.WithFilter(func(r *http.Request) bool {
|
|
||||||
return r.URL.Path != "/excluded"
|
|
||||||
}),
|
|
||||||
chizap.WithFilter(func(r *http.Request) bool {
|
|
||||||
return r.URL.Path != "/excluded2"
|
|
||||||
}),
|
|
||||||
chizap.WithAdditionalFieldExtractor(func(r *http.Request) []zap.Field {
|
|
||||||
if !strings.HasPrefix(r.URL.Path, "/delete") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return []zap.Field{
|
|
||||||
zap.String("id", chi.URLParam(r, "id")),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
r.Get("/info", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
})
|
|
||||||
r.Get("/warn", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
})
|
|
||||||
r.Get("/error", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
})
|
|
||||||
r.Get("/excluded", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
})
|
|
||||||
r.Delete("/delete/{id}", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
router.With(chizap.Logger(logger, chizap.WithTimeFormat(time.RFC1123Z))).
|
|
||||||
Get("/custom-time-format", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
})
|
|
||||||
|
|
||||||
return router
|
|
||||||
}
|
|
28
config.go
28
config.go
|
@ -1,8 +1,8 @@
|
||||||
package chizap
|
package chizap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -13,7 +13,7 @@ type AdditionalFieldExtractor func(r *http.Request) []zap.Field
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
filters []Filter
|
filters []Filter
|
||||||
timeFormat string
|
ipFn func(r *http.Request) string
|
||||||
additionalFieldExtractors []AdditionalFieldExtractor
|
additionalFieldExtractors []AdditionalFieldExtractor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,10 @@ type Option func(*config)
|
||||||
|
|
||||||
func newConfig(opts ...Option) *config {
|
func newConfig(opts ...Option) *config {
|
||||||
cfg := &config{
|
cfg := &config{
|
||||||
timeFormat: time.RFC3339,
|
ipFn: func(r *http.Request) string {
|
||||||
|
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
return ip
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
|
@ -41,18 +44,19 @@ func WithFilter(f Filter) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTimeFormat specifies a time format to use for logging a request time.
|
|
||||||
// time.RFC3339 is used by default.
|
|
||||||
func WithTimeFormat(f string) Option {
|
|
||||||
return func(c *config) {
|
|
||||||
c.timeFormat = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithAdditionalFieldExtractor takes a function that will be called on every
|
// WithAdditionalFieldExtractor takes a function that will be called on every
|
||||||
// request and the returned fields will be added to a log entry.
|
// request and the returned fields will be added to the log entry.
|
||||||
func WithAdditionalFieldExtractor(extractor AdditionalFieldExtractor) Option {
|
func WithAdditionalFieldExtractor(extractor AdditionalFieldExtractor) Option {
|
||||||
return func(c *config) {
|
return func(c *config) {
|
||||||
c.additionalFieldExtractors = append(c.additionalFieldExtractors, extractor)
|
c.additionalFieldExtractors = append(c.additionalFieldExtractors, extractor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithIPFn takes a function that will be called on every
|
||||||
|
// request and the returned ip will be added to the log entry.
|
||||||
|
// http.Request RemoteAddr is logged by default.
|
||||||
|
func WithIPFn(fn func(r *http.Request) string) Option {
|
||||||
|
return func(c *config) {
|
||||||
|
c.ipFn = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
9
go.mod
9
go.mod
|
@ -3,15 +3,14 @@ module gitea.dwysokinski.me/Kichiyaki/chizap
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-chi/chi/v5 v5.0.7
|
github.com/go-chi/chi/v5 v5.0.12
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.9.0
|
||||||
go.uber.org/zap v1.21.0
|
go.uber.org/zap v1.27.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
go.uber.org/multierr v1.7.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
71
go.sum
71
go.sum
|
@ -1,68 +1,17 @@
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
|
||||||
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||||
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
|
||||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
|
||||||
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
|
|
||||||
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
|
||||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
|
||||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
34
logger.go
34
logger.go
|
@ -4,15 +4,13 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger returns a go-chi middleware that logs requests using go.uber.org/zap.
|
// Logger returns a go-chi middleware that logs requests using go.uber.org/zap.
|
||||||
//
|
|
||||||
// Requests with status code >= 500 are logged using logger.Error.
|
|
||||||
// Requests with status code >= 400 are logged using logger.Warn.
|
|
||||||
// Other requests are logged using logger.Info.
|
|
||||||
func Logger(logger *zap.Logger, opts ...Option) func(next http.Handler) http.Handler {
|
func Logger(logger *zap.Logger, opts ...Option) func(next http.Handler) http.Handler {
|
||||||
cfg := newConfig(opts...)
|
cfg := newConfig(opts...)
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
|
@ -37,7 +35,7 @@ func Logger(logger *zap.Logger, opts ...Option) func(next http.Handler) http.Han
|
||||||
fields := []zap.Field{
|
fields := []zap.Field{
|
||||||
zap.Int("statusCode", statusCode),
|
zap.Int("statusCode", statusCode),
|
||||||
zap.Duration("duration", end.Sub(start)),
|
zap.Duration("duration", end.Sub(start)),
|
||||||
zap.String("ip", r.RemoteAddr),
|
zap.String("ip", cfg.ipFn(r)),
|
||||||
zap.String("method", r.Method),
|
zap.String("method", r.Method),
|
||||||
zap.String("query", query),
|
zap.String("query", query),
|
||||||
zap.String("path", path),
|
zap.String("path", path),
|
||||||
|
@ -45,20 +43,26 @@ func Logger(logger *zap.Logger, opts ...Option) func(next http.Handler) http.Han
|
||||||
zap.String("referer", r.Referer()),
|
zap.String("referer", r.Referer()),
|
||||||
zap.Int("contentSize", ww.BytesWritten()),
|
zap.Int("contentSize", ww.BytesWritten()),
|
||||||
zap.String("userAgent", r.UserAgent()),
|
zap.String("userAgent", r.UserAgent()),
|
||||||
zap.String("time", end.Format(cfg.timeFormat)),
|
zap.String("routePattern", chi.RouteContext(r.Context()).RoutePattern()),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fn := range cfg.additionalFieldExtractors {
|
for _, f := range cfg.additionalFieldExtractors {
|
||||||
fields = append(fields, fn(r)...)
|
fields = append(fields, f(r)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if statusCode >= http.StatusInternalServerError {
|
logger.Log(statusCodeToLogLvl(statusCode), path, fields...)
|
||||||
logger.Error(path, fields...)
|
|
||||||
} else if statusCode >= http.StatusBadRequest {
|
|
||||||
logger.Warn(path, fields...)
|
|
||||||
} else {
|
|
||||||
logger.Info(path, fields...)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func statusCodeToLogLvl(statusCode int) zapcore.Level {
|
||||||
|
if statusCode >= http.StatusInternalServerError {
|
||||||
|
return zap.ErrorLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
if statusCode >= http.StatusBadRequest {
|
||||||
|
return zap.WarnLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
return zap.InfoLevel
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package chizap_test
|
package chizap_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.dwysokinski.me/Kichiyaki/chizap"
|
"gitea.dwysokinski.me/Kichiyaki/chizap"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
@ -23,27 +23,31 @@ func TestLogger(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
req *http.Request
|
req *http.Request
|
||||||
excluded bool
|
excluded bool
|
||||||
|
expectedIP string
|
||||||
expectedLevel zapcore.Level
|
expectedLevel zapcore.Level
|
||||||
expectedTimeFormat string
|
expectedRoutePattern string
|
||||||
expectedAdditionalFields []zap.Field
|
expectedAdditionalFields []zap.Field
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "/info?test=true",
|
name: "/info?test=true",
|
||||||
req: httptest.NewRequest(http.MethodGet, "/info?test=true", nil),
|
req: httptest.NewRequest(http.MethodGet, "/info?test=true", nil),
|
||||||
|
expectedIP: "192.0.2.1",
|
||||||
expectedLevel: zap.InfoLevel,
|
expectedLevel: zap.InfoLevel,
|
||||||
expectedTimeFormat: time.RFC3339,
|
expectedRoutePattern: "/info",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "/warn?test=true",
|
name: "/warn?test=true",
|
||||||
req: httptest.NewRequest(http.MethodGet, "/warn?test=true", nil),
|
req: httptest.NewRequest(http.MethodGet, "/warn?test=true", nil),
|
||||||
|
expectedIP: "192.0.2.1",
|
||||||
expectedLevel: zap.WarnLevel,
|
expectedLevel: zap.WarnLevel,
|
||||||
expectedTimeFormat: time.RFC3339,
|
expectedRoutePattern: "/warn",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "/error?test=true",
|
name: "/error?test=true",
|
||||||
req: httptest.NewRequest(http.MethodGet, "/error?test=true", nil),
|
req: httptest.NewRequest(http.MethodGet, "/error?test=true", nil),
|
||||||
|
expectedIP: "192.0.2.1",
|
||||||
expectedLevel: zap.ErrorLevel,
|
expectedLevel: zap.ErrorLevel,
|
||||||
expectedTimeFormat: time.RFC3339,
|
expectedRoutePattern: "/error",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "/excluded?test=true",
|
name: "/excluded?test=true",
|
||||||
|
@ -53,17 +57,23 @@ func TestLogger(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "/delete/123",
|
name: "/delete/123",
|
||||||
req: httptest.NewRequest(http.MethodDelete, "/delete/123", nil),
|
req: httptest.NewRequest(http.MethodDelete, "/delete/123", nil),
|
||||||
|
expectedIP: "192.0.2.1",
|
||||||
expectedLevel: zap.InfoLevel,
|
expectedLevel: zap.InfoLevel,
|
||||||
expectedTimeFormat: time.RFC3339,
|
expectedRoutePattern: "/delete/{id}",
|
||||||
expectedAdditionalFields: []zap.Field{
|
expectedAdditionalFields: []zap.Field{
|
||||||
zap.String("id", "123"),
|
zap.String("id", "123"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "/custom-time-format",
|
name: "/x-forwarded-for",
|
||||||
req: httptest.NewRequest(http.MethodGet, "/custom-time-format", nil),
|
req: func() *http.Request {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/x-forwarded-for", nil)
|
||||||
|
req.Header.Set("X-Forwarded-For", "94.222.111.115")
|
||||||
|
return req
|
||||||
|
}(),
|
||||||
|
expectedIP: "94.222.111.115",
|
||||||
expectedLevel: zap.InfoLevel,
|
expectedLevel: zap.InfoLevel,
|
||||||
expectedTimeFormat: time.RFC1123Z,
|
expectedRoutePattern: "/x-forwarded-for",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +81,8 @@ func TestLogger(t *testing.T) {
|
||||||
tt := tt
|
tt := tt
|
||||||
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
logger, obs := newLogger()
|
logger, obs := newLogger()
|
||||||
|
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
|
@ -88,21 +100,18 @@ func TestLogger(t *testing.T) {
|
||||||
assert.Equal(t, tt.req.URL.Path, entry.Message)
|
assert.Equal(t, tt.req.URL.Path, entry.Message)
|
||||||
assert.Equal(t, tt.expectedLevel, entry.Level)
|
assert.Equal(t, tt.expectedLevel, entry.Level)
|
||||||
require.Len(t, entry.Context, 11+len(tt.expectedAdditionalFields))
|
require.Len(t, entry.Context, 11+len(tt.expectedAdditionalFields))
|
||||||
|
assert.Contains(t, entry.Context, zap.String("ip", tt.expectedIP))
|
||||||
assert.Contains(t, entry.Context, zap.Int("statusCode", rr.Code))
|
assert.Contains(t, entry.Context, zap.Int("statusCode", rr.Code))
|
||||||
assert.Contains(t, entry.Context, zap.String("method", tt.req.Method))
|
assert.Contains(t, entry.Context, zap.String("method", tt.req.Method))
|
||||||
assert.Contains(t, entry.Context, zap.String("path", tt.req.URL.Path))
|
assert.Contains(t, entry.Context, zap.String("path", tt.req.URL.Path))
|
||||||
assert.Contains(t, entry.Context, zap.String("query", tt.req.URL.RawQuery))
|
assert.Contains(t, entry.Context, zap.String("query", tt.req.URL.RawQuery))
|
||||||
|
assert.Contains(t, entry.Context, zap.String("proto", tt.req.Proto))
|
||||||
|
assert.Contains(t, entry.Context, zap.String("referer", tt.req.Referer()))
|
||||||
|
assert.Contains(t, entry.Context, zap.String("userAgent", tt.req.UserAgent()))
|
||||||
|
assert.Contains(t, entry.Context, zap.String("routePattern", tt.expectedRoutePattern))
|
||||||
for _, f := range tt.expectedAdditionalFields {
|
for _, f := range tt.expectedAdditionalFields {
|
||||||
assert.Contains(t, entry.Context, f)
|
assert.Contains(t, entry.Context, f)
|
||||||
}
|
}
|
||||||
for _, f := range entry.Context {
|
|
||||||
if f.Key != "time" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTime(t, tt.expectedTimeFormat, f.String)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,6 +143,13 @@ func newRouter(logger *zap.Logger) *chi.Mux {
|
||||||
zap.String("id", chi.URLParam(r, "id")),
|
zap.String("id", chi.URLParam(r, "id")),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
chizap.WithIPFn(func(r *http.Request) string {
|
||||||
|
if r.URL.Path != "/x-forwarded-for" {
|
||||||
|
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
return r.Header.Get("X-Forwarded-For")
|
||||||
|
}),
|
||||||
))
|
))
|
||||||
r.Get("/info", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/info", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
@ -147,23 +163,13 @@ func newRouter(logger *zap.Logger) *chi.Mux {
|
||||||
r.Get("/excluded", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/excluded", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
})
|
})
|
||||||
|
r.Get("/x-forwarded-for", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
r.Delete("/delete/{id}", func(w http.ResponseWriter, r *http.Request) {
|
r.Delete("/delete/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
router.With(chizap.Logger(logger, chizap.WithTimeFormat(time.RFC1123Z))).
|
|
||||||
Get("/custom-time-format", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
})
|
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertTime(tb testing.TB, format, timeStr string) {
|
|
||||||
tb.Helper()
|
|
||||||
|
|
||||||
end, err := time.Parse(format, timeStr)
|
|
||||||
assert.NoError(tb, err)
|
|
||||||
assert.WithinDuration(tb, end, time.Now(), 1*time.Second)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user