From 4b022114895211500724d93bf45295b1363545a0 Mon Sep 17 00:00:00 2001 From: Lars de Bruijn <9264036+ldebruijn@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:28:33 +0200 Subject: [PATCH] feat(access-logging): Improve access logging module (#91) Co-authored-by: ldebruijn --- .../rules/accesslogging/accesslogging.go | 46 +++++++++++-------- .../rules/accesslogging/accesslogging_test.go | 19 ++++---- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/internal/business/rules/accesslogging/accesslogging.go b/internal/business/rules/accesslogging/accesslogging.go index 41c1a1b..cb3715c 100644 --- a/internal/business/rules/accesslogging/accesslogging.go +++ b/internal/business/rules/accesslogging/accesslogging.go @@ -4,11 +4,10 @@ import ( "github.com/ldebruijn/graphql-protect/internal/business/gql" "log/slog" "net/http" - "slices" ) type Config struct { - Enable bool `config:"default:true" yaml:"enabled"` + Enabled bool `config:"default:true" yaml:"enabled"` IncludedHeaders []string `yaml:"included_headers"` IncludeOperationName bool `config:"default:true" yaml:"include_operation_name"` IncludeVariables bool `config:"default:true" yaml:"include_variables"` @@ -16,44 +15,55 @@ type Config struct { } type AccessLogging struct { - log *slog.Logger - cfg Config + log *slog.Logger + enabled bool + includeHeaders map[string]bool + includeOperationName bool + includeVariables bool + includePayload bool } func NewAccessLogging(cfg Config, log *slog.Logger) *AccessLogging { + headers := map[string]bool{} + for _, header := range cfg.IncludedHeaders { + headers[header] = true + } + return &AccessLogging{ - log: log.WithGroup("access-logging"), - cfg: cfg, + log: log.WithGroup("access-logging"), + enabled: cfg.Enabled, + includeHeaders: headers, + includeOperationName: cfg.IncludeOperationName, + includeVariables: cfg.IncludeVariables, + includePayload: cfg.IncludePayload, } } func (a *AccessLogging) Log(payloads []gql.RequestData, headers http.Header) { - if !a.cfg.Enable { + if !a.enabled { return } toLog := map[string]interface{}{} + logHeaders := map[string]interface{}{} + for key := range a.includeHeaders { + logHeaders[key] = headers.Values(key) + } + for _, req := range payloads { - if a.cfg.IncludeOperationName { + if a.includeOperationName { toLog["operationName"] = req.OperationName } - if a.cfg.IncludeVariables { + if a.includeVariables { toLog["variables"] = req.Variables } - if a.cfg.IncludePayload { + if a.includePayload { toLog["payload"] = req.Query } - logHeaders := map[string]interface{}{} - for name, values := range headers { - if slices.Contains(a.cfg.IncludedHeaders, name) { - logHeaders[name] = values - } - } - toLog["headers"] = logHeaders - a.log.Info("access-logging", "payload", toLog) + a.log.Info("record", "payload", toLog) } } diff --git a/internal/business/rules/accesslogging/accesslogging_test.go b/internal/business/rules/accesslogging/accesslogging_test.go index 38ad828..df8847e 100644 --- a/internal/business/rules/accesslogging/accesslogging_test.go +++ b/internal/business/rules/accesslogging/accesslogging_test.go @@ -44,8 +44,8 @@ func TestAccessLogging_Log(t *testing.T) { name: "logs expected fields when enabled", args: args{ cfg: Config{ - Enable: true, - IncludedHeaders: []string{"Authorization"}, + Enabled: true, + IncludedHeaders: []string{"Authorization", "not-case-sensitive"}, IncludeOperationName: true, IncludeVariables: true, IncludePayload: true, @@ -60,8 +60,9 @@ func TestAccessLogging_Log(t *testing.T) { }, }, headers: map[string][]string{ - "Authorization": {"bearer hello"}, - "Content-Type": {"application/json"}, + "Authorization": {"bearer hello"}, + "Content-Type": {"application/json"}, + "Not-Case-Sensitive": {"yes"}, }, count: 1, }, @@ -76,7 +77,8 @@ func TestAccessLogging_Log(t *testing.T) { "foo": "bar", }, val["variables"]) assert.Equal(t, map[string]interface{}{ - "Authorization": []string{"bearer hello"}, + "Authorization": []string{"bearer hello"}, + "not-case-sensitive": []string{"yes"}, }, val["headers"]) return true @@ -89,7 +91,7 @@ func TestAccessLogging_Log(t *testing.T) { name: "logs nothing when disabled", args: args{ cfg: Config{ - Enable: false, + Enabled: false, IncludedHeaders: []string{"Authorization"}, IncludeOperationName: true, IncludeVariables: true, @@ -121,10 +123,7 @@ func TestAccessLogging_Log(t *testing.T) { handler := &testLogHandler{assert: tt.want} log := slog.New(handler) - a := &AccessLogging{ - log: log, - cfg: tt.args.cfg, - } + a := NewAccessLogging(tt.args.cfg, log) a.Log(tt.args.payloads, tt.args.headers) assert.Equal(t, tt.args.count, a.log.Handler().(*testLogHandler).count)