Skip to content

Commit 98ef5e5

Browse files
committed
config: add more option-ful logging configuration
This moves the logging configuration from the single "level" key to top-level struct like all the new additions. This also does some internal shuffling of types and constants, taking inspiration from the `log/slog` package. Signed-off-by: Hank Donnay <[email protected]>
1 parent 4405fda commit 98ef5e5

File tree

5 files changed

+153
-97
lines changed

5 files changed

+153
-97
lines changed

config/config.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type Config struct {
1919
// environment variable is the recommended way to do that. The release
2020
// container has `/var/run/certs` added to the list already.
2121
TLS *TLS `yaml:"tls,omitempty" json:"tls,omitempty"`
22-
// Sets which mode the clair instance will run.
22+
// Sets which mode the Clair instance will run.
2323
Mode Mode `yaml:"-" json:"-"`
2424
// A string in <host>:<port> format where <host> can be an empty string.
2525
//
@@ -31,15 +31,18 @@ type Config struct {
3131
// exposes Clair's metrics and health endpoints.
3232
IntrospectionAddr string `yaml:"introspection_addr" json:"introspection_addr"`
3333
// Set the logging level.
34-
LogLevel LogLevel `yaml:"log_level" json:"log_level"`
35-
Indexer Indexer `yaml:"indexer,omitempty" json:"indexer,omitempty"`
36-
Matcher Matcher `yaml:"matcher,omitempty" json:"matcher,omitempty"`
37-
Matchers Matchers `yaml:"matchers,omitempty" json:"matchers,omitempty"`
38-
Updaters Updaters `yaml:"updaters,omitempty" json:"updaters,omitempty"`
39-
Notifier Notifier `yaml:"notifier,omitempty" json:"notifier,omitempty"`
40-
Auth Auth `yaml:"auth,omitempty" json:"auth,omitempty"`
41-
Trace Trace `yaml:"trace,omitempty" json:"trace,omitempty"`
42-
Metrics Metrics `yaml:"metrics,omitempty" json:"metrics,omitempty"`
34+
//
35+
// Deprecated: Use the "Logging" member.
36+
LogLevel *LogLevel `yaml:"log_level,omitempty" json:"log_level,omitempty"`
37+
Indexer Indexer `yaml:"indexer,omitempty" json:"indexer,omitempty"`
38+
Matcher Matcher `yaml:"matcher,omitempty" json:"matcher,omitempty"`
39+
Matchers Matchers `yaml:"matchers,omitempty" json:"matchers,omitempty"`
40+
Updaters Updaters `yaml:"updaters,omitempty" json:"updaters,omitempty"`
41+
Notifier Notifier `yaml:"notifier,omitempty" json:"notifier,omitempty"`
42+
Auth Auth `yaml:"auth,omitempty" json:"auth,omitempty"`
43+
Trace Trace `yaml:"trace,omitempty" json:"trace,omitempty"`
44+
Metrics Metrics `yaml:"metrics,omitempty" json:"metrics,omitempty"`
45+
Logging Logging `yaml:"logging,omitempty" json:"logging,omitempty"`
4346
}
4447

4548
func (c *Config) validate(mode Mode) ([]Warning, error) {
@@ -58,6 +61,10 @@ func (c *Config) validate(mode Mode) ([]Warning, error) {
5861
if _, _, err := net.SplitHostPort(c.HTTPListenAddr); err != nil {
5962
return nil, err
6063
}
64+
if c.LogLevel != nil {
65+
c.Logging.Level = *c.LogLevel
66+
}
67+
6168
return c.lint()
6269
}
6370

@@ -74,6 +81,12 @@ func (c *Config) lint() (ws []Warning, err error) {
7481
msg: `introspection address not provided, default will be used`,
7582
})
7683
}
84+
if c.LogLevel != nil {
85+
ws = append(ws, Warning{
86+
path: ".log_level",
87+
msg: `"log_level" is deprecated, use "logging"`,
88+
})
89+
}
7790
return ws, nil
7891
}
7992

config/enums.go

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package config
22

