config: validate log levels (#4367)

* config: validate log levels

* fix SetLevel

* document unset, merge warn/warning
This commit is contained in:
Caleb Doxsey 2023-07-17 16:41:48 -06:00 committed by GitHub
parent a1388592d8
commit 78e7a3e7d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 152 additions and 30 deletions

View file

@ -82,11 +82,11 @@ func buildAccessLogs(options *config.Options) []*envoy_config_accesslog_v3.Acces
lvl = options.LogLevel
}
if lvl == "" {
lvl = "debug"
lvl = config.LogLevelDebug
}
switch lvl {
case "trace", "debug", "info":
case config.LogLevelTrace, config.LogLevelDebug, config.LogLevelInfo:
default:
// don't log access requests for levels > info
return nil

View file

@ -41,6 +41,6 @@ func (mgr *LogManager) OnConfigChange(_ context.Context, cfg *Config) {
}
if cfg.Options.LogLevel != "" {
log.SetLevel(cfg.Options.LogLevel)
log.SetLevel(cfg.Options.LogLevel.ToZerolog())
}
}

107
config/log_level.go Normal file
View file

@ -0,0 +1,107 @@
package config
import (
"fmt"
"github.com/rs/zerolog"
)
// A LogLevel represents a logging level.
type LogLevel string
// Known log levels.
const (
LogLevelUnset LogLevel = "" // defaults to info
LogLevelTrace LogLevel = "trace"
LogLevelDebug LogLevel = "debug"
LogLevelInfo LogLevel = "info"
LogLevelWarn LogLevel = "warn"
LogLevelWarning LogLevel = "warning"
LogLevelError LogLevel = "error"
LogLevelCritical LogLevel = "critical"
LogLevelFatal LogLevel = "fatal"
LogLevelPanic LogLevel = "panic"
LogLevelOff LogLevel = "off"
LogLevelNone LogLevel = "none"
LogLevelDisabled LogLevel = "disabled"
)
// AllLogLevels are all of the known log levels.
var AllLogLevels = [...]LogLevel{
LogLevelUnset,
LogLevelTrace,
LogLevelDebug,
LogLevelInfo,
LogLevelWarn,
LogLevelWarning,
LogLevelError,
LogLevelCritical,
LogLevelFatal,
LogLevelPanic,
LogLevelOff,
LogLevelNone,
LogLevelDisabled,
}
var logLevelLookup = func() map[LogLevel]struct{} {
m := map[LogLevel]struct{}{}
for _, lvl := range AllLogLevels {
m[lvl] = struct{}{}
}
return m
}()
// ValidateLogLevel validates that a log level is one of the known log levels.
func ValidateLogLevel(lvl LogLevel) error {
_, ok := logLevelLookup[lvl]
if !ok {
return fmt.Errorf("unknown log level: %s", lvl)
}
return nil
}
// ToZerolog converts the log level to a level zerolog expects
func (lvl LogLevel) ToZerolog() zerolog.Level {
switch lvl {
case LogLevelTrace:
return zerolog.TraceLevel
case LogLevelDebug:
return zerolog.DebugLevel
case LogLevelInfo, LogLevelUnset:
return zerolog.InfoLevel
case LogLevelWarn, LogLevelWarning:
return zerolog.WarnLevel
case LogLevelError:
return zerolog.ErrorLevel
case LogLevelCritical, LogLevelFatal:
return zerolog.FatalLevel
case LogLevelPanic:
return zerolog.PanicLevel
case LogLevelOff, LogLevelNone, LogLevelDisabled:
return zerolog.Disabled
default:
panic(fmt.Sprintf("unknown log level: %s", lvl))
}
}
// ToEnvoy converts the log level to a string envoy expects.
func (lvl LogLevel) ToEnvoy() string {
switch lvl {
case LogLevelTrace:
return "trace"
case LogLevelDebug:
return "debug"
case LogLevelInfo, LogLevelUnset:
return "info"
case LogLevelWarn, LogLevelWarning:
return "warn"
case LogLevelError:
return "error"
case LogLevelCritical, LogLevelFatal, LogLevelPanic:
return "critical"
case LogLevelOff, LogLevelNone, LogLevelDisabled:
return "off"
default:
panic(fmt.Sprintf("unknown log level: %s", lvl))
}
}

View file

