Skip to content

Commit 2040fd6

Browse files
authored
Migrate to zap logger (#223)
1 parent 677de1e commit 2040fd6

File tree

3,746 files changed

+615474
-579696
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

3,746 files changed

+615474
-579696
lines changed

Gopkg.lock

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

Gopkg.toml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,21 @@
2020
# name = "github.com/x/y"
2121
# version = "2.4.0"
2222

23+
# See: https://github.com/golang/dep/issues/1799
24+
[[override]]
25+
name = "gopkg.in/fsnotify.v1"
26+
source = "https://github.com/fsnotify/fsnotify.git"
27+
2328
[[override]]
2429
branch = "master"
2530
name = "github.com/docker/distribution"
2631

32+
# glog -> zap
33+
[[override]]
34+
revision = "ad18aa91a01e5a7a4d312d16528a181979a23c62"
35+
name = "github.com/golang/glog"
36+
source = "github.com/prydie/glog"
37+
2738
[[override]]
2839
branch = "release-1.10"
2940
name = "k8s.io/apiextensions-apiserver"
@@ -32,14 +43,14 @@
3243
revision = "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf"
3344
name = "k8s.io/kube-openapi"
3445

35-
[[constraint]]
36-
branch = "master"
37-
name = "github.com/golang/glog"
38-
3946
[[constraint]]
4047
name = "github.com/spf13/pflag"
4148
version = "1.0.0"
4249

50+
[[constraint]]
51+
name = "go.uber.org/zap"
52+
version = "1.9.0"
53+
4354
[[constraint]]
4455
branch = "release-1.10"
4556
name = "k8s.io/api"

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@ import (
2121
"os"
2222
"time"
2323

24-
"github.com/golang/glog"
2524
"github.com/spf13/pflag"
25+
"go.uber.org/zap"
2626

2727
utilflag "k8s.io/apiserver/pkg/util/flag"
2828
"k8s.io/apiserver/pkg/util/logs"
2929
"k8s.io/kubernetes/cmd/cloud-controller-manager/app"
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+
"github.com/oracle/oci-cloud-controller-manager/pkg/logging"
3334
_ "github.com/oracle/oci-cloud-controller-manager/pkg/oci"
3435
)
3536

@@ -39,6 +40,10 @@ var build string
3940
func main() {
4041
rand.Seed(time.Now().UTC().UnixNano())
4142

43+
logger := logging.Logger()
44+
defer logger.Sync()
45+
zap.ReplaceGlobals(logger)
46+
4247
command := app.NewCloudControllerManagerCommand()
4348

4449
// TODO: once we switch everything over to Cobra commands, we can go back to calling
@@ -47,10 +52,11 @@ func main() {
4752
pflag.CommandLine.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
4853
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
4954
goflag.CommandLine.Parse([]string{})
55+
5056
logs.InitLogs()
5157
defer logs.FlushLogs()
5258

53-
glog.V(1).Infof("oci-cloud-controller-manager version: %s (%s)", version, build)
59+
logger.Sugar().With("version", version, "build", build).Info("oci-cloud-controller-manager")
5460

5561
if err := command.Execute(); err != nil {
5662
fmt.Fprintf(os.Stderr, "error: %v\n", err)

hack/test-e2e.sh

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ function run_e2e_tests() {
2626
ginkgo -v -progress \
2727
-focus "\[Canary\]" \
2828
test/e2e \
29-
-- --kubeconfig=${KUBECONFIG} --cloud-config=${CLOUDCONFIG} --delete-namespace=false
29+
-- \
30+
--kubeconfig="${KUBECONFIG}" \
31+
--cloud-config="${CLOUDCONFIG}" \
32+
--delete-namespace=false
3033
}
3134

3235
# Main ************************************************************************
@@ -57,5 +60,3 @@ fi
5760
run_e2e_tests
5861

5962
exit $?
60-
61-

pkg/logging/logging.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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+
"flag"
19+
"os"
20+
"strings"
21+
"sync"
22+
23+
"go.uber.org/zap"
24+
"go.uber.org/zap/zapcore"
25+
lumberjack "gopkg.in/natefinch/lumberjack.v2"
26+
)
27+
28+
var (
29+
lvl = zapcore.InfoLevel
30+
logJSON = false
31+
logfilePath = ""
32+
config *zap.Config
33+
mu sync.Mutex
34+
)
35+
36+
func init() {
37+
flag.Var(&lvl, "log-level", "Adjusts the level of the logs that will be omitted.")
38+
flag.BoolVar(&logJSON, "log-json", logJSON, "Log in json format.")
39+
flag.StringVar(&logfilePath, "logfile-path", "", "If specified, write log messages to a file at this path.")
40+
}
41+
42+
// Options holds the zap logger configuration.
43+
type Options struct {
44+
LogLevel *zapcore.Level
45+
Config *zap.Config
46+
}
47+
48+
// Level gets the current log level.
49+
func Level() *zap.AtomicLevel {
50+
return &config.Level
51+
}
52+
53+
// Logger builds a new logger based on the given flags.
54+
func Logger() *zap.Logger {
55+
mu.Lock()
56+
defer mu.Unlock()
57+
58+
var cfg zap.Config
59+
60+
if !logJSON {
61+
cfg = zap.NewDevelopmentConfig()
62+
} else {
63+
cfg = zap.NewProductionConfig()
64+
}
65+
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+
}
75+
76+
if len(logfilePath) > 0 {
77+
w := zapcore.AddSync(&lumberjack.Logger{
78+
Filename: logfilePath,
79+
MaxSize: 10, // megabytes
80+
MaxBackups: 3,
81+
MaxAge: 28, // days
82+
})
83+
var enc zapcore.Encoder
84+
if logJSON {
85+
enc = zapcore.NewJSONEncoder(cfg.EncoderConfig)
86+
} else {
87+
enc = zapcore.NewConsoleEncoder(cfg.EncoderConfig)
88+
}
89+
core := zapcore.NewCore(enc, w, lvl)
90+
options = append(options, zap.WrapCore(func(zapcore.Core) zapcore.Core {
91+
return core
92+
}))
93+
}
94+
95+
if config == nil {
96+
config = &cfg
97+
config.Level.SetLevel(lvl)
98+
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
99+
}
100+
101+
logger, err := config.Build(
102+
// We handle this via errors package for 99% of the stuff so only
103+
// enable this at the fatal/panic level.
104+
options...,
105+
)
106+
if err != nil {
107+
panic(err)
108+
}
109+
110+
return logger
111+
}
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

Lines changed: 81 additions & 0 deletions
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)