Skip to content

Commit

Permalink
config: validate log levels (#4367)
Browse files Browse the repository at this point in the history
* config: validate log levels

* fix SetLevel

* document unset, merge warn/warning
  • Loading branch information
calebdoxsey committed Jul 17, 2023
1 parent a138859 commit 78e7a3e
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 28 deletions.
4 changes: 2 additions & 2 deletions config/envoyconfig/envoyconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion config/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 107 additions & 0 deletions config/log_level.go
Original file line number Diff line number Diff line change
@@ -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))
}
}
25 changes: 20 additions & 5 deletions config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
24 changes: 12 additions & 12 deletions internal/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
case zerolog.DebugLevel, zerolog.TraceLevel:
zapLevel.SetLevel(zapcore.DebugLevel)
case zerolog.WarnLevel:
zapLevel.SetLevel(zapcore.WarnLevel)
case "error":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
case zerolog.ErrorLevel:
zapLevel.SetLevel(zapcore.ErrorLevel)
case zerolog.FatalLevel:
zapLevel.SetLevel(zapcore.FatalLevel)
case zerolog.PanicLevel:
zapLevel.SetLevel(zapcore.PanicLevel)
default:
zerolog.SetGlobalLevel(zerolog.DebugLevel)
zapLevel.SetLevel(zapcore.DebugLevel)
zapLevel.SetLevel(zapcore.InfoLevel)
}
}

Expand Down
8 changes: 4 additions & 4 deletions internal/log/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions pkg/envoy/envoy.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const (

type serverOptions struct {
services string
logLevel string
logLevel config.LogLevel
}

// A Server is a pomerium proxy implemented via envoy.
Expand Down Expand Up @@ -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{})) {
Expand All @@ -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",
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/envoy/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 78e7a3e

Please sign in to comment.