feat(configuration): add error and warn log levels (#2050)

This is so levels like warn and error can be used to exclude info or warn messages. Additionally there is a reasonable refactoring of logging moving the log config options to the logging key because there are a significant number of log options now. This also decouples the expvars and pprof handlers from the log level, and they are now configured by server.enable_expvars and server.enable_pprof at any logging level.
This commit is contained in:
James Elliott 2021-06-01 14:09:50 +10:00 committed by GitHub
parent 4cfda7eece
commit cef35fadcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 482 additions and 195 deletions

View File

@ -52,11 +52,17 @@ func startServer() {
}
}
if err := logging.InitializeLogger(config.LogFormat, config.LogFilePath, config.LogKeepStdout); err != nil {
if err := logging.InitializeLogger(config.Logging.Format, config.Logging.FilePath, config.Logging.KeepStdout); err != nil {
logger.Fatalf("Cannot initialize logger: %v", err)
}
switch config.LogLevel {
switch config.Logging.Level {
case "error":
logger.Info("Logging severity set to error")
logging.SetLevel(logrus.ErrorLevel)
case "warn":
logger.Info("Logging severity set to warn")
logging.SetLevel(logrus.WarnLevel)
case "info":
logger.Info("Logging severity set to info")
logging.SetLevel(logrus.InfoLevel)

View File

@ -34,17 +34,24 @@ server:
## Must be alphanumeric chars and should not contain any slashes.
path: ""
## Enables the pprof endpoint.
enable_pprof: false
## Enables the expvars endpoint.
enable_expvars: false
## Level of verbosity for logs: info, debug, trace.
log_level: debug
logging:
level: debug
## Format the logs are written as: json, text.
# log_format: json
## Format the logs are written as: json, text.
# format: json
## File path where the logs will be written. If not set logs are written to stdout.
# log_file_path: /config/authelia.log
## File path where the logs will be written. If not set logs are written to stdout.
# file_path: /config/authelia.log
## Whether to also log to stdout when a log_file_path is defined.
# log_keep_stdout: false
## Whether to also log to stdout when a log_file_path is defined.
# keep_stdout: false
## The secret used to generate JWT tokens when validating user identity by email confirmation. JWT Secret can also be
## set using a secret: https://www.authelia.com/docs/configuration/secrets.html

View File

@ -2,7 +2,7 @@
layout: default
title: Identity Providers
parent: Configuration
nav_order: 12
nav_order: 3
has_children: true
---

View File

@ -0,0 +1,106 @@
---
layout: default
title: Logging
parent: Configuration
nav_order: 4
---
# Logging
The logging section tunes the logging settings.
## Configuration
```yaml
logging:
level: info
format: text
file_path: ""
keep_stdout: false
```
## Options
### level
<div markdown="1">
type: string
{: .label .label-config .label-purple }
default: info
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
Defines the level of logs used by Authelia. This level can be set to `trace`, `debug`, `info`, `warn`, or `error`. When
setting level to `trace`, you will generate a large amount of log entries and expose the `/debug/vars` and
`/debug/pprof/` endpoints which should not be enabled in production.
```yaml
logging:
level: debug
```
### format
<div markdown="1">
type: string
{: .label .label-config .label-purple }
default: text
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
Defines the format of the logs written by Authelia. This format can be set to `json` or `text`.
```yaml
logging:
format: json
```
#### JSON format
```
{"level":"info","msg":"Logging severity set to info","time":"2020-01-01T00:00:00+11:00"}
{"level":"info","msg":"Authelia is listening for non-TLS connections on 0.0.0.0:9091","time":"2020-01-01T00:00:00+11:00"}
```
#### Text format
```
time="2020-01-01T00:00:00+11:00" level=info msg="Logging severity set to info"
time="2020-01-01T00:00:00+11:00" level=info msg="Authelia is listening for non-TLS connections on 0.0.0.0:9091"
```
### file_path
<div markdown="1">
type: string (path)
{: .label .label-config .label-purple }
default: ""
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
Logs can be stored in a file when file path is provided. Otherwise logs are written to standard output. When setting the
level to `debug` or `trace` this will generate large amount of log entries. Administrators will need to ensure that
they rotate and/or truncate the logs over time to prevent significant long-term disk usage.
```yaml
logging:
file_path: /config/authelia.log
```
### keep_stdout
<div markdown="1">
type: boolean
{: .label .label-config .label-purple }
default: false
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
Overrides the behaviour to redirect logging only to the `file_path`. If set to `true` logs will be written to both
standard output, and the defined logging location.
```yaml
logging:
keep_stdout: true
```

View File

@ -2,7 +2,7 @@
layout: default
title: Miscellaneous
parent: Configuration
nav_order: 3
nav_order: 5
---
# Miscellaneous
@ -93,88 +93,6 @@ key or the CA public key which signed them (don't add the private key).
certificates_directory: /config/certs/
```
## Logging
### log_level
<div markdown="1">
type: string
{: .label .label-config .label-purple }
default: info
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
Defines the level of logs used by Authelia. This level can be set to `trace`, `debug` or `info`. When setting log_level
to `trace`, you will generate a large amount of log entries and expose the `/debug/vars` and `/debug/pprof/` endpoints
which should not be enabled in production.
```yaml
log_level: debug
```
### log_format
<div markdown="1">
type: string
{: .label .label-config .label-purple }
default: text
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
Defines the format of the logs written by Authelia. This format can be set to `json` or `text`.
```yaml
log_format: json
```
#### JSON format
```
{"level":"info","msg":"Logging severity set to info","time":"2020-01-01T00:00:00+11:00"}
{"level":"info","msg":"Authelia is listening for non-TLS connections on 0.0.0.0:9091","time":"2020-01-01T00:00:00+11:00"}
```
#### Text format
```
time="2020-01-01T00:00:00+11:00" level=info msg="Logging severity set to info"
time="2020-01-01T00:00:00+11:00" level=info msg="Authelia is listening for non-TLS connections on 0.0.0.0:9091"
```
### log_file_path
<div markdown="1">
type: string (path)
{: .label .label-config .label-purple }
default: ""
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
Logs can be stored in a file when file path is provided. Otherwise logs are written to standard output. When setting the
log_level to `debug` or `trace` this will generate large amount of log entries. Administrators will need to ensure that
they rotate and/or truncate the logs over time to prevent significant long-term disk usage.
```yaml
log_file_path: /config/authelia.log
```
### log_keep_stdout
<div markdown="1">
type: boolean
{: .label .label-config .label-purple }
default: false
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
Overrides the behaviour to redirect logging only to the `log_file_path`. If set to `true` logs will be written to both
standard output, and the defined logging location.
```yaml
log_keep_stdout: true
```
## jwt_secret
<div markdown="1">
type: string

View File

@ -2,7 +2,7 @@
layout: default
title: Notifier
parent: Configuration
nav_order: 6
nav_order: 8
has_children: true
---

View File

@ -2,7 +2,7 @@
layout: default
title: Time-based One-Time Password
parent: Configuration
nav_order: 4
nav_order: 6
---
# Time-based One-Time Password

View File

@ -2,7 +2,7 @@
layout: default
title: Regulation
parent: Configuration
nav_order: 5
nav_order: 7
---
# Regulation

View File

@ -2,7 +2,7 @@
layout: default
title: Secrets
parent: Configuration
nav_order: 6
nav_order: 8
---
# Secrets

View File

@ -2,7 +2,7 @@
layout: default
title: Server
parent: Configuration
nav_order: 7
nav_order: 9
---
# Server
@ -16,6 +16,8 @@ server:
read_buffer_size: 4096
write_buffer_size: 4096
path: ""
enable_pprof: false
enable_expvars: false
```
## Options
@ -72,6 +74,31 @@ server:
path: authelia
```
### enable_pprof
<div markdown="1">
type: boolean
{: .label .label-config .label-purple }
default: false
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
Enables the go pprof endpoints.
### enable_expvars
<div markdown="1">
type: boolean
{: .label .label-config .label-purple }
default: false
{: .label .label-config .label-blue }
required: no
{: .label .label-config .label-green }
</div>
Enables the go expvars endpoints.
## Additional Notes
### Buffer Sizes

View File

@ -2,7 +2,7 @@
layout: default
title: Session
parent: Configuration
nav_order: 8
nav_order: 10
has_children: true
---

View File

@ -2,7 +2,7 @@
layout: default
title: Storage backends
parent: Configuration
nav_order: 10
nav_order: 12
has_children: true
---

View File

@ -2,7 +2,7 @@
layout: default
title: Theme
parent: Configuration
nav_order: 11
nav_order: 13
---
# Theme

View File

@ -5,7 +5,8 @@
host: 0.0.0.0
port: 9091
log_level: debug
logging:
level: debug
# This secret can also be set using the env variables AUTHELIA_JWT_SECRET_FILE
jwt_secret: a_very_important_secret
default_redirection_url: https://public.example.com

View File

@ -5,7 +5,8 @@
host: 0.0.0.0
port: 9091
log_level: debug
logging:
level: debug
jwt_secret: a_very_important_secret
default_redirection_url: https://public.example.com
totp:

View File

@ -34,17 +34,24 @@ server:
## Must be alphanumeric chars and should not contain any slashes.
path: ""
## Enables the pprof endpoint.
enable_pprof: false
## Enables the expvars endpoint.
enable_expvars: false
## Level of verbosity for logs: info, debug, trace.
log_level: debug
logging:
level: debug
## Format the logs are written as: json, text.
# log_format: json
## Format the logs are written as: json, text.
# format: json
## File path where the logs will be written. If not set logs are written to stdout.
# log_file_path: /config/authelia.log
## File path where the logs will be written. If not set logs are written to stdout.
# file_path: /config/authelia.log
## Whether to also log to stdout when a log_file_path is defined.
# log_keep_stdout: false
## Whether to also log to stdout when a log_file_path is defined.
# keep_stdout: false
## The secret used to generate JWT tokens when validating user identity by email confirmation. JWT Secret can also be
## set using a secret: https://www.authelia.com/docs/configuration/secrets.html

View File

@ -164,7 +164,7 @@ func TestShouldErrorParseBadConfigFile(t *testing.T) {
require.Len(t, errors, 1)
require.EqualError(t, errors[0], "Error malformed yaml: line 24: did not find expected alphabetic or numeric character")
require.EqualError(t, errors[0], "Error malformed yaml: line 25: did not find expected alphabetic or numeric character")
}
func TestShouldParseConfigFile(t *testing.T) {
@ -185,7 +185,7 @@ func TestShouldParseConfigFile(t *testing.T) {
require.Len(t, errors, 0)
assert.Equal(t, 9091, config.Port)
assert.Equal(t, "debug", config.LogLevel)
assert.Equal(t, "debug", config.Logging.Level)
assert.Equal(t, "https://home.example.com:8080/", config.DefaultRedirectionURL)
assert.Equal(t, "authelia.com", config.TOTP.Issuer)
assert.Equal(t, "secret_from_env", config.JWTSecret)
@ -221,7 +221,7 @@ func TestShouldParseAltConfigFile(t *testing.T) {
require.Len(t, errors, 0)
assert.Equal(t, 9091, config.Port)
assert.Equal(t, "debug", config.LogLevel)
assert.Equal(t, "debug", config.Logging.Level)
assert.Equal(t, "https://home.example.com:8080/", config.DefaultRedirectionURL)
assert.Equal(t, "authelia.com", config.TOTP.Issuer)
assert.Equal(t, "secret_from_env", config.JWTSecret)
@ -253,7 +253,7 @@ func TestShouldNotParseConfigFileWithOldOrUnexpectedKeys(t *testing.T) {
return errors[i].Error() < errors[j].Error()
})
assert.EqualError(t, errors[0], "config key not expected: loggy_file")
assert.EqualError(t, errors[1], "invalid configuration key 'logs_level' was replaced by 'log_level'")
assert.EqualError(t, errors[1], "invalid configuration key 'logs_level' was replaced by 'logging.level'")
}
func TestShouldValidateConfigurationTemplate(t *testing.T) {

View File

@ -8,13 +8,16 @@ type Configuration struct {
TLSCert string `mapstructure:"tls_cert"`
TLSKey string `mapstructure:"tls_key"`
CertificatesDirectory string `mapstructure:"certificates_directory"`
LogLevel string `mapstructure:"log_level"`
LogFormat string `mapstructure:"log_format"`
LogFilePath string `mapstructure:"log_file_path"`
LogKeepStdout bool `mapstructure:"log_keep_stdout"`
JWTSecret string `mapstructure:"jwt_secret"`
DefaultRedirectionURL string `mapstructure:"default_redirection_url"`
// TODO: DEPRECATED START. Remove in 4.33.0.
LogLevel string `mapstructure:"log_level"`
LogFormat string `mapstructure:"log_format"`
LogFilePath string `mapstructure:"log_file_path"`
// TODO: DEPRECATED END. Remove in 4.33.0.
Logging LoggingConfiguration `mapstructure:"logging"`
IdentityProviders IdentityProvidersConfiguration `mapstructure:"identity_providers"`
AuthenticationBackend AuthenticationBackendConfiguration `mapstructure:"authentication_backend"`
Session SessionConfiguration `mapstructure:"session"`

View File

@ -0,0 +1,15 @@
package schema
// LoggingConfiguration represents the logging configuration.
type LoggingConfiguration struct {
Level string `mapstructure:"level"`
Format string `mapstructure:"format"`
FilePath string `mapstructure:"file_path"`
KeepStdout bool `mapstructure:"keep_stdout"`
}
// DefaultLoggingConfiguration is the default logging configuration.
var DefaultLoggingConfiguration = LoggingConfiguration{
Level: "info",
Format: "text",
}

View File

@ -5,6 +5,8 @@ type ServerConfiguration struct {
Path string `mapstructure:"path"`
ReadBufferSize int `mapstructure:"read_buffer_size"`
WriteBufferSize int `mapstructure:"write_buffer_size"`
EnablePprof bool `mapstructure:"enable_endpoint_pprof"`
EnableExpvars bool `mapstructure:"enable_endpoint_expvars"`
}
// DefaultServerConfiguration represents the default values of the ServerConfiguration.

View File

@ -2,7 +2,8 @@
host: 127.0.0.1
port: 9091
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080/
totp:

View File

@ -2,7 +2,8 @@
host: 127.0.0.1
port: 9091
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080/
totp:

View File

@ -1,7 +1,8 @@
---
host: 0.0.0.0
port: 9091
log_level: debug
logging:
level: debug
jwt_secret: RUtG9TnbXrOl1XLLmDgySw1DGgx9QcrtepIf1uDDBlBVKFZxkVBruYKBi32PvaU

View File

@ -3,7 +3,8 @@ host: 127.0.0.1
port: 9091
jwt_secret: secret_from_config
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080/
totp:

View File

@ -50,7 +50,11 @@ func IsNetworkValid(network string) (isValid bool) {
}
// ValidateAccessControl validates access control configuration.
func ValidateAccessControl(configuration schema.AccessControlConfiguration, validator *schema.StructValidator) {
func ValidateAccessControl(configuration *schema.AccessControlConfiguration, validator *schema.StructValidator) {
if configuration.DefaultPolicy == "" {
configuration.DefaultPolicy = denyPolicy
}
if !IsPolicyValid(configuration.DefaultPolicy) {
validator.Push(fmt.Errorf("'default_policy' must either be 'deny', 'two_factor', 'one_factor' or 'bypass'"))
}

View File

@ -24,7 +24,7 @@ func (suite *AccessControl) SetupTest() {
}
func (suite *AccessControl) TestShouldValidateCompleteConfiguration() {
ValidateAccessControl(suite.configuration, suite.validator)
ValidateAccessControl(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings())
suite.Assert().False(suite.validator.HasErrors())
@ -33,7 +33,7 @@ func (suite *AccessControl) TestShouldValidateCompleteConfiguration() {
func (suite *AccessControl) TestShouldRaiseErrorInvalidDefaultPolicy() {
suite.configuration.DefaultPolicy = testInvalidPolicy
ValidateAccessControl(suite.configuration, suite.validator)
ValidateAccessControl(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings())
suite.Require().Len(suite.validator.Errors(), 1)
@ -49,7 +49,7 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidNetworkGroupNetwork() {
},
}
ValidateAccessControl(suite.configuration, suite.validator)
ValidateAccessControl(&suite.configuration, suite.validator)
suite.Assert().False(suite.validator.HasWarnings())
suite.Require().Len(suite.validator.Errors(), 1)

View File

@ -9,7 +9,6 @@ import (
)
var defaultPort = 9091
var defaultLogLevel = "info"
// ValidateConfiguration and adapt the configuration read from file.
//nolint:gocyclo // This function is likely to always have lots of if/else statements, as long as we keep the flow clean it should be understandable.
@ -37,10 +36,6 @@ func ValidateConfiguration(configuration *schema.Configuration, validator *schem
}
}
if configuration.LogLevel == "" {
configuration.LogLevel = defaultLogLevel
}
if configuration.JWTSecret == "" {
validator.Push(fmt.Errorf("Provide a JWT secret using \"jwt_secret\" key"))
}
@ -52,25 +47,19 @@ func ValidateConfiguration(configuration *schema.Configuration, validator *schem
}
}
if configuration.Theme == "" {
configuration.Theme = "light"
}
ValidateTheme(configuration, validator)
if configuration.TOTP == nil {
configuration.TOTP = &schema.DefaultTOTPConfiguration
}
ValidateLogging(configuration, validator)
ValidateTOTP(configuration.TOTP, validator)
ValidateAuthenticationBackend(&configuration.AuthenticationBackend, validator)
if configuration.AccessControl.DefaultPolicy == "" {
configuration.AccessControl.DefaultPolicy = denyPolicy
}
ValidateAccessControl(configuration.AccessControl, validator)
ValidateAccessControl(&configuration.AccessControl, validator)
ValidateRules(configuration.AccessControl, validator)

View File

@ -14,8 +14,8 @@ func newDefaultConfig() schema.Configuration {
config := schema.Configuration{}
config.Host = "127.0.0.1"
config.Port = 9090
config.LogLevel = "info"
config.LogFormat = "text"
config.Logging.Level = "info"
config.Logging.Format = "text"
config.JWTSecret = testJWTSecret
config.AuthenticationBackend.File = &schema.FileAuthenticationBackendConfiguration{
Path: "/a/path",
@ -48,7 +48,7 @@ func TestShouldNotUpdateConfig(t *testing.T) {
require.Len(t, validator.Errors(), 0)
assert.Equal(t, 9090, config.Port)
assert.Equal(t, "info", config.LogLevel)
assert.Equal(t, "info", config.Logging.Level)
}
func TestShouldValidateAndUpdatePort(t *testing.T) {
@ -73,17 +73,6 @@ func TestShouldValidateAndUpdateHost(t *testing.T) {
assert.Equal(t, "0.0.0.0", config.Host)
}
func TestShouldValidateAndUpdateLogsLevel(t *testing.T) {
validator := schema.NewStructValidator()
config := newDefaultConfig()
config.LogLevel = ""
ValidateConfiguration(&config, validator)
require.Len(t, validator.Errors(), 0)
assert.Equal(t, "info", config.LogLevel)
}
func TestShouldEnsureNotifierConfigIsProvided(t *testing.T) {
validator := schema.NewStructValidator()
config := newDefaultConfig()

View File

@ -1,11 +1,16 @@
package validator
const (
errFmtDeprecatedConfigurationKey = "[DEPRECATED] The %s configuration option is deprecated and will be " +
"removed in %s, please use %s instead"
errFmtReplacedConfigurationKey = "invalid configuration key '%s' was replaced by '%s'"
errFmtLoggingLevelInvalid = "the log level '%s' is invalid, must be one of: %s"
errFmtSessionSecretRedisProvider = "The session secret must be set when using the %s session provider"
errFmtSessionRedisPortRange = "The port must be between 1 and 65535 for the %s session provider"
errFmtSessionRedisHostRequired = "The host must be provided when using the %s session provider"
errFmtSessionRedisHostOrNodesRequired = "Either the host or a node must be provided when using the %s session provider"
errFmtReplacedConfigurationKey = "invalid configuration key '%s' was replaced by '%s'"
errOAuthOIDCServerClientRedirectURIFmt = "OIDC Server Client redirect URI %s has an invalid scheme %s, should be http or https"
errOAuthOIDCServerClientRedirectURICantBeParsedFmt = "OIDC Client with ID '%s' has an invalid redirect URI '%s' could not be parsed: %v"
@ -43,6 +48,7 @@ const (
"https://www.authelia.com/docs/configuration/access-control.html#combining-subjects-and-the-bypass-policy"
)
var validLoggingLevels = []string{"trace", "debug", "info", "warn", "error"}
var validRequestMethods = []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "TRACE", "CONNECT", "OPTIONS"}
// SecretNames contains a map of secret names.
@ -66,19 +72,30 @@ var validKeys = []string{
// Root Keys.
"host",
"port",
"log_level",
"log_format",
"log_file_path",
"default_redirection_url",
"theme",
"tls_key",
"tls_cert",
"certificates_directory",
// Logging keys.
"logging.level",
"logging.format",
"logging.file_path",
"logging.keep_stdout",
// TODO: DEPRECATED START. Remove in 4.33.0.
"log_level",
"log_format",
"log_file_path",
// TODO: DEPRECATED END. Remove in 4.33.0.
// Server Keys.
"server.read_buffer_size",
"server.write_buffer_size",
"server.path",
"server.enable_pprof",
"server.enable_expvars",
// TOTP Keys.
"totp.issuer",
@ -200,8 +217,8 @@ var replacedKeys = map[string]string{
"authentication_backend.ldap.skip_verify": "authentication_backend.ldap.tls.skip_verify",
"authentication_backend.ldap.minimum_tls_version": "authentication_backend.ldap.tls.minimum_version",
"notifier.smtp.disable_verify_cert": "notifier.smtp.tls.skip_verify",
"logs_file_path": "log_file",
"logs_level": "log_level",
"logs_file_path": "logging.file_path",
"logs_level": "logging.level",
}
var specificErrorKeys = map[string]string{

View File

@ -106,8 +106,8 @@ func TestReplacedErrors(t *testing.T) {
assert.EqualError(t, errs[0], fmt.Sprintf(errFmtReplacedConfigurationKey, "authentication_backend.ldap.skip_verify", "authentication_backend.ldap.tls.skip_verify"))
assert.EqualError(t, errs[1], fmt.Sprintf(errFmtReplacedConfigurationKey, "authentication_backend.ldap.minimum_tls_version", "authentication_backend.ldap.tls.minimum_version"))
assert.EqualError(t, errs[2], fmt.Sprintf(errFmtReplacedConfigurationKey, "notifier.smtp.disable_verify_cert", "notifier.smtp.tls.skip_verify"))
assert.EqualError(t, errs[3], fmt.Sprintf(errFmtReplacedConfigurationKey, "logs_file_path", "log_file"))
assert.EqualError(t, errs[4], fmt.Sprintf(errFmtReplacedConfigurationKey, "logs_level", "log_level"))
assert.EqualError(t, errs[3], fmt.Sprintf(errFmtReplacedConfigurationKey, "logs_file_path", "logging.file_path"))
assert.EqualError(t, errs[4], fmt.Sprintf(errFmtReplacedConfigurationKey, "logs_level", "logging.level"))
}
func TestSecretKeysDontRaiseErrors(t *testing.T) {

View File

@ -0,0 +1,53 @@
package validator
import (
"fmt"
"strings"
"github.com/authelia/authelia/internal/configuration/schema"
"github.com/authelia/authelia/internal/utils"
)
// ValidateLogging validates the logging configuration.
func ValidateLogging(configuration *schema.Configuration, validator *schema.StructValidator) {
applyDeprecatedLoggingConfiguration(configuration, validator) // TODO: DEPRECATED LINE. Remove in 4.33.0.
if configuration.Logging.Level == "" {
configuration.Logging.Level = schema.DefaultLoggingConfiguration.Level
}
if configuration.Logging.Format == "" {
configuration.Logging.Format = schema.DefaultLoggingConfiguration.Format
}
if !utils.IsStringInSlice(configuration.Logging.Level, validLoggingLevels) {
validator.Push(fmt.Errorf(errFmtLoggingLevelInvalid, configuration.Logging.Level, strings.Join(validLoggingLevels, ", ")))
}
}
// TODO: DEPRECATED FUNCTION. Remove in 4.33.0.
func applyDeprecatedLoggingConfiguration(configuration *schema.Configuration, validator *schema.StructValidator) {
if configuration.LogLevel != "" {
validator.PushWarning(fmt.Errorf(errFmtDeprecatedConfigurationKey, "log_level", "4.33.0", "logging.level"))
if configuration.Logging.Level == "" {
configuration.Logging.Level = configuration.LogLevel
}
}
if configuration.LogFormat != "" {
validator.PushWarning(fmt.Errorf(errFmtDeprecatedConfigurationKey, "log_format", "4.33.0", "logging.format"))
if configuration.Logging.Format == "" {
configuration.Logging.Format = configuration.LogFormat
}
}
if configuration.LogFilePath != "" {
validator.PushWarning(fmt.Errorf(errFmtDeprecatedConfigurationKey, "log_file_path", "4.33.0", "logging.file_path"))
if configuration.Logging.FilePath == "" {
configuration.Logging.FilePath = configuration.LogFilePath
}
}
}

View File

@ -0,0 +1,111 @@
package validator
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/authelia/authelia/internal/configuration/schema"
)
func TestShouldSetDefaultLoggingValues(t *testing.T) {
config := &schema.Configuration{}
validator := schema.NewStructValidator()
ValidateLogging(config, validator)
assert.Len(t, validator.Warnings(), 0)
assert.Len(t, validator.Errors(), 0)
require.NotNil(t, config.Logging.KeepStdout)
assert.Equal(t, "", config.LogLevel)
assert.Equal(t, "", config.LogFormat)
assert.Equal(t, "", config.LogFilePath)
assert.Equal(t, "info", config.Logging.Level)
assert.Equal(t, "text", config.Logging.Format)
assert.Equal(t, "", config.Logging.FilePath)
}
func TestShouldRaiseErrorOnInvalidLoggingLevel(t *testing.T) {
config := &schema.Configuration{
Logging: schema.LoggingConfiguration{
Level: "TRACE",
},
}
validator := schema.NewStructValidator()
ValidateLogging(config, validator)
assert.Len(t, validator.Warnings(), 0)
require.Len(t, validator.Errors(), 1)
assert.EqualError(t, validator.Errors()[0], "the log level 'TRACE' is invalid, must be one of: trace, debug, info, warn, error")
}
// TODO: DEPRECATED TEST. Remove in 4.33.0.
func TestShouldMigrateDeprecatedLoggingConfig(t *testing.T) {
config := &schema.Configuration{
LogLevel: "trace",
LogFormat: "json",
LogFilePath: "/a/b/c",
}
validator := schema.NewStructValidator()
ValidateLogging(config, validator)
assert.Len(t, validator.Errors(), 0)
require.Len(t, validator.Warnings(), 3)
require.NotNil(t, config.Logging.KeepStdout)
assert.Equal(t, "trace", config.LogLevel)
assert.Equal(t, "json", config.LogFormat)
assert.Equal(t, "/a/b/c", config.LogFilePath)
assert.Equal(t, "trace", config.Logging.Level)
assert.Equal(t, "json", config.Logging.Format)
assert.Equal(t, "/a/b/c", config.Logging.FilePath)
assert.EqualError(t, validator.Warnings()[0], fmt.Sprintf(errFmtDeprecatedConfigurationKey, "log_level", "4.33.0", "logging.level"))
assert.EqualError(t, validator.Warnings()[1], fmt.Sprintf(errFmtDeprecatedConfigurationKey, "log_format", "4.33.0", "logging.format"))
assert.EqualError(t, validator.Warnings()[2], fmt.Sprintf(errFmtDeprecatedConfigurationKey, "log_file_path", "4.33.0", "logging.file_path"))
}
func TestShouldRaiseErrorsAndNotOverwriteConfigurationWhenUsingDeprecatedLoggingConfig(t *testing.T) {
config := &schema.Configuration{
Logging: schema.LoggingConfiguration{
Level: "info",
Format: "text",
FilePath: "/x/y/z",
KeepStdout: true,
},
LogLevel: "debug",
LogFormat: "json",
LogFilePath: "/a/b/c",
}
validator := schema.NewStructValidator()
ValidateLogging(config, validator)
require.NotNil(t, config.Logging.KeepStdout)
assert.Equal(t, "info", config.Logging.Level)
assert.Equal(t, "text", config.Logging.Format)
assert.True(t, config.Logging.KeepStdout)
assert.Equal(t, "/x/y/z", config.Logging.FilePath)
assert.Len(t, validator.Errors(), 0)
require.Len(t, validator.Warnings(), 3)
assert.EqualError(t, validator.Warnings()[0], fmt.Sprintf(errFmtDeprecatedConfigurationKey, "log_level", "4.33.0", "logging.level"))
assert.EqualError(t, validator.Warnings()[1], fmt.Sprintf(errFmtDeprecatedConfigurationKey, "log_format", "4.33.0", "logging.format"))
assert.EqualError(t, validator.Warnings()[2], fmt.Sprintf(errFmtDeprecatedConfigurationKey, "log_file_path", "4.33.0", "logging.file_path"))
}

View File

@ -9,6 +9,10 @@ import (
// ValidateTheme validates and update Theme configuration.
func ValidateTheme(configuration *schema.Configuration, validator *schema.StructValidator) {
if configuration.Theme == "" {
configuration.Theme = "light"
}
validThemes := regexp.MustCompile("light|dark|grey")
if !validThemes.MatchString(configuration.Theme) {
validator.Push(fmt.Errorf("Theme: %s is not valid, valid themes are: \"light\", \"dark\" or \"grey\"", configuration.Theme))

View File

@ -339,7 +339,7 @@ func verifySessionHasUpToDateProfile(ctx *middlewares.AutheliaCtx, targetURL *ur
}
} else {
ctx.Logger.Debugf("Updated profile detected for %s.", userSession.Username)
if ctx.Configuration.LogLevel == "trace" {
if ctx.Configuration.Logging.Level == "trace" {
generateVerifySessionHasUpToDateProfileTraceLogs(ctx, userSession, details)
}
userSession.Emails = details.Emails

View File

@ -127,9 +127,11 @@ func registerRoutes(configuration schema.Configuration, providers middlewares.Pr
middlewares.RequireFirstFactor(handlers.SecondFactorDuoPost(duoAPI))))
}
// If trace is set, enable pprofhandler and expvarhandler.
if configuration.LogLevel == "trace" {
if configuration.Server.EnablePprof {
r.GET("/debug/pprof/{name?}", pprofhandler.PprofHandler)
}
if configuration.Server.EnableExpvars {
r.GET("/debug/vars", expvarhandler.ExpvarHandler)
}

View File

@ -9,7 +9,8 @@ tls_key: /config/ssl/key.pem
theme: grey
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080/

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
jwt_secret: unsecure_secret

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
jwt_secret: unsecure_secret

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080/

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: trace
logging:
level: trace
default_redirection_url: https://home.example.com:8080/

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
jwt_secret: unsecure_secret

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
jwt_secret: unsecure_secret

View File

@ -9,7 +9,8 @@ tls_key: /config/ssl/key.pem
theme: dark
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080/

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080/

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080/

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
jwt_secret: unsecure_password

View File

@ -3,7 +3,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
jwt_secret: unsecure_secret

View File

@ -3,7 +3,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
jwt_secret: unsecure_secret

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080/

View File

@ -10,7 +10,8 @@ tls_key: /config/ssl/key.pem
server:
path: auth
log_level: debug
logging:
level: debug
jwt_secret: unsecure_secret

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080/

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
jwt_secret: unsecure_secret

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
authentication_backend:
file:

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
jwt_secret: unsecure_secret

View File

@ -7,7 +7,8 @@ port: 9091
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
jwt_secret: unsecure_secret

View File

@ -7,7 +7,8 @@ port: 443
tls_cert: /config/ssl/cert.pem
tls_key: /config/ssl/key.pem
log_level: debug
logging:
level: debug
default_redirection_url: https://home.example.com:8080

View File

@ -19,9 +19,9 @@ func IsStringAlphaNumeric(input string) bool {
}
// IsStringInSlice checks if a single string is in a slice of strings.
func IsStringInSlice(a string, slice []string) (inSlice bool) {
for _, b := range slice {
if b == a {
func IsStringInSlice(needle string, haystack []string) (inSlice bool) {
for _, b := range haystack {
if b == needle {
return true
}
}
@ -30,9 +30,9 @@ func IsStringInSlice(a string, slice []string) (inSlice bool) {
}
// IsStringInSliceFold checks if a single string is in a slice of strings but uses strings.EqualFold to compare them.
func IsStringInSliceFold(a string, slice []string) (inSlice bool) {
for _, b := range slice {
if strings.EqualFold(b, a) {
func IsStringInSliceFold(needle string, haystack []string) (inSlice bool) {
for _, b := range haystack {
if strings.EqualFold(b, needle) {
return true
}
}
@ -41,9 +41,9 @@ func IsStringInSliceFold(a string, slice []string) (inSlice bool) {
}
// IsStringInSliceContains checks if a single string is in an array of strings.
func IsStringInSliceContains(a string, list []string) (inSlice bool) {
for _, b := range list {
if strings.Contains(a, b) {
func IsStringInSliceContains(needle string, haystack []string) (inSlice bool) {
for _, b := range haystack {
if strings.Contains(needle, b) {
return true
}
}