Skip to content

Commit a0f808f

Browse files
committed
add json logger format
add unit test case add benchmark test case fix staticcheck, and response for review comment remove unnecessary variable add test case for non-string field or zap-field, refactor code update vendor
1 parent 35fc65d commit a0f808f

File tree

16 files changed

+582
-2
lines changed

16 files changed

+582
-2
lines changed

staging/src/k8s.io/apiserver/go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ require (
3131
github.com/stretchr/testify v1.4.0
3232
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
3333
go.etcd.io/etcd v0.5.0-alpha.5.0.20200520232829-54ba9589114f
34-
go.uber.org/atomic v1.4.0 // indirect
3534
go.uber.org/zap v1.10.0
3635
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975
3736
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e

staging/src/k8s.io/cloud-provider/go.sum

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

staging/src/k8s.io/component-base/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ require (
1717
github.com/prometheus/procfs v0.0.11
1818
github.com/spf13/pflag v1.0.5
1919
github.com/stretchr/testify v1.4.0
20+
go.uber.org/atomic v1.4.0 // indirect
21+
go.uber.org/multierr v1.1.0 // indirect
22+
go.uber.org/zap v1.10.0
2023
k8s.io/apimachinery v0.0.0
2124
k8s.io/client-go v0.0.0
2225
k8s.io/klog/v2 v2.1.0

staging/src/k8s.io/component-base/go.sum

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

staging/src/k8s.io/component-base/logs/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ go_library(
1616
importpath = "k8s.io/component-base/logs",
1717
deps = [
1818
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
19+
"//staging/src/k8s.io/component-base/logs/json:go_default_library",
1920
"//vendor/github.com/go-logr/logr:go_default_library",
2021
"//vendor/github.com/spf13/pflag:go_default_library",
2122
"//vendor/k8s.io/klog/v2:go_default_library",
@@ -33,6 +34,7 @@ filegroup(
3334
name = "all-srcs",
3435
srcs = [
3536
":package-srcs",
37+
"//staging/src/k8s.io/component-base/logs/json:all-srcs",
3638
"//staging/src/k8s.io/component-base/logs/logreduction:all-srcs",
3739
],
3840
tags = ["automanaged"],
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["json.go"],
6+
importmap = "k8s.io/kubernetes/vendor/k8s.io/component-base/logs/json",
7+
importpath = "k8s.io/component-base/logs/json",
8+
visibility = ["//visibility:public"],
9+
deps = [
10+
"//vendor/github.com/go-logr/logr:go_default_library",
11+
"//vendor/go.uber.org/zap:go_default_library",
12+
"//vendor/go.uber.org/zap/zapcore:go_default_library",
13+
],
14+
)
15+
16+
go_test(
17+
name = "go_default_test",
18+
srcs = [
19+
"json_benchmark_test.go",
20+
"json_test.go",
21+
],
22+
embed = [":go_default_library"],
23+
deps = [
24+
"//vendor/github.com/stretchr/testify/assert:go_default_library",
25+
"//vendor/go.uber.org/zap:go_default_library",
26+
"//vendor/go.uber.org/zap/zapcore:go_default_library",
27+
],
28+
)
29+
30+
filegroup(
31+
name = "package-srcs",
32+
srcs = glob(["**"]),
33+
tags = ["automanaged"],
34+
visibility = ["//visibility:private"],
35+
)
36+
37+
filegroup(
38+
name = "all-srcs",
39+
srcs = [":package-srcs"],
40+
tags = ["automanaged"],
41+
visibility = ["//visibility:public"],
42+
)
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package logs
18+
19+
import (
20+
"os"
21+
"time"
22+
23+
"github.com/go-logr/logr"
24+
"go.uber.org/zap"
25+
"go.uber.org/zap/zapcore"
26+
)
27+
28+
// Inspired from https://github.com/go-logr/zapr, some functions is copy from the repo.
29+
30+
var (
31+
// JSONLogger is global json log format logr
32+
JSONLogger logr.Logger
33+
34+
// timeNow stubbed out for testing
35+
timeNow = time.Now
36+
)
37+
38+
type noopInfoLogger struct{}
39+
40+
// Enabled func in noopInfoLogger always return false
41+
func (l *noopInfoLogger) Enabled() bool {
42+
return false
43+
}
44+
45+
func (l *noopInfoLogger) Info(_ string, _ ...interface{}) {}
46+
47+
var disabledInfoLogger = &noopInfoLogger{}
48+
49+
type infoLogger struct {
50+
lvl zapcore.Level
51+
l *zap.Logger
52+
}
53+
54+
// implement logr.InfoLogger
55+
var _ logr.InfoLogger = &infoLogger{}
56+
57+
// Enabled always return true
58+
func (l *infoLogger) Enabled() bool {
59+
return true
60+
}
61+
62+
// Info write message to error level log
63+
func (l *infoLogger) Info(msg string, keysAndVals ...interface{}) {
64+
if checkedEntry := l.l.Check(l.lvl, msg); checkedEntry != nil {
65+
checkedEntry.Time = timeNow()
66+
checkedEntry.Write(l.handleFields(keysAndVals)...)
67+
}
68+
}
69+
70+
// dPanic write message to DPanicLevel level log
71+
// we need implement this because unit test case need stub time.Now
72+
// otherwise the ts field always changed
73+
func (l *infoLogger) dPanic(msg string, keysAndVals ...interface{}) {
74+
entry := zapcore.Entry{
75+
Level: zapcore.DPanicLevel,
76+
Time: timeNow(),
77+
Message: msg,
78+
}
79+
checkedEntry := l.l.Core().Check(entry, nil)
80+
checkedEntry.Write(l.handleFields(keysAndVals)...)
81+
}
82+
83+
// handleFields converts a bunch of arbitrary key-value pairs into Zap fields. It takes
84+
// additional pre-converted Zap fields, for use with automatically attached fields, like
85+
// `error`.
86+
func (l *infoLogger) handleFields(args []interface{}, additional ...zap.Field) []zap.Field {
87+
// a slightly modified version of zap.SugaredLogger.sweetenFields
88+
if len(args) == 0 {
89+
// fast-return if we have no suggared fields.
90+
return additional
91+
}
92+
93+
// unlike Zap, we can be pretty sure users aren't passing structured
94+
// fields (since logr has no concept of that), so guess that we need a
95+
// little less space.
96+
fields := make([]zap.Field, 0, len(args)/2+len(additional))
97+
for i := 0; i < len(args)-1; i += 2 {
98+
// check just in case for strongly-typed Zap fields, which is illegal (since
99+
// it breaks implementation agnosticism), so we can give a better error message.
100+
if _, ok := args[i].(zap.Field); ok {
101+
l.dPanic("strongly-typed Zap Field passed to logr", zap.Any("zap field", args[i]))
102+
break
103+
}
104+
105+
// process a key-value pair,
106+
// ensuring that the key is a string
107+
key, val := args[i], args[i+1]
108+
keyStr, isString := key.(string)
109+
if !isString {
110+
// if the key isn't a string, DPanic and stop logging
111+
l.dPanic("non-string key argument passed to logging, ignoring all later arguments", zap.Any("invalid key", key))
112+
break
113+
}
114+
115+
fields = append(fields, zap.Any(keyStr, val))
116+
}
117+
118+
return append(fields, additional...)
119+
}
120+
121+
// zapLogger is a logr.Logger that uses Zap to record log.
122+
type zapLogger struct {
123+
// NB: this looks very similar to zap.SugaredLogger, but
124+
// deals with our desire to have multiple verbosity levels.
125+
l *zap.Logger
126+
infoLogger
127+
}
128+
129+
// implement logr.Logger
130+
var _ logr.Logger = &zapLogger{}
131+
132+
// Error write log message to error level
133+
func (l *zapLogger) Error(err error, msg string, keysAndVals ...interface{}) {
134+
entry := zapcore.Entry{
135+
Level: zapcore.ErrorLevel,
136+
Time: timeNow(),
137+
Message: msg,
138+
}
139+
checkedEntry := l.l.Core().Check(entry, nil)
140+
checkedEntry.Write(l.handleFields(keysAndVals, handleError(err))...)
141+
}
142+
143+
// V return info logr.Logger with specified level
144+
func (l *zapLogger) V(level int) logr.InfoLogger {
145+
lvl := zapcore.Level(-1 * level)
146+
if l.l.Core().Enabled(lvl) {
147+
return &infoLogger{
148+
l: l.l,
149+
lvl: lvl,
150+
}
151+
}
152+
return disabledInfoLogger
153+
}
154+
155+
// WithValues return logr.Logger with some keys And Values
156+
func (l *zapLogger) WithValues(keysAndValues ...interface{}) logr.Logger {
157+
l.l = l.l.With(l.handleFields(keysAndValues)...)
158+
return l
159+
}
160+
161+
// WithName return logger Named with specified name
162+
func (l *zapLogger) WithName(name string) logr.Logger {
163+
l.l = l.l.Named(name)
164+
return l
165+
}
166+
167+
// encoderConfig config zap json encoder key format, and encodetime format
168+
var encoderConfig = zapcore.EncoderConfig{
169+
MessageKey: "msg",
170+
171+
LevelKey: "v",
172+
EncodeLevel: int8LevelEncoder,
173+
174+
TimeKey: "ts",
175+
EncodeTime: zapcore.EpochMillisTimeEncoder,
176+
}
177+
178+
// NewJSONLogger creates a new json logr.Logger using the given Zap Logger to log.
179+
func NewJSONLogger(l *zap.Logger, w zapcore.WriteSyncer) logr.Logger {
180+
if w == nil {
181+
w = os.Stdout
182+
}
183+
log := l.WithOptions(zap.AddCallerSkip(1),
184+
zap.WrapCore(
185+
func(zapcore.Core) zapcore.Core {
186+
return zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(w), zapcore.DebugLevel)
187+
}))
188+
return &zapLogger{
189+
l: log,
190+
infoLogger: infoLogger{
191+
l: log,
192+
lvl: zap.DebugLevel,
193+
},
194+
}
195+
}
196+
197+
func int8LevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
198+
lvl := int8(l)
199+
if lvl < 0 {
200+
lvl = -lvl
201+
}
202+
enc.AppendInt8(lvl)
203+
}
204+
205+
func handleError(err error) zap.Field {
206+
return zap.NamedError("err", err)
207+
}
208+
209+
func init() {
210+
l, _ := zap.NewProduction()
211+
JSONLogger = NewJSONLogger(l, nil)
212+
}

0 commit comments

Comments
 (0)