From 575d3a02e5dd95a9da8000add142c61174066537 Mon Sep 17 00:00:00 2001 From: maxlerebourg Date: Sun, 11 Feb 2024 11:52:47 +0100 Subject: [PATCH] :sparkles: Add a logger instance to bouncer instance (#134) * :sparkles: Add a logger instance to bouncer instance * :bento: fix test * :bento: fix lint * :bento: fix lint * :bento: fix lint * :bento: fix lint * :bento: fix lint * :bento: fix test * :bento: fix test * :bento: fix lint + test * :bento: fix test * :bento: fix test * :bento: fix test * :bento: fix lint * :bento: fix lint --- bouncer.go | 61 +++++++++++++------------ pkg/cache/cache.go | 42 +++++++++-------- pkg/cache/cache_test.go | 8 ++-- pkg/configuration/configuration.go | 25 +++++----- pkg/configuration/configuration_test.go | 6 ++- pkg/ip/ip.go | 8 ++-- pkg/logger/logger.go | 41 ++++++++++------- 7 files changed, 106 insertions(+), 85 deletions(-) diff --git a/bouncer.go b/bouncer.go index e50b144..beaa1ff 100644 --- a/bouncer.go +++ b/bouncer.go @@ -75,19 +75,20 @@ type Bouncer struct { serverPoolStrategy *ip.PoolStrategy httpClient *http.Client cacheClient *cache.Client + log *logger.Log } // New creates the crowdsec bouncer plugin. func New(ctx context.Context, next http.Handler, config *configuration.Config, name string) (http.Handler, error) { - logger.Init(config.LogLevel) + log := logger.New(config.LogLevel) err := configuration.ValidateParams(config) if err != nil { - logger.Error(fmt.Sprintf("New:validateParams %s", err.Error())) + log.Error(fmt.Sprintf("New:validateParams %s", err.Error())) return nil, err } - serverChecker, _ := ip.NewChecker(config.ForwardedHeadersTrustedIPs) - clientChecker, _ := ip.NewChecker(config.ClientTrustedIPs) + serverChecker, _ := ip.NewChecker(log, config.ForwardedHeadersTrustedIPs) + clientChecker, _ := ip.NewChecker(log, config.ClientTrustedIPs) var tlsConfig *tls.Config crowdsecStreamRoute := "" @@ -103,14 +104,14 @@ func New(ctx context.Context, next http.Handler, config *configuration.Config, n } else { crowdsecStreamRoute = crowdsecLapiStreamRoute crowdsecHeader = crowdsecLapiHeader - tlsConfig, err = configuration.GetTLSConfigCrowdsec(config) + tlsConfig, err = configuration.GetTLSConfigCrowdsec(config, log) if err != nil { - logger.Error(fmt.Sprintf("New:getTLSConfigCrowdsec fail to get tlsConfig %s", err.Error())) + log.Error(fmt.Sprintf("New:getTLSConfigCrowdsec fail to get tlsConfig %s", err.Error())) return nil, err } apiKey, errAPIKey := configuration.GetVariable(config, "CrowdsecLapiKey") if errAPIKey != nil && len(tlsConfig.Certificates) == 0 { - logger.Error(fmt.Sprintf("New:crowdsecLapiKey fail to get CrowdsecLapiKey and no client certificate setup %s", errAPIKey.Error())) + log.Error(fmt.Sprintf("New:crowdsecLapiKey fail to get CrowdsecLapiKey and no client certificate setup %s", errAPIKey.Error())) return nil, err } config.CrowdsecLapiKey = apiKey @@ -137,6 +138,7 @@ func New(ctx context.Context, next http.Handler, config *configuration.Config, n defaultDecisionTimeout: config.DefaultDecisionSeconds, crowdsecStreamRoute: crowdsecStreamRoute, crowdsecHeader: crowdsecHeader, + log: log, serverPoolStrategy: &ip.PoolStrategy{ Checker: serverChecker, }, @@ -157,7 +159,8 @@ func New(ctx context.Context, next http.Handler, config *configuration.Config, n return bouncer, nil } config.RedisCachePassword, _ = configuration.GetVariable(config, "RedisCachePassword") - bouncer.cacheClient.New( + bouncer.cacheClient.Init( + log, config.RedisCacheEnabled, config.RedisCacheHost, config.RedisCachePassword, @@ -167,17 +170,17 @@ func New(ctx context.Context, next http.Handler, config *configuration.Config, n if (config.CrowdsecMode == configuration.StreamMode || config.CrowdsecMode == configuration.AloneMode) && ticker == nil { if config.CrowdsecMode == configuration.AloneMode { if err := getToken(bouncer); err != nil { - logger.Error(fmt.Sprintf("New:getToken %s", err.Error())) + bouncer.log.Error(fmt.Sprintf("New:getToken %s", err.Error())) return nil, err } } handleStreamTicker(bouncer) isStartup = false - ticker = startTicker(config, func() { + ticker = startTicker(config, log, func() { handleStreamTicker(bouncer) }) } - logger.Debug(fmt.Sprintf("New initialized mode:%s", config.CrowdsecMode)) + bouncer.log.Debug(fmt.Sprintf("New initialized mode:%s", config.CrowdsecMode)) return bouncer, nil } @@ -194,18 +197,18 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // Here we check for the trusted IPs in the customHeader remoteIP, err := ip.GetRemoteIP(req, bouncer.serverPoolStrategy, bouncer.customHeader) if err != nil { - logger.Error(fmt.Sprintf("ServeHTTP:getRemoteIp ip:%s %s", remoteIP, err.Error())) + bouncer.log.Error(fmt.Sprintf("ServeHTTP:getRemoteIp ip:%s %s", remoteIP, err.Error())) rw.WriteHeader(http.StatusForbidden) return } isTrusted, err := bouncer.clientPoolStrategy.Checker.Contains(remoteIP) if err != nil { - logger.Error(fmt.Sprintf("ServeHTTP:checkerContains ip:%s %s", remoteIP, err.Error())) + bouncer.log.Error(fmt.Sprintf("ServeHTTP:checkerContains ip:%s %s", remoteIP, err.Error())) rw.WriteHeader(http.StatusForbidden) return } // if our IP is in the trusted list we bypass the next checks - logger.Debug(fmt.Sprintf("ServeHTTP ip:%s isTrusted:%v", remoteIP, isTrusted)) + bouncer.log.Debug(fmt.Sprintf("ServeHTTP ip:%s isTrusted:%v", remoteIP, isTrusted)) if isTrusted { bouncer.next.ServeHTTP(rw, req) return @@ -221,14 +224,14 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { isBanned, cacheErr := bouncer.cacheClient.GetDecision(remoteIP) if cacheErr != nil { errString := cacheErr.Error() - logger.Debug(fmt.Sprintf("ServeHTTP:getDecision ip:%s isBanned:false %s", remoteIP, errString)) + bouncer.log.Debug(fmt.Sprintf("ServeHTTP:getDecision ip:%s isBanned:false %s", remoteIP, errString)) if errString != cache.CacheMiss { - logger.Error(fmt.Sprintf("ServeHTTP:getDecision ip:%s %s", remoteIP, errString)) + bouncer.log.Error(fmt.Sprintf("ServeHTTP:getDecision ip:%s %s", remoteIP, errString)) rw.WriteHeader(http.StatusForbidden) return } } else { - logger.Debug(fmt.Sprintf("ServeHTTP ip:%s cache:hit isBanned:%v", remoteIP, isBanned)) + bouncer.log.Debug(fmt.Sprintf("ServeHTTP ip:%s cache:hit isBanned:%v", remoteIP, isBanned)) if isBanned { rw.WriteHeader(http.StatusForbidden) } else { @@ -243,13 +246,13 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if isCrowdsecStreamHealthy { handleNextServeHTTP(bouncer, remoteIP, rw, req) } else { - logger.Debug(fmt.Sprintf("ServeHTTP isCrowdsecStreamHealthy:false ip:%s", remoteIP)) + bouncer.log.Debug(fmt.Sprintf("ServeHTTP isCrowdsecStreamHealthy:false ip:%s", remoteIP)) rw.WriteHeader(http.StatusForbidden) } } else { err = handleNoStreamCache(bouncer, remoteIP) if err != nil { - logger.Debug(fmt.Sprintf("ServeHTTP:handleNoStreamCache ip:%s isBanned:true %s", remoteIP, err.Error())) + bouncer.log.Debug(fmt.Sprintf("ServeHTTP:handleNoStreamCache ip:%s isBanned:true %s", remoteIP, err.Error())) rw.WriteHeader(http.StatusForbidden) } else { handleNextServeHTTP(bouncer, remoteIP, rw, req) @@ -289,7 +292,7 @@ func handleNextServeHTTP(bouncer *Bouncer, remoteIP string, rw http.ResponseWrit if bouncer.appsecEnabled { err := appsecQuery(bouncer, remoteIP, req) if err != nil { - logger.Debug(fmt.Sprintf("handleNextServeHTTP ip:%s isWaf:true %s", remoteIP, err.Error())) + bouncer.log.Debug(fmt.Sprintf("handleNextServeHTTP ip:%s isWaf:true %s", remoteIP, err.Error())) rw.WriteHeader(http.StatusForbidden) return } @@ -300,17 +303,17 @@ func handleNextServeHTTP(bouncer *Bouncer, remoteIP string, rw http.ResponseWrit func handleStreamTicker(bouncer *Bouncer) { if err := handleStreamCache(bouncer); err != nil { isCrowdsecStreamHealthy = false - logger.Error(err.Error()) + bouncer.log.Error(err.Error()) } else { isCrowdsecStreamHealthy = true } } -func startTicker(config *configuration.Config, work func()) chan bool { +func startTicker(config *configuration.Config, log *logger.Log, work func()) chan bool { ticker := time.NewTicker(time.Duration(config.UpdateIntervalSeconds) * time.Second) stop := make(chan bool, 1) go func() { - defer logger.Debug("ticker:stopped") + defer log.Debug("ticker:stopped") for { select { case <-ticker.C: @@ -387,7 +390,7 @@ func getToken(bouncer *Bouncer) error { } if login.Code == 200 && len(login.Token) > 0 { bouncer.crowdsecKey = login.Token - logger.Debug(fmt.Sprintf("getToken statusCode:%d", login.Code)) + bouncer.log.Debug(fmt.Sprintf("getToken statusCode:%d", login.Code)) return nil } return fmt.Errorf("getToken statusCode:%d", login.Code) @@ -400,7 +403,7 @@ func handleStreamCache(bouncer *Bouncer) error { // because updated routine information is in the cache _, err := bouncer.cacheClient.GetDecision(cacheTimeoutKey) if err == nil { - logger.Debug("handleStreamCache:alreadyUpdated") + bouncer.log.Debug("handleStreamCache:alreadyUpdated") return nil } if err.Error() != cache.CacheMiss { @@ -431,7 +434,7 @@ func handleStreamCache(bouncer *Bouncer) error { for _, decision := range stream.Deleted { bouncer.cacheClient.DeleteDecision(decision.Value) } - logger.Debug("handleStreamCache:updated") + bouncer.log.Debug("handleStreamCache:updated") isCrowdsecStreamHealthy = true return nil } @@ -456,7 +459,7 @@ func crowdsecQuery(bouncer *Bouncer, stringURL string, isPost bool) ([]byte, err } defer func() { if err = res.Body.Close(); err != nil { - logger.Error(fmt.Sprintf("crowdsecQuery:closeBody %s", err.Error())) + bouncer.log.Error(fmt.Sprintf("crowdsecQuery:closeBody %s", err.Error())) } }() if res.StatusCode == http.StatusUnauthorized && bouncer.crowdsecMode == configuration.AloneMode { @@ -511,11 +514,11 @@ func appsecQuery(bouncer *Bouncer, ip string, httpReq *http.Request) error { } defer func() { if err = res.Body.Close(); err != nil { - logger.Error(fmt.Sprintf("appsecQuery:closeBody %s", err.Error())) + bouncer.log.Error(fmt.Sprintf("appsecQuery:closeBody %s", err.Error())) } }() if res.StatusCode == http.StatusInternalServerError { - logger.Debug("crowdsecQuery statusCode:500") + bouncer.log.Debug("crowdsecQuery statusCode:500") if bouncer.appsecFailureBlock { return fmt.Errorf("appsecQuery statusCode:%d", res.StatusCode) } diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index d14a82f..759c1e8 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -44,7 +44,9 @@ func (localCache) deleteDecision(clientIP string) { cache.Del(clientIP) } -type redisCache struct{} +type redisCache struct { + log *logger.Log +} func (redisCache) getDecision(clientIP string) (bool, error) { banned, err := redis.Get(clientIP) @@ -58,15 +60,15 @@ func (redisCache) getDecision(clientIP string) (bool, error) { return false, err } -func (redisCache) setDecision(clientIP string, value string, duration int64) { +func (rc redisCache) setDecision(clientIP string, value string, duration int64) { if err := redis.Set(clientIP, []byte(value), duration); err != nil { - logger.Error(fmt.Sprintf("cache:setDecisionRedisCache %s", err.Error())) + rc.log.Error(fmt.Sprintf("cache:setDecisionRedisCache %s", err.Error())) } } -func (redisCache) deleteDecision(clientIP string) { +func (rc redisCache) deleteDecision(clientIP string) { if err := redis.Del(clientIP); err != nil { - logger.Error(fmt.Sprintf("cache:deleteDecisionRedisCache %s", err.Error())) + rc.log.Error(fmt.Sprintf("cache:deleteDecisionRedisCache %s", err.Error())) } } @@ -79,40 +81,42 @@ type cacheInterface interface { // Client Cache client. type Client struct { cache cacheInterface + log *logger.Log } -// New Initialize cache client. -func (client *Client) New(isRedis bool, host, pass, database string) { +// Init Initialize cache client. +func (c *Client) Init(log *logger.Log, isRedis bool, host, pass, database string) { + c.log = log if isRedis { redis.Init(host, pass, database) - client.cache = &redisCache{} + c.cache = &redisCache{log: log} } else { - client.cache = &localCache{} + c.cache = &localCache{} } - logger.Debug(fmt.Sprintf("cache:New initialized isRedis:%v", isRedis)) + c.log.Debug(fmt.Sprintf("cache:New initialized isRedis:%v", isRedis)) } // DeleteDecision delete decision in cache. -func (client *Client) DeleteDecision(clientIP string) { - logger.Debug(fmt.Sprintf("cache:DeleteDecision ip:%v", clientIP)) - client.cache.deleteDecision(clientIP) +func (c *Client) DeleteDecision(clientIP string) { + c.log.Debug(fmt.Sprintf("cache:DeleteDecision ip:%v", clientIP)) + c.cache.deleteDecision(clientIP) } // GetDecision check in the cache if the IP has the banned / not banned value. // Otherwise return with an error to add the IP in cache if we are on. -func (client *Client) GetDecision(clientIP string) (bool, error) { - logger.Debug(fmt.Sprintf("cache:GetDecision ip:%v", clientIP)) - return client.cache.getDecision(clientIP) +func (c *Client) GetDecision(clientIP string) (bool, error) { + c.log.Debug(fmt.Sprintf("cache:GetDecision ip:%v", clientIP)) + return c.cache.getDecision(clientIP) } // SetDecision update the cache with the IP as key and the value banned / not banned. -func (client *Client) SetDecision(clientIP string, isBanned bool, duration int64) { - logger.Debug(fmt.Sprintf("cache:SetDecision ip:%v isBanned:%v duration:%vs", clientIP, isBanned, duration)) +func (c *Client) SetDecision(clientIP string, isBanned bool, duration int64) { + c.log.Debug(fmt.Sprintf("cache:SetDecision ip:%v isBanned:%v duration:%vs", clientIP, isBanned, duration)) var value string if isBanned { value = cacheBannedValue } else { value = cacheNoBannedValue } - client.cache.setDecision(clientIP, value, duration) + c.cache.setDecision(clientIP, value, duration) } diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go index 9e40618..17e2aa2 100644 --- a/pkg/cache/cache_test.go +++ b/pkg/cache/cache_test.go @@ -4,12 +4,14 @@ package cache import ( "testing" + + logger "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/logger" ) func Test_GetDecision(t *testing.T) { IPInCache := "10.0.0.10" IPNotInCache := "10.0.0.20" - client := &Client{cache: &localCache{}} + client := &Client{cache: &localCache{}, log: logger.New("INFO")} client.SetDecision(IPInCache, true, 10) type args struct { clientIP string @@ -45,7 +47,7 @@ func Test_GetDecision(t *testing.T) { } func Test_SetDecision(t *testing.T) { - client := &Client{cache: &localCache{}} + client := &Client{cache: &localCache{}, log: logger.New("INFO")} IPInCache := "10.0.0.11" type args struct { clientIP string @@ -76,7 +78,7 @@ func Test_SetDecision(t *testing.T) { func Test_DeleteDecision(t *testing.T) { IPInCache := "10.0.0.12" IPNotInCache := "10.0.0.22" - client := &Client{cache: &localCache{}} + client := &Client{cache: &localCache{}, log: logger.New("INFO")} client.SetDecision(IPInCache, true, 10) type args struct { clientIP string diff --git a/pkg/configuration/configuration.go b/pkg/configuration/configuration.go index 9573db3..7047c1a 100644 --- a/pkg/configuration/configuration.go +++ b/pkg/configuration/configuration.go @@ -235,11 +235,9 @@ func validateParamsTLS(config *Config) error { func validateParamsIPs(listIP []string, key string) error { if len(listIP) > 0 { - if _, err := ip.NewChecker(listIP); err != nil { + if _, err := ip.NewChecker(logger.New("INFO"), listIP); err != nil { return fmt.Errorf("%s must be a list of IP/CIDR :%w", key, err) } - } else { - logger.Debug(fmt.Sprintf("No IP provided for %s", key)) } return nil } @@ -275,16 +273,18 @@ func validateParamsRequired(config *Config) error { } // GetTLSConfigCrowdsec get TLS config from Config. -func GetTLSConfigCrowdsec(config *Config) (*tls.Config, error) { +// +//nolint:nestif +func GetTLSConfigCrowdsec(config *Config, log *logger.Log) (*tls.Config, error) { tlsConfig := new(tls.Config) tlsConfig.RootCAs = x509.NewCertPool() //nolint:gocritic if config.CrowdsecLapiScheme != HTTPS { - logger.Debug("getTLSConfigCrowdsec:CrowdsecLapiScheme https:no") + log.Debug("getTLSConfigCrowdsec:CrowdsecLapiScheme https:no") return tlsConfig, nil } else if config.CrowdsecLapiTLSInsecureVerify { - logger.Debug("getTLSConfigCrowdsec:CrowdsecLapiTLSInsecureVerify tlsInsecure:true") tlsConfig.InsecureSkipVerify = true + log.Debug("getTLSConfigCrowdsec:CrowdsecLapiTLSInsecureVerify tlsInsecure:true") // If we return here and still want to use client auth this won't work // return tlsConfig, nil } else { @@ -292,12 +292,13 @@ func GetTLSConfigCrowdsec(config *Config) (*tls.Config, error) { if err != nil { return nil, err } - cert := []byte(certAuthority) - if !tlsConfig.RootCAs.AppendCertsFromPEM(cert) { - logger.Debug("getTLSConfigCrowdsec:CrowdsecLapiTLSCertificateAuthority read cert failed") - // here we return because if CrowdsecLapiTLSInsecureVerify is false - // and CA not load, we can't communicate with https - return nil, fmt.Errorf("getTLSConfigCrowdsec:cannot load CA and verify cert is enabled") + if len(certAuthority) > 0 { + if !tlsConfig.RootCAs.AppendCertsFromPEM([]byte(certAuthority)) { + // here we return because if CrowdsecLapiTLSInsecureVerify is false + // and CA not load, we can't communicate with https + return nil, fmt.Errorf("getTLSConfigCrowdsec:cannot load CA and verify cert is enabled") + } + log.Debug("getTLSConfigCrowdsec:CrowdsecLapiTLSCertificateAuthority CA added successfully") } } diff --git a/pkg/configuration/configuration_test.go b/pkg/configuration/configuration_test.go index b3cc4db..8790e21 100644 --- a/pkg/configuration/configuration_test.go +++ b/pkg/configuration/configuration_test.go @@ -4,6 +4,8 @@ import ( "crypto/tls" "reflect" "testing" + + logger "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/logger" ) func getMinimalConfig() *Config { @@ -147,7 +149,7 @@ func Test_validateParamsIPs(t *testing.T) { {name: "Not validate localhost", args: args{listIP: []string{0: "localhost"}}, wantErr: true}, {name: "Not validate a weird ip", args: args{listIP: []string{0: "0.0.0.0/89"}}, wantErr: true}, {name: "Not validate a weird ip 2", args: args{listIP: []string{0: "0.0.0.256/12"}}, wantErr: true}, - {name: "Validate an ip not trimed", args: args{listIP: []string{0: " 0.0.0.0/0"}}, wantErr: false}, + {name: "Validate an ip not trimmed", args: args{listIP: []string{0: " 0.0.0.0/0"}}, wantErr: false}, {name: "Validate an ip", args: args{listIP: []string{0: "0.0.0.0/12"}}, wantErr: false}, {name: "Validate an ip list", args: args{listIP: []string{0: "0.0.0.0/0", 1: "1.1.1.1/1"}}, wantErr: false}, } @@ -231,7 +233,7 @@ func Test_GetTLSConfigCrowdsec(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetTLSConfigCrowdsec(tt.args.config) + got, err := GetTLSConfigCrowdsec(tt.args.config, logger.New("INFO")) if (err != nil) != tt.wantErr { t.Errorf("getTLSConfigCrowdsec() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/ip/ip.go b/pkg/ip/ip.go index 357ef57..cc7db5d 100644 --- a/pkg/ip/ip.go +++ b/pkg/ip/ip.go @@ -8,7 +8,7 @@ import ( "net/http" "strings" - "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/logger" + logger "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/logger" ) // CHECKER @@ -20,14 +20,14 @@ type Checker struct { } // NewChecker builds a new Checker given a list of CIDR-Strings to trusted IPs. -func NewChecker(trustedIPs []string) (*Checker, error) { +func NewChecker(log *logger.Log, trustedIPs []string) (*Checker, error) { checker := &Checker{} for _, ipMaskRaw := range trustedIPs { ipMask := strings.TrimSpace(ipMaskRaw) if ipAddr := net.ParseIP(ipMask); ipAddr != nil { checker.authorizedIPs = append(checker.authorizedIPs, &ipAddr) - logger.Debug(fmt.Sprintf("IP %v is trusted", ipAddr)) + log.Debug(fmt.Sprintf("IP %v is trusted", ipAddr)) continue } @@ -36,7 +36,7 @@ func NewChecker(trustedIPs []string) (*Checker, error) { return nil, fmt.Errorf("parsing CIDR trusted IPs %s: %w", ipAddr, err) } checker.authorizedIPsNet = append(checker.authorizedIPsNet, ipAddr) - logger.Debug(fmt.Sprintf("IP network %v is trusted", ipAddr)) + log.Debug(fmt.Sprintf("IP network %v is trusted", ipAddr)) } return checker, nil diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 30dffd0..c485bfc 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -8,32 +8,41 @@ import ( "os" ) -var ( - loggerInfo = log.New(io.Discard, "INFO: CrowdsecBouncerTraefikPlugin: ", log.Ldate|log.Ltime) //nolint:gochecknoglobals - loggerDebug = log.New(io.Discard, "DEBUG: CrowdsecBouncerTraefikPlugin: ", log.Ldate|log.Ltime) //nolint:gochecknoglobals - loggerError = log.New(io.Discard, "ERROR: CrowdsecBouncerTraefikPlugin: ", log.Ldate|log.Ltime) //nolint:gochecknoglobals -) +// Log Logger struct. +type Log struct { + logError *log.Logger + logInfo *log.Logger + logDebug *log.Logger +} -// Init Set Default log level to info in case log level to defined. -func Init(logLevel string) { - loggerError.SetOutput(os.Stderr) - loggerInfo.SetOutput(os.Stdout) +// New Set Default log level to info in case log level to defined. +func New(logLevel string) *Log { + logError := log.New(io.Discard, "ERROR: CrowdsecBouncerTraefikPlugin: ", log.Ldate|log.Ltime) + logInfo := log.New(io.Discard, "INFO: CrowdsecBouncerTraefikPlugin: ", log.Ldate|log.Ltime) + logDebug := log.New(io.Discard, "DEBUG: CrowdsecBouncerTraefikPlugin: ", log.Ldate|log.Ltime) + logError.SetOutput(os.Stderr) + logInfo.SetOutput(os.Stdout) if logLevel == "DEBUG" { - loggerDebug.SetOutput(os.Stdout) + logDebug.SetOutput(os.Stdout) + } + return &Log{ + logError: logError, + logInfo: logInfo, + logDebug: logDebug, } } // Info log to Stdout. -func Info(str string) { - loggerInfo.Printf(str) +func (l *Log) Info(str string) { + l.logInfo.Printf(str) } // Debug log to Stdout. -func Debug(str string) { - loggerDebug.Printf(str) +func (l *Log) Debug(str string) { + l.logDebug.Printf(str) } // Error log to Stderr. -func Error(str string) { - loggerError.Printf(str) +func (l *Log) Error(str string) { + l.logError.Printf(str) }