Skip to content

Commit

Permalink
log: add access log level (#231)
Browse files Browse the repository at this point in the history
Adds support for configuring the access log level.

This also changes the purpose of access-log.disable to block all access
logs.
  • Loading branch information
andydunstall authored Feb 23, 2025
1 parent 7daf410 commit ee641c9
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 121 deletions.
3 changes: 3 additions & 0 deletions cli/agent/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ Examples:
if conf.Listeners[i].Protocol == "" {
conf.Listeners[i].Protocol = config.ListenerProtocolHTTP
}
if conf.Listeners[i].AccessLog.Level == "" {
conf.Listeners[i].AccessLog.Level = "info"
}
}

if err := conf.Validate(); err != nil {
Expand Down
7 changes: 4 additions & 3 deletions cli/agent/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ Examples:
`,
}

accessLog := log.AccessLogConfig{
accessLogConfig := log.AccessLogConfig{
Level: "info",
Disable: false,
}
flags := cmd.Flags()
accessLog.RegisterFlags(flags, "")
accessLogConfig.RegisterFlags(flags, "")

var timeout time.Duration
flags.DurationVar(
Expand All @@ -59,7 +60,7 @@ Timeout forwarding incoming HTTP requests to the upstream.`,
EndpointID: args[0],
Addr: args[1],
Protocol: config.ListenerProtocolHTTP,
AccessLog: accessLog,
AccessLog: accessLogConfig,
Timeout: timeout,
}}

Expand Down
26 changes: 18 additions & 8 deletions cli/agent/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,25 @@ Examples:
`,
}

var disableAccessLogging bool
accessLogConfig := log.AccessLogConfig{
Level: "info",
Disable: false,
}
cmd.Flags().StringVar(
&accessLogConfig.Level,
"access-log.level",
accessLogConfig.Level,
`
The record log level for audit log entries.
The available levels are 'debug', 'info', 'warn' and 'error'.`,
)
cmd.Flags().BoolVar(
&disableAccessLogging,
&accessLogConfig.Disable,
"access-log.disable",
false,
accessLogConfig.Disable,
`
Disables logging all incoming connections as 'info' logs. For more options, use a configuration file.`,
Disable the access log, so requests will not be logged.`,
)

var timeout time.Duration
Expand All @@ -59,10 +71,8 @@ Timeout connecting to the upstream.`,
EndpointID: args[0],
Addr: args[1],
Protocol: config.ListenerProtocolTCP,
AccessLog: log.AccessLogConfig{
Disable: disableAccessLogging,
},
Timeout: timeout,
AccessLog: accessLogConfig,
Timeout: timeout,
}}

var err error
Expand Down
80 changes: 54 additions & 26 deletions pkg/log/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func (c *Config) Validate() error {
if c.Level == "" {
return fmt.Errorf("missing level")
}
if _, err := zapLevelFromString(c.Level); err != nil {
if _, err := ZapLevelFromString(c.Level); err != nil {
return err
}
return nil
Expand Down Expand Up @@ -52,51 +52,70 @@ Such as you can enable 'gossip' logs with '--log.subsystems gossip'.`,
}

type AccessLogHeaderConfig struct {
// Prevent these headers from being logged.
// You can only define one of Allowlist or Blocklist.
Blocklist []string `json:"blocklist" yaml:"blocklist"`

// Log only these headers.
// You can only define one of Allowlist or Blocklist.
Allowlist []string `json:"allowlist" yaml:"allowlist"`
// BlockList contains headers that will be redacted from the audit log.
//
// You must only define one of AllowList or BlockList.
BlockList []string `json:"block_list" yaml:"block_list"`

// AllowList contains the ONLY headers that will be logged.
//
// You must only define one of AllowList or BlockList.
AllowList []string `json:"allow_list" yaml:"allow_list"`
}

