Skip to content

Commit fbd244a

Browse files
author
Russ Egan
committed
Support levels string in env vars
For backward compatibility with v1, support parsing the environment variable as a levels string if it does not appear to be a JSON object.
1 parent 067d35e commit fbd244a

File tree

3 files changed

+57
-73
lines changed

3 files changed

+57
-73
lines changed

v2/config_from_env.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"log/slog"
88
"os"
9+
"strings"
910
"sync"
1011

1112
"github.com/ansel1/console-slog"
@@ -65,15 +66,31 @@ func MustConfigFromEnv(envvars ...string) {
6566

6667
func UnmarshalEnv(o *HandlerOptions, envvars ...string) error {
6768
for _, v := range envvars {
68-
if configString := os.Getenv(v); configString != "" {
69-
// todo: need to add a branch here to handle when the environment variable is
70-
// set to a raw levels string, and isn't JSON
69+
configString := os.Getenv(v)
70+
if configString == "" {
71+
continue
72+
}
73+
if strings.HasPrefix(configString, "{") {
7174
err := json.Unmarshal([]byte(configString), o)
7275
if err != nil {
7376
return fmt.Errorf("parsing configuration from environment variable %v: %w", v, err)
7477
}
7578
return nil
7679
}
80+
81+
// parse the value like a levels string
82+
var levels Levels
83+
err := levels.UnmarshalText([]byte(configString))
84+
if err != nil {
85+
return fmt.Errorf("parsing levels string from environment variable %v: %w", v, err)
86+
}
87+
opts := HandlerOptions{}
88+
if defLvl, ok := levels["*"]; ok {
89+
opts.Level = defLvl
90+
delete(levels, "*")
91+
}
92+
opts.Levels = levels
93+
*o = opts
7794
}
7895
return nil
7996
}

v2/config_from_env_test.go

Lines changed: 37 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"github.com/stretchr/testify/require"
1717
)
1818

19-
func TestConfig_UnmarshalEnv(t *testing.T) {
19+
func TestUnmarshalEnv(t *testing.T) {
2020
tests := []struct {
2121
name string
2222
env map[string]string
@@ -55,11 +55,32 @@ func TestConfig_UnmarshalEnv(t *testing.T) {
5555
{
5656
name: "parse error",
5757
env: map[string]string{
58-
"FLUME": `not json`,
58+
"FLUME": `{{not json}}`,
5959
},
6060
envvars: DefaultConfigEnvVars,
6161
expectError: "parsing configuration from environment variable FLUME: invalid character",
6262
},
63+
{
64+
name: "levels string error",
65+
env: map[string]string{
66+
"FLUME": `blue=RED`,
67+
},
68+
envvars: DefaultConfigEnvVars,
69+
expectError: "parsing levels string from environment variable FLUME: invalid log levels: invalid log level 'RED': slog: level string \"RED\": unknown name",
70+
},
71+
{
72+
name: "parse levels string",
73+
env: map[string]string{
74+
"FLUME": `*=DBG,blue=WRN`,
75+
},
76+
envvars: DefaultConfigEnvVars,
77+
expected: HandlerOptions{
78+
Level: LevelDebug,
79+
Levels: map[string]slog.Leveler{
80+
"blue": LevelWarn,
81+
},
82+
},
83+
},
6384
}
6485

6586
for _, test := range tests {
@@ -112,9 +133,9 @@ func TestConfigFromEnv(t *testing.T) {
112133
{
113134
name: "parse error",
114135
env: map[string]string{
115-
"FLUME": `not json`,
136+
"FLUME": `{{not json}}`,
116137
},
117-
expectError: "parsing configuration from environment variable FLUME: invalid character",
138+
expectError: "parsing configuration from environment variable FLUME: invalid character '{' looking for beginning of object key string",
118139
},
119140
}
120141

@@ -131,77 +152,25 @@ func TestConfigFromEnv(t *testing.T) {
131152
}
132153
err := ConfigFromEnv(test.envvars...)
133154
if test.expectError != "" {
134-
assert.ErrorContains(t, err, test.expectError)
135-
return
136-
}
137-
138-
require.NoError(t, err)
139-
assert.Equal(t, test.expectedLevel, Default().HandlerOptions().Level)
140-
})
141-
}
142-
}
143-
144-
func TestMustConfigFromEnv(t *testing.T) {
145-
testCases := []struct {
146-
name string
147-
env map[string]string
148-
envvars []string
149-
expectedLevel slog.Leveler
150-
expectPanic string
151-
}{
152-
{
153-
name: "default",
154-
env: map[string]string{
155-
"FLUME": `{"level":"WRN"}`,
156-
},
157-
expectedLevel: slog.LevelWarn,
158-
},
159-
{
160-
name: "search envvars",
161-
env: map[string]string{
162-
"EMPTY": "",
163-
"LOGCONFIG": `{"level":"WRN"}`,
164-
},
165-
envvars: []string{"EMPTY", "LOGCONFIG"},
166-
expectedLevel: slog.LevelWarn,
167-
},
168-
{
169-
name: "no-op",
170-
envvars: []string{"EMPTY", "LOGCONFIG"},
171-
expectedLevel: nil,
172-
},
173-
{
174-
name: "parse error",
175-
env: map[string]string{
176-
"FLUME": `not json`,
177-
},
178-
expectPanic: "parsing configuration from environment variable FLUME: invalid character 'o' in literal null (expecting 'u')",
179-
},
180-
}
181-
for _, tC := range testCases {
182-
t.Run(tC.name, func(t *testing.T) {
183-
// make sure the default handler is restored after the test
184-
ogOpts := Default().HandlerOptions()
185-
t.Cleanup(func() {
186-
Default().SetHandlerOptions(ogOpts)
187-
})
188-
189-
for k, v := range tC.env {
190-
t.Setenv(k, v)
155+
assert.Error(t, err, test.expectError) //nolint:testifylint
156+
} else {
157+
require.NoError(t, err)
158+
assert.Equal(t, test.expectedLevel, Default().HandlerOptions().Level)
191159
}
192160

193-
if tC.expectPanic != "" {
194-
assert.PanicsWithError(t, tC.expectPanic, func() {
195-
MustConfigFromEnv(tC.envvars...)
161+
// also test MustConfig
162+
if test.expectError != "" {
163+
assert.PanicsWithError(t, test.expectError, func() {
164+
MustConfigFromEnv(test.envvars...)
196165
})
197-
return
166+
} else {
167+
MustConfigFromEnv(test.envvars...)
168+
assert.Equal(t, test.expectedLevel, Default().HandlerOptions().Level)
198169
}
199-
200-
MustConfigFromEnv(tC.envvars...)
201-
assert.Equal(t, tC.expectedLevel, Default().HandlerOptions().Level)
202170
})
203171
}
204172
}
173+
205174
func TestRegisterHandlerFn(t *testing.T) {
206175
tests := []struct {
207176
name string

v2/middleware.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,6 @@ func (h *replaceAttrsMiddleware) Enabled(ctx context.Context, level slog.Level)
301301
}
302302

303303
func (h *replaceAttrsMiddleware) Handle(ctx context.Context, record slog.Record) error {
304-
// TODO: apply ReplaceAttrs to the built-in attrs as well
305-
306304
var replacedBuiltIns [3]slog.Attr
307305
replaced := replacedBuiltIns[:0]
308306

0 commit comments

Comments
 (0)