@ -62,11 +62,11 @@ type Options struct {
// LogLevel sets the global override for log level. All Loggers will use at least this value.
// Possible options are "info","warn","debug" and "error". Defaults to "info".
LogLevel string `mapstructure:"log_level" yaml:"log_level,omitempty"`
LogLevel LogLevel `mapstructure:"log_level" yaml:"log_level,omitempty"`
// ProxyLogLevel sets the log level for the proxy service.
// Possible options are "info","warn", and "error". Defaults to the value of `LogLevel`.
ProxyLogLevel string `mapstructure:"proxy_log_level" yaml:"proxy_log_level,omitempty"`
ProxyLogLevel LogLevel `mapstructure:"proxy_log_level" yaml:"proxy_log_level,omitempty"`
// SharedKey is the shared secret authorization key used to mutually authenticate
// requests between services.
@ -302,7 +302,7 @@ type certificateFilePair struct {
// DefaultOptions are the default configuration options for pomerium
var defaultOptions = Options{
Debug: false,
LogLevel: "info",
LogLevel: LogLevelInfo,
Services: "all",
CookieHTTPOnly: true,
CookieSecure: true,
@ -741,6 +741,14 @@ func (o *Options) Validate() error {
return fmt.Errorf("config: invalid cookie_same_site: %w", err)
}
if err := ValidateLogLevel(o.LogLevel); err != nil {
return fmt.Errorf("config: invalid log_level: %w", err)
}
if err := ValidateLogLevel(o.ProxyLogLevel); err != nil {
return fmt.Errorf("config: invalid proxy_log_level: %w", err)
}
return nil
}
@ -1320,8 +1328,8 @@ func (o *Options) ApplySettings(ctx context.Context, certsIndex *cryptutil.Certi
set(&o.InstallationID, settings.InstallationId)
set(&o.Debug, settings.Debug)
set(&o.LogLevel, settings.LogLevel)
set(&o.ProxyLogLevel, settings.ProxyLogLevel)
setLogLevel(&o.LogLevel, settings.LogLevel)
setLogLevel(&o.ProxyLogLevel, settings.ProxyLogLevel)
set(&o.SharedKey, settings.SharedSecret)
set(&o.Services, settings.Services)
set(&o.Addr, settings.Address)
@ -1474,6 +1482,13 @@ func setDuration(dst *time.Duration, src *durationpb.Duration) {
*dst = src.AsDuration()
}
func setLogLevel(dst *LogLevel, src *string) {
if src == nil {
return
}
*dst = LogLevel(*src)
}
func setOptional[T any](dst **T, src *T) {
if src == nil {
return

View file

@ -64,22 +64,22 @@ func ZapLogger() *zap.Logger {
return zapLogger.Load()
}
// SetLevel sets the minimum global log level. Options are 'debug' 'info' 'warn' and 'error'.
// Defaults to 'debug'
func SetLevel(level string) {
// SetLevel sets the minimum global log level.
func SetLevel(level zerolog.Level) {
zerolog.SetGlobalLevel(level)
switch level {
case "info":
zerolog.SetGlobalLevel(zerolog.InfoLevel)
zapLevel.SetLevel(zapcore.InfoLevel)
case "warn":
zerolog.SetGlobalLevel(zerolog.WarnLevel)
zapLevel.SetLevel(zapcore.WarnLevel)
case "error":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
zapLevel.SetLevel(zapcore.ErrorLevel)
default:
zerolog.SetGlobalLevel(zerolog.DebugLevel)
case zerolog.DebugLevel, zerolog.TraceLevel:
zapLevel.SetLevel(zapcore.DebugLevel)
case zerolog.WarnLevel:
zapLevel.SetLevel(zapcore.WarnLevel)
case zerolog.ErrorLevel:
zapLevel.SetLevel(zapcore.ErrorLevel)
case zerolog.FatalLevel:
zapLevel.SetLevel(zapcore.FatalLevel)
case zerolog.PanicLevel:
zapLevel.SetLevel(zapcore.PanicLevel)
default:
zapLevel.SetLevel(zapcore.InfoLevel)
}
}

View file

@ -134,19 +134,19 @@ func Example() {
func ExampleSetLevel() {
setup()
log.SetLevel("info")
log.SetLevel(zerolog.InfoLevel)
log.Debug(context.Background()).Msg("Debug")
log.Info(context.Background()).Msg("Debug or Info")
log.SetLevel("warn")
log.SetLevel(zerolog.WarnLevel)
log.Debug(context.Background()).Msg("Debug")
log.Info(context.Background()).Msg("Debug or Info")
log.Warn(context.Background()).Msg("Debug or Info or Warn")
log.SetLevel("error")
log.SetLevel(zerolog.ErrorLevel)
log.Debug(context.Background()).Msg("Debug")
log.Info(context.Background()).Msg("Debug or Info")
log.Warn(context.Background()).Msg("Debug or Info or Warn")
log.Error(context.Background()).Msg("Debug or Info or Warn or Error")
log.SetLevel("default-fall-through")
log.SetLevel(zerolog.DebugLevel)
log.Debug(context.Background()).Msg("Debug")
// Output:

View file

@ -37,7 +37,7 @@ const (
type serverOptions struct {
services string
logLevel string
logLevel config.LogLevel
}
// A Server is a pomerium proxy implemented via envoy.
@ -113,7 +113,7 @@ func (srv *Server) update(ctx context.Context, cfg *config.Config) {
options := serverOptions{
services: cfg.Options.Services,
logLevel: firstNonEmpty(cfg.Options.ProxyLogLevel, cfg.Options.LogLevel, "debug"),
logLevel: firstNonEmpty(cfg.Options.ProxyLogLevel, cfg.Options.LogLevel, config.LogLevelDebug),
}
if cmp.Equal(srv.options, options, cmp.AllowUnexported(serverOptions{})) {
@ -140,7 +140,7 @@ func (srv *Server) run(ctx context.Context, cfg *config.Config) error {
args := []string{
"-c", configFileName,
"--log-level", srv.options.logLevel,
"--log-level", srv.options.logLevel.ToEnvoy(),
"--log-format", "[LOG_FORMAT]%l--%n--%v",
"--log-format-escaped",
}

View file

@ -8,7 +8,7 @@ import (
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
)
func firstNonEmpty(args ...string) string {
func firstNonEmpty[T interface{ ~string }](args ...T) T {
for _, a := range args {
if a != "" {
return a