From cd06929e751b463056e84a730ae839e0d11eb6d9 Mon Sep 17 00:00:00 2001 From: "Thibault \"bui\" Koechlin" Date: Wed, 7 Apr 2021 11:39:24 +0200 Subject: [PATCH] honor log levels for api : don't log access logs if level is warn/err (#732) * honor log levels for api : don't log access logs if level is warn/err * add basic test for logging of api server --- pkg/apiserver/apiserver.go | 22 ++--- pkg/apiserver/apiserver_test.go | 137 ++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 9 deletions(-) diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 9993ce09e..5022ac28a 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -3,9 +3,7 @@ package apiserver import ( "context" "fmt" - "io" "net/http" - "os" "time" "github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers" @@ -16,6 +14,7 @@ import ( "github.com/go-co-op/gocron" "github.com/pkg/errors" log "github.com/sirupsen/logrus" + "gopkg.in/natefinch/lumberjack.v2" "gopkg.in/tomb.v2" ) @@ -71,6 +70,7 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) { /*The logger that will be used by handlers*/ clog := log.New() + if err := types.ConfigureLogger(clog); err != nil { return nil, errors.Wrap(err, "while configuring gin logger") } @@ -78,17 +78,21 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) { clog.SetLevel(*config.LogLevel) } - gin.DefaultErrorWriter = clog.Writer() - - // Logging to a file. + /*Configure logs*/ if logFile != "" { - file, err := os.Create(logFile) - if err != nil { - return &APIServer{}, errors.Wrapf(err, "creating api access log file: %s", logFile) + LogOutput := &lumberjack.Logger{ + Filename: logFile, + MaxSize: 500, //megabytes + MaxBackups: 3, + MaxAge: 28, //days + Compress: true, //disabled by default } - gin.DefaultWriter = io.MultiWriter(file) + clog.SetOutput(LogOutput) } + gin.DefaultErrorWriter = clog.WriterLevel(log.ErrorLevel) + gin.DefaultWriter = clog.Writer() + router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", param.ClientIP, diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index ad8897df6..2f0888ad9 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -3,15 +3,18 @@ package apiserver import ( "encoding/json" "fmt" + "io/ioutil" "net/http" "net/http/httptest" "os" "strings" "testing" + "time" middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1" "github.com/crowdsecurity/crowdsec/pkg/cwversion" "github.com/crowdsecurity/crowdsec/pkg/models" + "github.com/crowdsecurity/crowdsec/pkg/types" "github.com/go-openapi/strfmt" "github.com/crowdsecurity/crowdsec/pkg/csconfig" @@ -214,3 +217,137 @@ func TestUnknownPath(t *testing.T) { assert.Equal(t, 404, w.Code) } + +/* + +ListenURI string `yaml:"listen_uri,omitempty"` //127.0.0.1:8080 + TLS *TLSCfg `yaml:"tls"` + DbConfig *DatabaseCfg `yaml:"-"` + LogDir string `yaml:"-"` + LogMedia string `yaml:"-"` + OnlineClient *OnlineApiClientCfg `yaml:"online_client"` + ProfilesPath string `yaml:"profiles_path,omitempty"` + Profiles []*ProfileCfg `yaml:"-"` + LogLevel *log.Level `yaml:"log_level"` + UseForwardedForHeaders bool `yaml:"use_forwarded_for_headers,omitempty"` + +*/ + +func TestLoggingDebugToFileConfig(t *testing.T) { + + /*declare settings*/ + maxAge := "1h" + flushConfig := csconfig.FlushDBCfg{ + MaxAge: &maxAge, + } + dbconfig := csconfig.DatabaseCfg{ + Type: "sqlite", + DbPath: "./ent", + Flush: &flushConfig, + } + cfg := csconfig.LocalApiServerCfg{ + ListenURI: "127.0.0.1:8080", + LogMedia: "file", + LogDir: ".", + DbConfig: &dbconfig, + } + lvl := log.DebugLevel + expectedFile := "./crowdsec_api.log" + expectedLines := []string{"/test42"} + cfg.LogLevel = &lvl + + os.Remove("./crowdsec.log") + os.Remove(expectedFile) + + // Configure logging + if err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel); err != nil { + t.Fatal(err.Error()) + } + api, err := NewServer(&cfg) + if err != nil { + t.Fatalf("failed to create api : %s", err) + } + if api == nil { + t.Fatalf("failed to create api #2 is nbill") + } + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/test42", nil) + req.Header.Set("User-Agent", UserAgent) + api.router.ServeHTTP(w, req) + assert.Equal(t, 404, w.Code) + //wait for the request to happen + time.Sleep(500 * time.Millisecond) + + //check file content + data, err := ioutil.ReadFile(expectedFile) + if err != nil { + t.Fatalf("failed to read file : %s", err) + } + + for _, expectedStr := range expectedLines { + if !strings.Contains(string(data), expectedStr) { + t.Fatalf("expected %s in %s", expectedStr, string(data)) + } + } + + os.Remove("./crowdsec.log") + os.Remove(expectedFile) + +} + +func TestLoggingErrorToFileConfig(t *testing.T) { + + /*declare settings*/ + maxAge := "1h" + flushConfig := csconfig.FlushDBCfg{ + MaxAge: &maxAge, + } + dbconfig := csconfig.DatabaseCfg{ + Type: "sqlite", + DbPath: "./ent", + Flush: &flushConfig, + } + cfg := csconfig.LocalApiServerCfg{ + ListenURI: "127.0.0.1:8080", + LogMedia: "file", + LogDir: ".", + DbConfig: &dbconfig, + } + lvl := log.ErrorLevel + expectedFile := "./crowdsec_api.log" + cfg.LogLevel = &lvl + + os.Remove("./crowdsec.log") + os.Remove(expectedFile) + + // Configure logging + if err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel); err != nil { + t.Fatal(err.Error()) + } + api, err := NewServer(&cfg) + if err != nil { + t.Fatalf("failed to create api : %s", err) + } + if api == nil { + t.Fatalf("failed to create api #2 is nbill") + } + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/test42", nil) + req.Header.Set("User-Agent", UserAgent) + api.router.ServeHTTP(w, req) + assert.Equal(t, 404, w.Code) + //wait for the request to happen + time.Sleep(500 * time.Millisecond) + + //check file content + _, err = ioutil.ReadFile(expectedFile) + if err == nil { + t.Fatalf("file should be empty") + } + + os.Remove("./crowdsec.log") + os.Remove(expectedFile) + +}