From 8fc05e396a40df825dbf1ec2024920405220332a Mon Sep 17 00:00:00 2001 From: Samuel Berthe Date: Fri, 15 Sep 2023 04:55:51 +0200 Subject: [PATCH] feat: adding filters --- README.md | 40 ++++++ filters.go | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++ middleware.go | 26 ++++ 3 files changed, 400 insertions(+) create mode 100644 filters.go diff --git a/README.md b/README.md index 9f4fd79..83d7ed5 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,46 @@ router.Run(":1234") // time=2023-04-10T14:00:0.000000Z level=INFO msg="Incoming 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.000Z ``` +### Filters + +```go +import ( + "github.com/gin-gonic/gin" + sloggin "github.com/samber/slog-gin" + "log/slog" +) + +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 ```go diff --git a/filters.go b/filters.go new file mode 100644 index 0000000..65bf69e --- /dev/null +++ b/filters.go @@ -0,0 +1,334 @@ +package sloggin + +import ( + "regexp" + "strings" + + "github.com/gin-gonic/gin" +) + +type Filter func(ctx *gin.Context) bool + +// Basic +func Accept(filter Filter) Filter { return filter } +func Ignore(filter Filter) Filter { return filter } + +// Method +func AcceptMethod(methods ...string) Filter { + return func(c *gin.Context) bool { + reqMethod := strings.ToLower(c.Request.Method) + + for _, method := range methods { + if strings.ToLower(method) == reqMethod { + return true + } + } + + return false + } +} + +func IgnoreMethod(methods ...string) Filter { + return func(c *gin.Context) bool { + reqMethod := strings.ToLower(c.Request.Method) + + for _, method := range methods { + if strings.ToLower(method) == reqMethod { + return false + } + } + + return true + } +} + +// Status +func AcceptStatus(statuses ...int) Filter { + return func(c *gin.Context) bool { + for _, status := range statuses { + if status == c.Writer.Status() { + return true + } + } + + return false + } +} + +func IgnoreStatus(statuses ...int) Filter { + return func(c *gin.Context) bool { + for _, status := range statuses { + if status == c.Writer.Status() { + return false + } + } + + return true + } +} + +func AcceptStatusGreaterThan(status int) Filter { + return func(c *gin.Context) bool { + return c.Writer.Status() > status + } +} + +func IgnoreStatusLessThan(status int) Filter { + return func(c *gin.Context) bool { + return c.Writer.Status() < status + } +} + +func AcceptStatusGreaterThanOrEqual(status int) Filter { + return func(c *gin.Context) bool { + return c.Writer.Status() >= status + } +} + +func IgnoreStatusLessThanOrEqual(status int) Filter { + return func(c *gin.Context) bool { + return c.Writer.Status() <= status + } +} + +// Path +func AcceptPath(urls ...string) Filter { + return func(c *gin.Context) bool { + for _, url := range urls { + if c.Request.URL.Path == url { + return true + } + } + + return false + } +} + +func IgnorePath(urls ...string) Filter { + return func(c *gin.Context) bool { + for _, url := range urls { + if c.Request.URL.Path == url { + return false + } + } + + return true + } +} + +func AcceptPathContains(parts ...string) Filter { + return func(c *gin.Context) bool { + for _, part := range parts { + if strings.Contains(c.Request.URL.Path, part) { + return true + } + } + + return false + } +} + +func IgnorePathContains(parts ...string) Filter { + return func(c *gin.Context) bool { + for _, part := range parts { + if strings.Contains(c.Request.URL.Path, part) { + return false + } + } + + return true + } +} + +func AcceptPathPrefix(prefixs ...string) Filter { + return func(c *gin.Context) bool { + for _, prefix := range prefixs { + if strings.HasPrefix(c.Request.URL.Path, prefix) { + return true + } + } + + return false + } +} + +func IgnorePathPrefix(prefixs ...string) Filter { + return func(c *gin.Context) bool { + for _, prefix := range prefixs { + if strings.HasPrefix(c.Request.URL.Path, prefix) { + return false + } + } + + return true + } +} + +func AcceptPathSuffix(prefixs ...string) Filter { + return func(c *gin.Context) bool { + for _, prefix := range prefixs { + if strings.HasPrefix(c.Request.URL.Path, prefix) { + return true + } + } + + return false + } +} + +func IgnorePathSuffix(suffixs ...string) Filter { + return func(c *gin.Context) bool { + for _, suffix := range suffixs { + if strings.HasSuffix(c.Request.URL.Path, suffix) { + return false + } + } + + return true + } +} + +func AcceptPathMatch(regs ...regexp.Regexp) Filter { + return func(c *gin.Context) bool { + for _, reg := range regs { + if reg.Match([]byte(c.Request.URL.Path)) { + return true + } + } + + return false + } +} + +func IgnorePathMatch(regs ...regexp.Regexp) Filter { + return func(c *gin.Context) bool { + for _, reg := range regs { + if reg.Match([]byte(c.Request.URL.Path)) { + return false + } + } + + return true + } +} + +// Host +func AcceptHost(hosts ...string) Filter { + return func(c *gin.Context) bool { + for _, host := range hosts { + if c.Request.URL.Host == host { + return true + } + } + + return false + } +} + +func IgnoreHost(hosts ...string) Filter { + return func(c *gin.Context) bool { + for _, host := range hosts { + if c.Request.URL.Host == host { + return false + } + } + + return true + } +} + +func AcceptHostContains(parts ...string) Filter { + return func(c *gin.Context) bool { + for _, part := range parts { + if strings.Contains(c.Request.URL.Host, part) { + return true + } + } + + return false + } +} + +func IgnoreHostContains(parts ...string) Filter { + return func(c *gin.Context) bool { + for _, part := range parts { + if strings.Contains(c.Request.URL.Host, part) { + return false + } + } + + return true + } +} + +func AcceptHostPrefix(prefixs ...string) Filter { + return func(c *gin.Context) bool { + for _, prefix := range prefixs { + if strings.HasPrefix(c.Request.URL.Host, prefix) { + return true + } + } + + return false + } +} + +func IgnoreHostPrefix(prefixs ...string) Filter { + return func(c *gin.Context) bool { + for _, prefix := range prefixs { + if strings.HasPrefix(c.Request.URL.Host, prefix) { + return false + } + } + + return true + } +} + +func AcceptHostSuffix(prefixs ...string) Filter { + return func(c *gin.Context) bool { + for _, prefix := range prefixs { + if strings.HasPrefix(c.Request.URL.Host, prefix) { + return true + } + } + + return false + } +} + +func IgnoreHostSuffix(suffixs ...string) Filter { + return func(c *gin.Context) bool { + for _, suffix := range suffixs { + if strings.HasSuffix(c.Request.URL.Host, suffix) { + return false + } + } + + return true + } +} + +func AcceptHostMatch(regs ...regexp.Regexp) Filter { + return func(c *gin.Context) bool { + for _, reg := range regs { + if reg.Match([]byte(c.Request.URL.Host)) { + return true + } + } + + return false + } +} + +func IgnoreHostMatch(regs ...regexp.Regexp) Filter { + return func(c *gin.Context) bool { + for _, reg := range regs { + if reg.Match([]byte(c.Request.URL.Host)) { + return false + } + } + + return true + } +} diff --git a/middleware.go b/middleware.go index 014f960..1cfadc5 100644 --- a/middleware.go +++ b/middleware.go @@ -19,6 +19,8 @@ type Config struct { ServerErrorLevel slog.Level WithRequestID bool + + Filters []Filter } // New returns a gin.HandlerFunc (middleware) that logs requests using slog. @@ -32,6 +34,24 @@ func New(logger *slog.Logger) gin.HandlerFunc { ServerErrorLevel: slog.LevelError, WithRequestID: true, + + Filters: []Filter{}, + }) +} + +// 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(). +func NewWithFilters(logger *slog.Logger, filters ...Filter) gin.HandlerFunc { + return NewWithConfig(logger, Config{ + DefaultLevel: slog.LevelInfo, + ClientErrorLevel: slog.LevelWarn, + ServerErrorLevel: slog.LevelError, + + WithRequestID: true, + + Filters: filters, }) } @@ -66,6 +86,12 @@ func NewWithConfig(logger *slog.Logger, config Config) gin.HandlerFunc { attributes = append(attributes, slog.String("request-id", requestID)) } + for _, filter := range config.Filters { + if !filter(c) { + return + } + } + switch { case c.Writer.Status() >= http.StatusBadRequest && c.Writer.Status() < http.StatusInternalServerError: logger.LogAttrs(context.Background(), config.ClientErrorLevel, c.Errors.String(), attributes...)