Skip to content

Commit a78362b

Browse files
committed
Add support for setting log fields from env vars
Setting env vars in the form LOG_VAR_field_name=value now adds fields fields to the logger (e.g. zap.String("field_name", "value") in this example).
1 parent 9347ab1 commit a78362b

File tree

3 files changed

+119
-4
lines changed

3 files changed

+119
-4
lines changed

cmd/oci-cloud-controller-manager/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import (
3030
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
3131
_ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration
3232

33-
logutil "github.com/oracle/oci-cloud-controller-manager/pkg/log"
33+
"github.com/oracle/oci-cloud-controller-manager/pkg/logging"
3434
_ "github.com/oracle/oci-cloud-controller-manager/pkg/oci"
3535
)
3636

@@ -40,7 +40,7 @@ var build string
4040
func main() {
4141
rand.Seed(time.Now().UTC().UnixNano())
4242

43-
logger := logutil.Logger()
43+
logger := logging.Logger()
4444
defer logger.Sync()
4545
zap.ReplaceGlobals(logger)
4646

pkg/log/log.go renamed to pkg/logging/logging.go

+36-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package log
15+
package logging
1616

1717
import (
1818
"flag"
19+
"os"
20+
"strings"
1921
"sync"
2022

2123
"go.uber.org/zap"
@@ -61,7 +63,15 @@ func Logger() *zap.Logger {
6163
cfg = zap.NewProductionConfig()
6264
}
6365

64-
options := []zap.Option{zap.AddStacktrace(zapcore.FatalLevel)}
66+
// Extract log fields from environment variables.
67+
envFields := FieldsFromEnv(os.Environ())
68+
69+
options := []zap.Option{
70+
zap.AddStacktrace(zapcore.FatalLevel),
71+
zap.WrapCore(func(c zapcore.Core) zapcore.Core {
72+
return c.With(envFields)
73+
}),
74+
}
6575

6676
if len(logfilePath) > 0 {
6777
w := zapcore.AddSync(&lumberjack.Logger{
@@ -99,3 +109,27 @@ func Logger() *zap.Logger {
99109

100110
return logger
101111
}
112+
113+
// FieldsFromEnv extracts log fields from environment variables.
114+
// If an environment variable starts with LOG_FIELD_, the suffix is extracted
115+
// and split on =. The first part is used for the name and the second for the
116+
// value.
117+
// For example, LOG_FIELD_foo=bar would result in a field named "foo" with the
118+
// value "bar".
119+
func FieldsFromEnv(env []string) []zapcore.Field {
120+
const logfieldPrefix = "LOG_FIELD_"
121+
122+
fields := []zapcore.Field{}
123+
for _, s := range env {
124+
if !strings.HasPrefix(s, logfieldPrefix) || len(s) < (len(logfieldPrefix)+1) {
125+
continue
126+
}
127+
s = s[len(logfieldPrefix):]
128+
parts := strings.SplitN(s, "=", 2)
129+
if len(parts) != 2 {
130+
continue
131+
}
132+
fields = append(fields, zap.String(parts[0], parts[1]))
133+
}
134+
return fields
135+
}

pkg/logging/logging_test.go

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2018 Oracle and/or its affiliates. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package logging
16+
17+
import (
18+
"reflect"
19+
"testing"
20+
21+
"go.uber.org/zap"
22+
"go.uber.org/zap/zapcore"
23+
)
24+
25+
func TestFieldsFromEnv(t *testing.T) {
26+
testCases := map[string]struct {
27+
env []string
28+
fields []zapcore.Field
29+
}{
30+
"single": {
31+
env: []string{"LOG_FIELD_foo=bar"},
32+
fields: []zapcore.Field{
33+
zap.String("foo", "bar"),
34+
},
35+
},
36+
"multiple": {
37+
env: []string{
38+
"LOG_FIELD_foo=bar",
39+
"LOG_FIELD_bar=baz",
40+
},
41+
fields: []zapcore.Field{
42+
zap.String("foo", "bar"),
43+
zap.String("bar", "baz"),
44+
},
45+
},
46+
"handles_equals_in_value": {
47+
env: []string{
48+
"LOG_FIELD_foo=a=b",
49+
},
50+
fields: []zapcore.Field{
51+
zap.String("foo", "a=b"),
52+
},
53+
},
54+
"handles_empty_value": {
55+
env: []string{
56+
"LOG_FIELD_foo=",
57+
},
58+
fields: []zapcore.Field{
59+
zap.String("foo", ""),
60+
},
61+
},
62+
"ignores_non_log_field": {
63+
env: []string{
64+
"LOG_FIELD_foo=bar",
65+
"NOT_A_FIELD=1",
66+
},
67+
fields: []zapcore.Field{
68+
zap.String("foo", "bar"),
69+
},
70+
},
71+
}
72+
73+
for name, tc := range testCases {
74+
t.Run(name, func(t *testing.T) {
75+
fields := FieldsFromEnv(tc.env)
76+
if !reflect.DeepEqual(fields, tc.fields) {
77+
t.Errorf("Got incorrect fields:\nexpected=%+v\nactual=%+v", tc.fields, fields)
78+
}
79+
})
80+
}
81+
}

0 commit comments

Comments
 (0)