33
import (
4-
"encoding"
5-
"errors"
64
"fmt"
75
"strings"
86
)
@@ -35,62 +33,3 @@ func ParseMode(s string) (Mode, error) {
3533
}
3634
return Mode(-1), fmt.Errorf(`unknown mode %q`, s)
3735
}
38-
39-
// A LogLevel is a log level recognized by Clair.
40-
//
41-
// The zero value is "info".
42-
type LogLevel int
43-
44-
// The recognized log levels, with their string representations as the comments.
45-
//
46-
// NB "Fatal" and "Panic" are not used in clair or claircore, and will result in
47-
// almost no logging.
48-
const (
49-
InfoLog LogLevel = iota // info
50-
DebugColorLog // debug-color
51-
DebugLog // debug
52-
WarnLog // warn
53-
ErrorLog // error
54-
FatalLog // fatal
55-
PanicLog // panic
56-
)
57-
58-
// ParseLogLevel returns the log lever for the given string.
59-
//
60-
// The passed string is case-insensitive.
61-
func ParseLogLevel(s string) (LogLevel, error) {
62-
for i, lim := 0, len(_LogLevel_index); i < lim; i++ {
63-
l := LogLevel(i)
64-
if strings.EqualFold(s, l.String()) {
65-
return l, nil
66-
}
67-
}
68-
return LogLevel(-1), fmt.Errorf(`unknown log level %q`, s)
69-
}
70-
71-
// UnmarshalText implements encoding.TextUnmarshaler.
72-
func (l *LogLevel) UnmarshalText(b []byte) (err error) {
73-
*l, err = ParseLogLevel(string(b))
74-
if err != nil {
75-
return err
76-
}
77-
return nil
78-
}
79-
80-
// MarshalText implements encoding.TextMarshaler.
81-
func (l *LogLevel) MarshalText() ([]byte, error) {
82-
if l == nil {
83-
return nil, errors.New("invalid LogLevel pointer: <nil>")
84-
}
85-
i := *l
86-
if i < 0 || i >= LogLevel(len(_LogLevel_index)-1) {
87-
return nil, fmt.Errorf("invalid LogLevel: %q", l.String())
88-
}
89-
return []byte(_LogLevel_name[_LogLevel_index[i]:_LogLevel_index[i+1]]), nil
90-
}
91-
92-
// Assert LogLevel implements TextUnmarshaler and TextMarshaler.
93-
var (
94-
_ encoding.TextUnmarshaler = (*LogLevel)(nil)
95-
_ encoding.TextMarshaler = (*LogLevel)(nil)
96-
)

config/enums_string.go

Lines changed: 11 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/enums_test.go

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,47 @@
11
package config_test
22

33
import (
4-
"bytes"
54
"testing"
65

76
"github.com/quay/clair/config"
87
)
98

