Skip to content

Commit fd61c31

Browse files
authored
Merge pull request kubernetes#91608 from yuzhiquan/feature-implement-json-logformat
Feature implement json logformat
2 parents 1c11ff7 + a0f808f commit fd61c31

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)