Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add possibility to configure log format #799 #2941

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a8477f7
make it possible to enable json log
Apr 14, 2024
fe0c8f3
fix
Apr 14, 2024
c8652aa
fix typo
Apr 14, 2024
9a50396
fix typo
Apr 14, 2024
a72f223
fix typo
Apr 14, 2024
d98a569
fix typo
Apr 14, 2024
11b1e23
fix typo
Apr 14, 2024
9674cc1
fix typo
Apr 14, 2024
194ac7a
Add error handling
Apr 14, 2024
bf179f4
Add log_format to default config
Apr 14, 2024
cb6e59f
Fix syntax error in if statement
Apr 14, 2024
2355a93
Fix typo
Apr 14, 2024
d7022a2
Fix typo
Apr 14, 2024
99606b7
Fix some typos and change naming from native to text, makes more sense
Apr 15, 2024
ea83066
Set same timestamp format for json logging
Apr 15, 2024
2697c75
Fix formatting
Apr 15, 2024
e44b4cc
Move in if statement under previous
Apr 15, 2024
27d4311
Fix some formatting that got messed up
Apr 15, 2024
b5a3e1c
Default to text formatter, if log_format is not configured.
Apr 15, 2024
96b8408
defining logFormatter outside if statement so that log.SetFormatter(l…
Apr 15, 2024
1ee0358
Add variables that were undefined
Apr 15, 2024
c7cdb77
Argument were missing when calling SetDefaultLoggerConfig function
Apr 15, 2024
65dcc58
Fix order of arguments passed
Apr 15, 2024
8ad6220
Fix order of arguments passed
Apr 15, 2024
8ed607e
Fix typo
Apr 15, 2024
3c6e086
Merge branch 'master' into fix_799
mmetc May 7, 2024
babb74c
Implicit log_format = "text"
mmetc May 7, 2024
c05c46d
functional test
mmetc May 7, 2024
1ace3f1
ignore log_format in FatalHook
mmetc May 7, 2024
5cfc3c5
Merge branch 'master' into fix_799
buixor May 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cmd/crowdsec-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@ func (cli *cliRoot) initialize() {
log.Fatalf("output format '%s' not supported: must be one of human, json, raw", csConfig.Cscli.Output)
}

log.SetFormatter(&log.TextFormatter{DisableTimestamp: true})
log.SetFormatter(&log.TextFormatter{
DisableTimestamp: true,
DisableLevelTruncation: true,
})

if csConfig.Cscli.Output == "json" {
log.SetFormatter(&log.JSONFormatter{})
Expand Down
24 changes: 22 additions & 2 deletions cmd/crowdsec/fatalhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,43 @@

import (
"io"
"os"

log "github.com/sirupsen/logrus"
)

// FatalHook is used to log fatal messages to stderr when the rest goes to a file
type FatalHook struct {
Writer io.Writer
Formatter log.Formatter
LogLevels []log.Level
}

func newFatalHook() *FatalHook {
return &FatalHook{
Writer: os.Stderr,
Formatter: &log.TextFormatter{
DisableTimestamp: true,
// XXX: logrus.TextFormatter has either key pairs with no colors,
// or "LEVEL [optional timestamp] message", with colors.
// We force colors to make sure we get the latter, even if
// the output is not a terminal.
// There are more flexible formatters that don't conflate the two concepts,
// or we can write our own.

Check warning on line 27 in cmd/crowdsec/fatalhook.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec/fatalhook.go#L22-L27

Added lines #L22 - L27 were not covered by tests
ForceColors: true,
DisableLevelTruncation: true,
},
LogLevels: []log.Level{log.FatalLevel, log.PanicLevel},
}

Check warning on line 32 in cmd/crowdsec/fatalhook.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec/fatalhook.go#L32

Added line #L32 was not covered by tests
}

func (hook *FatalHook) Fire(entry *log.Entry) error {
line, err := entry.String()
line, err := hook.Formatter.Format(entry)
if err != nil {
return err
}

_, err = hook.Writer.Write([]byte(line))
_, err = hook.Writer.Write(line)

return err
}
Expand Down
7 changes: 2 additions & 5 deletions cmd/crowdsec/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,16 +255,13 @@ func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet boo
if err := types.SetDefaultLoggerConfig(cConfig.Common.LogMedia,
cConfig.Common.LogDir, *cConfig.Common.LogLevel,
cConfig.Common.LogMaxSize, cConfig.Common.LogMaxFiles,
cConfig.Common.LogMaxAge, cConfig.Common.CompressLogs,
cConfig.Common.LogMaxAge, cConfig.Common.LogFormat, cConfig.Common.CompressLogs,
cConfig.Common.ForceColorLogs); err != nil {
return nil, err
}

if cConfig.Common.LogMedia != "stdout" {
log.AddHook(&FatalHook{
Writer: os.Stderr,
LogLevels: []log.Level{log.FatalLevel, log.PanicLevel},
})
log.AddHook(newFatalHook())
}

if err := csconfig.LoadFeatureFlagsFile(configFile, log.StandardLogger()); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/apiserver/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ func TestLoggingDebugToFileConfig(t *testing.T) {
cfg.LogLevel = ptr.Of(log.DebugLevel)

// Configure logging
err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel, cfg.LogMaxSize, cfg.LogMaxFiles, cfg.LogMaxAge, cfg.CompressLogs, false)
err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel, cfg.LogMaxSize, cfg.LogMaxFiles, cfg.LogMaxAge, cfg.LogFormat, cfg.CompressLogs, false)
require.NoError(t, err)

api, err := NewServer(&cfg)
Expand Down Expand Up @@ -394,7 +394,7 @@ func TestLoggingErrorToFileConfig(t *testing.T) {
cfg.LogLevel = ptr.Of(log.ErrorLevel)

// Configure logging
err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel, cfg.LogMaxSize, cfg.LogMaxFiles, cfg.LogMaxAge, cfg.CompressLogs, false)
err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel, cfg.LogMaxSize, cfg.LogMaxFiles, cfg.LogMaxAge, cfg.LogFormat, cfg.CompressLogs, false)
require.NoError(t, err)