109
func TestEnumMarshal(t *testing.T) {
1110
t.Run("LogLevel", func(t *testing.T) {
12-
tt := [][]byte{
13-
[]byte("info"),
14-
[]byte("debug-color"),
15-
[]byte("debug"),
16-
[]byte("warn"),
17-
[]byte("error"),
18-
[]byte("fatal"),
19-
[]byte("panic"),
11+
type testcase struct {
12+
Level config.LogLevel
13+
String string
14+
}
15+
tt := []testcase{
16+
{Level: config.TraceLog, String: "trace"},
17+
{Level: config.DebugColorLog, String: "debug-color"},
18+
{Level: config.DebugLog, String: "debug"},
19+
{Level: config.InfoLog, String: "info"},
20+
{Level: config.WarnLog, String: "warn"},
21+
{Level: config.ErrorLog, String: "error"},
22+
{Level: config.FatalLog, String: "fatal"},
23+
{Level: config.PanicLog, String: "panic"},
2024
}
2125
t.Run("Marshal", func(t *testing.T) {
22-
for i, want := range tt {
23-
l := config.LogLevel(i)
24-
got, err := l.MarshalText()
26+
for _, tc := range tt {
27+
m, err := tc.Level.MarshalText()
2528
if err != nil {
2629
t.Error(err)
2730
continue
2831
}
29-
if !bytes.Equal(got, want) {
32+
if got, want := string(m), tc.String; got != want {
3033
t.Errorf("got: %q, want: %q", got, want)
3134
}
3235
}
3336
})
3437
t.Run("Unmarshal", func(t *testing.T) {
35-
for want, in := range tt {
36-
var l config.LogLevel
37-
if err := l.UnmarshalText(in); err != nil {
38+
for _, tc := range tt {
39+
var got config.LogLevel
40+
if err := got.UnmarshalText([]byte(tc.String)); err != nil {
3841
t.Error(err)
3942
continue
4043
}
41-
if got := int(l); got != want {
44+
if want := tc.Level; got != want {
4245
t.Errorf("got: %q, want: %q", got, want)
4346
}
4447
}

config/logging.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package config
2+
3+
import (
4+
"encoding"
5+
"errors"
6+
"fmt"
7+
"strings"
8+
)
9+
10+
// Logging is all the log configuration.
11+
type Logging struct {
12+
Level LogLevel `yaml:"level,omitempty" json:"level,omitempty"`
13+
OmitTimestamps bool `yaml:"omit_timestamps,omitempty" json:"omit_timestamps,omitempty"`
14+
}
15+
16+
// A LogLevel is a log level recognized by Clair.
17+
//
18+
// The zero value is [InfoLog].
19+
type LogLevel int
20+
21+
// The recognized log levels, with their string representations as the comments.
22+
//
23+
// NB [FatalLog] and [PanicLog] are not used in Clair or Claircore, and will
24+
// result in almost no logging.
25+
const (
26+
TraceLog LogLevel = iota - 3 // trace
27+
DebugColorLog // debug-color
28+
DebugLog // debug
29+
InfoLog // info
30+
WarnLog // warn
31+
ErrorLog // error
32+
FatalLog // fatal
33+
PanicLog // panic
34+
)
35+
36+
// Assert that the zero value is correct:
37+
var _ = [1]struct{}{{}}[InfoLog]
38+
39+
// ParseLogLevel returns the log level for the given string.
40+
//
41+
// The passed string is case-insensitive.
42+
func ParseLogLevel(s string) (LogLevel, error) {
43+
const offset = int(TraceLog)
44+
for i, lim := 0, len(_LogLevel_index); i < lim; i++ {
45+
l := LogLevel(i + offset)
46+
if strings.EqualFold(s, l.String()) {
47+
return l, nil
48+
}
49+
}
50+
return LogLevel(-127), fmt.Errorf(`unknown log level %q`, s)
51+
}
52+
53+
// UnmarshalText implements [encoding.TextUnmarshaler].
54+
func (l *LogLevel) UnmarshalText(b []byte) (err error) {
55+
*l, err = ParseLogLevel(string(b))
56+
if err != nil {
57+
return err
58+
}
59+
return nil
60+
}
61+
62+
// MarshalText implements [encoding.TextMarshaler].
63+
func (l *LogLevel) MarshalText() ([]byte, error) {
64+
const offset = int(TraceLog)
65+
if l == nil {
66+
return nil, errors.New("invalid LogLevel pointer: <nil>")
67+
}
68+
i := int(*l) - offset
69+
if i < 0 || i >= len(_LogLevel_index)-1 {
70+
return nil, fmt.Errorf("invalid LogLevel: %q", l.String())
71+
}
72+
return []byte(_LogLevel_name[_LogLevel_index[i]:_LogLevel_index[i+1]]), nil
73+
}
74+
75+
// Assert LogLevel implements everything that's needed.
76+
var (
77+
_ encoding.TextUnmarshaler = (*LogLevel)(nil)
78+
_ encoding.TextMarshaler = (*LogLevel)(nil)
79+
)
80+
81+
func (l *Logging) validate(mode Mode) ([]Warning, error) {
82+
return l.lint()
83+
}
84+
85+
func (l *Logging) lint() (ws []Warning, _ error) {
86+
if l.Level > ErrorLog {
87+
ws = append(ws, Warning{
88+
path: ".level",
89+
msg: `"fatal" and "panic" levels are not used and will result in almost no logging`,
90+
})
91+
}
92+
if l.Level == DebugColorLog {
93+
ws = append(ws, Warning{
94+
path: ".level",
95+
msg: `"debug-color" is deprecated; it will become an alias for "debug" in a future release`,
96+
})
97+
}
98+
return ws, nil
99+
}

0 commit comments

Comments
 (0)