Browse Source

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
Thibault "bui" Koechlin 4 years ago
parent
commit
cd06929e75
2 changed files with 150 additions and 9 deletions
  1. 13 9
      pkg/apiserver/apiserver.go
  2. 137 0
      pkg/apiserver/apiserver_test.go

+ 13 - 9
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,

+ 137 - 0
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)
+
+}