api, err := NewServer(&cfg)
Expand Down
2 changes: 2 additions & 0 deletions pkg/csconfig/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ type LocalApiServerCfg struct {
LogMaxSize int `yaml:"-"`
LogMaxAge int `yaml:"-"`
LogMaxFiles int `yaml:"-"`
LogFormat string `yaml:"-"`
TrustedIPs []string `yaml:"trusted_ips,omitempty"`
PapiLogLevel *log.Level `yaml:"papi_log_level"`
DisableRemoteLapiRegistration bool `yaml:"disable_remote_lapi_registration,omitempty"`
Expand Down Expand Up @@ -351,6 +352,7 @@ func (c *Config) LoadAPIServer(inCli bool) error {
c.API.Server.CompressLogs = c.Common.CompressLogs
c.API.Server.LogMaxSize = c.Common.LogMaxSize
c.API.Server.LogMaxAge = c.Common.LogMaxAge
c.API.Server.LogFormat = c.Common.LogFormat
c.API.Server.LogMaxFiles = c.Common.LogMaxFiles

if c.API.Server.UseForwardedForHeaders && c.API.Server.TrustedProxies == nil {
Expand Down
1 change: 1 addition & 0 deletions pkg/csconfig/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type CommonCfg struct {
WorkingDir string `yaml:"working_dir,omitempty"` // TODO: This is just for backward compat. Remove this later
CompressLogs *bool `yaml:"compress_logs,omitempty"`
LogMaxSize int `yaml:"log_max_size,omitempty"`
LogFormat string `yaml:"log_format,omitempty"`
LogMaxAge int `yaml:"log_max_age,omitempty"`
LogMaxFiles int `yaml:"log_max_files,omitempty"`
ForceColorLogs bool `yaml:"force_color_logs,omitempty"`
Expand Down
23 changes: 20 additions & 3 deletions pkg/types/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,24 @@
var LogOutput *lumberjack.Logger //io.Writer
var logLevel log.Level

func SetDefaultLoggerConfig(cfgMode string, cfgFolder string, cfgLevel log.Level, maxSize int, maxFiles int, maxAge int, compress *bool, forceColors bool) error {
/*Configure logs*/
func SetDefaultLoggerConfig(cfgMode string, cfgFolder string, cfgLevel log.Level, maxSize int, maxFiles int, maxAge int, format string, compress *bool, forceColors bool) error {
if format == "" {
format = "text"
}

Check warning on line 20 in pkg/types/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/types/utils.go#L20

Added line #L20 was not covered by tests

switch format {
case "text":
logFormatter = &log.TextFormatter{
TimestampFormat: time.RFC3339,
FullTimestamp: true,
ForceColors: forceColors,
}

Check warning on line 28 in pkg/types/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/types/utils.go#L28

Added line #L28 was not covered by tests
case "json":
logFormatter = &log.JSONFormatter{TimestampFormat: time.RFC3339}
default:
return fmt.Errorf("unknown log_format '%s'", format)

Check warning on line 32 in pkg/types/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/types/utils.go#L31-L32

Added lines #L31 - L32 were not covered by tests
}

if cfgMode == "file" {
_maxsize := 500
if maxSize != 0 {
Expand Down Expand Up @@ -45,9 +61,10 @@
} else if cfgMode != "stdout" {
return fmt.Errorf("log mode '%s' unknown", cfgMode)
}

logLevel = cfgLevel
log.SetLevel(logLevel)
logFormatter = &log.TextFormatter{TimestampFormat: time.RFC3339, FullTimestamp: true, ForceColors: forceColors}

Check warning on line 67 in pkg/types/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/types/utils.go#L67

Added line #L67 was not covered by tests
log.SetFormatter(logFormatter)
return nil
}
Expand Down
34 changes: 34 additions & 0 deletions test/bats/01_crowdsec.bats
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,40 @@ teardown() {
refute_output
}

@test "crowdsec - log format" {
# fail early
config_disable_lapi
config_disable_agent

config_set '.common.log_media="stdout"'

config_set '.common.log_format=""'
rune -0 wait-for --err "you must run at least the API Server or crowdsec" "$CROWDSEC"
assert_stderr --partial 'level=fatal msg="you must run at least the API Server or crowdsec"'

config_set '.common.log_format="text"'
rune -0 wait-for --err "you must run at least the API Server or crowdsec" "$CROWDSEC"
assert_stderr --partial 'level=fatal msg="you must run at least the API Server or crowdsec"'

config_set '.common.log_format="json"'
rune -0 wait-for --err "you must run at least the API Server or crowdsec" "$CROWDSEC"
rune -0 jq -c 'select(.msg=="you must run at least the API Server or crowdsec") | .level' <(stderr | grep "^{")
assert_output '"fatal"'

# If log_media='file', a hook to stderr is added only for fatal messages,
# with a predefined formatter (level + msg, no timestamp, ignore log_format)

config_set '.common.log_media="file"'

config_set '.common.log_format="text"'
rune -0 wait-for --err "you must run at least the API Server or crowdsec" "$CROWDSEC"
assert_stderr --regexp 'FATAL.* you must run at least the API Server or crowdsec$'

config_set '.common.log_format="json"'
rune -0 wait-for --err "you must run at least the API Server or crowdsec" "$CROWDSEC"
assert_stderr --regexp 'FATAL.* you must run at least the API Server or crowdsec$'
}

@test "CS_LAPI_SECRET not strong enough" {
CS_LAPI_SECRET=foo rune -1 wait-for "$CROWDSEC"
assert_stderr --partial "api server init: unable to run local API: controller init: CS_LAPI_SECRET not strong enough"
Expand Down
Loading