func (c *AccessLogHeaderConfig) Validate() error {
if len(c.Allowlist) > 0 && len(c.Blocklist) > 0 {
return fmt.Errorf("cannot define both allowlist and blocklist")
if len(c.AllowList) > 0 && len(c.BlockList) > 0 {
return fmt.Errorf("cannot define both allow list and block list")
}

return nil
}

func (c *AccessLogHeaderConfig) RegisterFlags(fs *pflag.FlagSet, prefix string) {
fs.StringSliceVar(
&c.Allowlist,
prefix+"allowlist",
c.Allowlist,
&c.BlockList,
prefix+"block-list",
c.BlockList,
`
Log only these headers`,
Block these headers from being logged.
You must only define one of block list and allow list.`,
)
fs.StringSliceVar(
&c.Blocklist,
prefix+"blocklist",
c.Blocklist,
&c.AllowList,
prefix+"allow-list",
c.AllowList,
`
Block these headers from being logged`,
The ONLY headers that will be logged.
You must only define one of block list and allow list.`,
)
}

type AccessLogConfig struct {
// If disabled, logs will be emitted with the 'debug' log level,
// while respecting the header allow and block lists.
Disable bool `json:"disable" yaml:"disable"`
// Level is the record log level for audit log entries. Either 'debug',
// 'info', 'warn' or 'error'.
Level string `json:"level" yaml:"level"`

RequestHeaders AccessLogHeaderConfig `json:"request_headers" yaml:"request_headers"`

ResponseHeaders AccessLogHeaderConfig `json:"response_headers" yaml:"response_headers"`

// Disable disables the access log, so requests will not be logged.
Disable bool `json:"disable" yaml:"disable"`
}

func (c *AccessLogConfig) Validate() error {
if c.Disable {
return nil
}

if c.Level == "" {
return fmt.Errorf("missing level")
}
if _, err := ZapLevelFromString(c.Level); err != nil {
return err
}

if err := c.RequestHeaders.Validate(); err != nil {
return fmt.Errorf("request headers: %w", err)
}
Expand All @@ -113,13 +132,22 @@ func (c *AccessLogConfig) RegisterFlags(fs *pflag.FlagSet, prefix string) {
} else {
prefix = "access-log."
}
fs.StringVar(
&c.Level,
prefix+"level",
c.Level,
`
The record log level for audit log entries.
The available levels are 'debug', 'info', 'warn' and 'error'.`,
)
c.RequestHeaders.RegisterFlags(fs, prefix+"request-headers.")
c.ResponseHeaders.RegisterFlags(fs, prefix+"response-headers.")
fs.BoolVar(
&c.Disable,
prefix+"disable",
false,
c.Disable,
`
If Access logging is disabled`,
Disable the access log, so requests will not be logged.`,
)
c.RequestHeaders.RegisterFlags(fs, prefix+"request-headers.")
c.ResponseHeaders.RegisterFlags(fs, prefix+"response-headers.")
}
26 changes: 14 additions & 12 deletions pkg/log/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type Logger interface {
Info(msg string, fields ...zap.Field)
Warn(msg string, fields ...zap.Field)
Error(msg string, fields ...zap.Field)
Log(level zapcore.Level, msg string, fields ...zap.Field)
Sync() error
// StdLogger returns a standard library log.Logger that logs records using
// with the given level.
Expand All @@ -58,7 +59,7 @@ type logger struct {
// NewLogger creates a new logger filtering using the given log level and
// enabled subsystems.
func NewLogger(lvl string, enabledSubsystems []string) (Logger, error) {
zapLevel, err := zapLevelFromString(lvl)
zapLevel, err := ZapLevelFromString(lvl)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -114,25 +115,23 @@ func (l *logger) With(fields ...zap.Field) Logger {
}

func (l *logger) Debug(msg string, fields ...zap.Field) {
if ce := l.check(zap.DebugLevel, msg); ce != nil {
ce.Write(fields...)
}
l.Log(zap.DebugLevel, msg, fields...)
}

func (l *logger) Info(msg string, fields ...zap.Field) {
if ce := l.check(zap.InfoLevel, msg); ce != nil {
ce.Write(fields...)
}
l.Log(zap.InfoLevel, msg, fields...)
}

func (l *logger) Warn(msg string, fields ...zap.Field) {
if ce := l.check(zap.WarnLevel, msg); ce != nil {
ce.Write(fields...)
}
l.Log(zap.WarnLevel, msg, fields...)
}

func (l *logger) Error(msg string, fields ...zap.Field) {
if ce := l.check(zap.ErrorLevel, msg); ce != nil {
l.Log(zap.ErrorLevel, msg, fields...)
}

func (l *logger) Log(level zapcore.Level, msg string, fields ...zap.Field) {
if ce := l.check(level, msg); ce != nil {
ce.Write(fields...)
}
}
Expand Down Expand Up @@ -214,6 +213,9 @@ func (l *nopLogger) Warn(_ string, _ ...zap.Field) {
func (l *nopLogger) Error(_ string, _ ...zap.Field) {
}

func (l *nopLogger) Log(_ zapcore.Level, _ string, _ ...zap.Field) {
}

func (l *nopLogger) Sync() error {
return nil
}
Expand All @@ -234,7 +236,7 @@ func subsystemMatch(subsystem string, enabled []string) bool {
return false
}

func zapLevelFromString(s string) (zapcore.Level, error) {
func ZapLevelFromString(s string) (zapcore.Level, error) {
switch s {
case "debug":
return zap.DebugLevel, nil
Expand Down
Loading

0 comments on commit ee641c9

Please sign in to comment.