├── .gitignore ├── go.mod ├── Makefile ├── golangci.yml ├── .github └── workflows │ └── build.yaml ├── README.md ├── default_test.go ├── default.go ├── scope_test.go ├── scope.go ├── config.go ├── config_test.go ├── LICENSE ├── options.go ├── options_test.go └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .idea 3 | .vscode 4 | bin/ 5 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tetratelabs/log 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/spf13/cobra v1.1.3 7 | github.com/spf13/pflag v1.0.5 8 | go.uber.org/multierr v1.6.0 // indirect 9 | go.uber.org/zap v1.16.0 10 | google.golang.org/grpc v1.36.0 11 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 12 | ) 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Packages to build 2 | PKGS := ./... 3 | TEST_OPTS ?= 4 | 5 | build: 6 | @echo "--- build ---" 7 | go build -v $(PKGS) 8 | 9 | test: 10 | @echo "--- test ---" 11 | go test $(TEST_OPTS) $(PKGS) 12 | 13 | LINTER := bin/golangci-lint 14 | $(LINTER): 15 | wget -O - -q https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b bin v1.38.0 16 | 17 | lint: $(LINTER) golangci.yml 18 | @echo "--- lint ---" 19 | $(LINTER) run --config golangci.yml 20 | 21 | .PHONY: build test lint 22 | -------------------------------------------------------------------------------- /golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | enable: 3 | - deadcode 4 | - errcheck 5 | - goconst 6 | - golint 7 | - ineffassign 8 | - lll 9 | - maligned 10 | - misspell 11 | - structcheck 12 | - unconvert 13 | - varcheck 14 | - govet 15 | - goimports 16 | - prealloc 17 | - unused 18 | - staticcheck 19 | - gosimple 20 | - megacheck 21 | disable: 22 | - interfacer 23 | linters-settings: 24 | lll: 25 | line-length: 170 26 | goconst: 27 | min-occurrences: 4 28 | govet: 29 | check-shadowing: true 30 | run: 31 | deadline: 10m 32 | skip-dirs: 33 | - k8s/istioapis/generated 34 | - k8s/tsbapis/generated 35 | issues: 36 | exclude: 37 | # staticcheck 38 | - 'SA1019: Package github.com/golang/protobuf/proto is deprecated: Use the "google.golang.org/protobuf/proto" package instead.' 39 | - 'SA1019: Package github.com/golang/protobuf/jsonpb is deprecated: Use the "google.golang.org/protobuf/encoding/protojson" package instead.' 40 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Tetrate Labs 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 | name: build 16 | 17 | on: [push, pull_request] 18 | 19 | env: 20 | GOPROXY: https://proxy.golang.org 21 | 22 | jobs: 23 | build: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v2 27 | - uses: actions/setup-go@v2 28 | with: 29 | go-version: 1.15 30 | - run: make build 31 | - run: make test 32 | - run: make lint 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Common logging infrastructure 2 | 3 | This `log` package is taken from [Istio](https://istio.io/) and provides the canonical logging functionality used by Go-based components. 4 | 5 | Istio's logging subsystem is built on top of the [Zap](https:godoc.org/go.uber.org/zap) package. 6 | High performance scenarios should use the Error, Warn, Info, and Debug methods. Lower perf 7 | scenarios can use the more expensive convenience methods such as Debugf and Warnw. 8 | 9 | The package provides direct integration with the Cobra command-line processor which makes it 10 | easy to build programs that use a consistent interface for logging. Here's an example 11 | of a simple Cobra-based program using this log package: 12 | 13 | ```go 14 | func main() { 15 | // get the default logging options 16 | options := log.DefaultOptions() 17 | 18 | rootCmd := &cobra.Command{ 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | // configure the logging system 22 | if err := log.Configure(options); err != nil { 23 | // print an error and quit 24 | } 25 | 26 | // output some logs 27 | log.Info("Hello") 28 | log.Sync() 29 | }, 30 | } 31 | 32 | // add logging-specific flags to the cobra command 33 | options.AttachFlags(rootCmd) 34 | rootCmd.SetArgs(os.Args[1:]) 35 | rootCmd.Execute() 36 | } 37 | ``` 38 | 39 | Once configured, this package intercepts the output of the standard golang "log" package as well as anything 40 | sent to the global zap logger (`zap.L()`). 41 | 42 | ## Installing 43 | 44 | The log package can be installed using `go get`: 45 | 46 | go get -u github.com/tetratelabs/log 47 | 48 | To build it from source just run: 49 | 50 | make 51 | 52 | ## License 53 | 54 | This sowftare is licensed under the Apache License 2.0. See LICENSE file for details. 55 | -------------------------------------------------------------------------------- /default_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tetrate, Inc 2018 All Rights Reserved. 2 | 3 | // Copyright 2017 Istio Authors 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package log 18 | 19 | import ( 20 | "regexp" 21 | "strconv" 22 | "testing" 23 | ) 24 | 25 | func TestDefault(t *testing.T) { 26 | cases := []struct { 27 | f func() 28 | pat string 29 | json bool 30 | caller bool 31 | stackLevel Level 32 | }{ 33 | {func() { Debug("Hello") }, timePattern + "\tdebug\tHello", false, false, NoneLevel}, 34 | {func() { Debugf("Hello") }, timePattern + "\tdebug\tHello", false, false, NoneLevel}, 35 | {func() { Debugf("%s", "Hello") }, timePattern + "\tdebug\tHello", false, false, NoneLevel}, 36 | {func() { Debuga("Hello") }, timePattern + "\tdebug\tHello", false, false, NoneLevel}, 37 | 38 | {func() { Info("Hello") }, timePattern + "\tinfo\tHello", false, false, NoneLevel}, 39 | {func() { Infof("Hello") }, timePattern + "\tinfo\tHello", false, false, NoneLevel}, 40 | {func() { Infof("%s", "Hello") }, timePattern + "\tinfo\tHello", false, false, NoneLevel}, 41 | {func() { Infoa("Hello") }, timePattern + "\tinfo\tHello", false, false, NoneLevel}, 42 | 43 | {func() { Warn("Hello") }, timePattern + "\twarn\tHello", false, false, NoneLevel}, 44 | {func() { Warnf("Hello") }, timePattern + "\twarn\tHello", false, false, NoneLevel}, 45 | {func() { Warnf("%s", "Hello") }, timePattern + "\twarn\tHello", false, false, NoneLevel}, 46 | {func() { Warna("Hello") }, timePattern + "\twarn\tHello", false, false, NoneLevel}, 47 | 48 | {func() { Error("Hello") }, timePattern + "\terror\tHello", false, false, NoneLevel}, 49 | {func() { Errorf("Hello") }, timePattern + "\terror\tHello", false, false, NoneLevel}, 50 | {func() { Errorf("%s", "Hello") }, timePattern + "\terror\tHello", false, false, NoneLevel}, 51 | {func() { Errora("Hello") }, timePattern + "\terror\tHello", false, false, NoneLevel}, 52 | 53 | {func() { Debug("Hello") }, timePattern + "\tdebug\tlog/default_test.go:.*\tHello", false, true, NoneLevel}, 54 | 55 | {func() { Debug("Hello") }, "{\"level\":\"debug\",\"time\":\"" + timePattern + "\",\"caller\":\"log/default_test.go:.*\",\"msg\":\"Hello\"," + 56 | "\"stack\":\".*\"}", 57 | true, true, DebugLevel}, 58 | {func() { Info("Hello") }, "{\"level\":\"info\",\"time\":\"" + timePattern + "\",\"caller\":\"log/default_test.go:.*\",\"msg\":\"Hello\"," + 59 | "\"stack\":\".*\"}", 60 | true, true, DebugLevel}, 61 | {func() { Warn("Hello") }, "{\"level\":\"warn\",\"time\":\"" + timePattern + "\",\"caller\":\"log/default_test.go:.*\",\"msg\":\"Hello\"," + 62 | "\"stack\":\".*\"}", 63 | true, true, DebugLevel}, 64 | {func() { Error("Hello") }, "{\"level\":\"error\",\"time\":\"" + timePattern + "\",\"caller\":\"log/default_test.go:.*\",\"msg\":\"Hello\"," + 65 | "\"stack\":\".*\"}", 66 | true, true, DebugLevel}, 67 | } 68 | 69 | for i, c := range cases { 70 | t.Run(strconv.Itoa(i), func(t *testing.T) { 71 | lines, err := captureStdout(func() { 72 | o := DefaultOptions() 73 | o.JSONEncoding = c.json 74 | 75 | if err := Configure(o); err != nil { 76 | t.Errorf("Got err '%v', expecting success", err) 77 | } 78 | 79 | defaultScope.SetOutputLevel(DebugLevel) 80 | defaultScope.SetStackTraceLevel(c.stackLevel) 81 | defaultScope.SetLogCallers(c.caller) 82 | 83 | c.f() 84 | _ = Sync() 85 | }) 86 | 87 | if err != nil { 88 | t.Errorf("Got error '%v', expected success", err) 89 | } 90 | 91 | if match, _ := regexp.MatchString(c.pat, lines[0]); !match { 92 | t.Errorf("Got '%v', expected a match with '%v'", lines[0], c.pat) 93 | } 94 | }) 95 | } 96 | } 97 | 98 | func TestEnabled(t *testing.T) { 99 | cases := []struct { 100 | level Level 101 | debugEnabled bool 102 | infoEnabled bool 103 | warnEnabled bool 104 | errorEnabled bool 105 | }{ 106 | {DebugLevel, true, true, true, true}, 107 | {InfoLevel, false, true, true, true}, 108 | {WarnLevel, false, false, true, true}, 109 | {ErrorLevel, false, false, false, true}, 110 | {NoneLevel, false, false, false, false}, 111 | } 112 | 113 | for i, c := range cases { 114 | t.Run(strconv.Itoa(i), func(t *testing.T) { 115 | o := DefaultOptions() 116 | o.SetOutputLevel(DefaultScopeName, c.level) 117 | _ = Configure(o) 118 | 119 | if c.debugEnabled != DebugEnabled() { 120 | t.Errorf("Got %v, expecting %v", DebugEnabled(), c.debugEnabled) 121 | } 122 | 123 | if c.infoEnabled != InfoEnabled() { 124 | t.Errorf("Got %v, expecting %v", InfoEnabled(), c.infoEnabled) 125 | } 126 | 127 | if c.warnEnabled != WarnEnabled() { 128 | t.Errorf("Got %v, expecting %v", WarnEnabled(), c.warnEnabled) 129 | } 130 | 131 | if c.errorEnabled != ErrorEnabled() { 132 | t.Errorf("Got %v, expecting %v", ErrorEnabled(), c.errorEnabled) 133 | } 134 | }) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /default.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tetrate, Inc 2018 All Rights Reserved. 2 | 3 | // Copyright 2017 Istio Authors 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package log // nolint: golint 18 | 19 | import ( 20 | "fmt" 21 | 22 | "go.uber.org/zap/zapcore" 23 | ) 24 | 25 | func registerDefaultScope() *Scope { 26 | return RegisterScope(DefaultScopeName, "Unscoped logging messages.", 1) 27 | } 28 | 29 | var defaultScope = registerDefaultScope() 30 | 31 | // Error outputs a message at error level. 32 | func Error(msg string, fields ...zapcore.Field) { 33 | if defaultScope.GetOutputLevel() >= ErrorLevel { 34 | defaultScope.emit(zapcore.ErrorLevel, defaultScope.GetStackTraceLevel() >= ErrorLevel, msg, fields) 35 | } 36 | } 37 | 38 | // Errora uses fmt.Sprint to construct and log a message at error level. 39 | func Errora(args ...interface{}) { 40 | if defaultScope.GetOutputLevel() >= ErrorLevel { 41 | defaultScope.emit(zapcore.ErrorLevel, defaultScope.GetStackTraceLevel() >= ErrorLevel, fmt.Sprint(args...), nil) 42 | } 43 | } 44 | 45 | // Errorf uses fmt.Sprintf to construct and log a message at error level. 46 | func Errorf(template string, args ...interface{}) { 47 | if defaultScope.GetOutputLevel() >= ErrorLevel { 48 | msg := template 49 | if len(args) > 0 { 50 | msg = fmt.Sprintf(template, args...) 51 | } 52 | defaultScope.emit(zapcore.ErrorLevel, defaultScope.GetStackTraceLevel() >= ErrorLevel, msg, nil) 53 | } 54 | } 55 | 56 | // ErrorEnabled returns whether output of messages using this scope is currently enabled for error-level output. 57 | func ErrorEnabled() bool { 58 | return defaultScope.GetOutputLevel() >= ErrorLevel 59 | } 60 | 61 | // Warn outputs a message at warn level. 62 | func Warn(msg string, fields ...zapcore.Field) { 63 | if defaultScope.GetOutputLevel() >= WarnLevel { 64 | defaultScope.emit(zapcore.WarnLevel, defaultScope.GetStackTraceLevel() >= WarnLevel, msg, fields) 65 | } 66 | } 67 | 68 | // Warna uses fmt.Sprint to construct and log a message at warn level. 69 | func Warna(args ...interface{}) { 70 | if defaultScope.GetOutputLevel() >= WarnLevel { 71 | defaultScope.emit(zapcore.WarnLevel, defaultScope.GetStackTraceLevel() >= WarnLevel, fmt.Sprint(args...), nil) 72 | } 73 | } 74 | 75 | // Warnf uses fmt.Sprintf to construct and log a message at warn level. 76 | func Warnf(template string, args ...interface{}) { 77 | if defaultScope.GetOutputLevel() >= WarnLevel { 78 | msg := template 79 | if len(args) > 0 { 80 | msg = fmt.Sprintf(template, args...) 81 | } 82 | defaultScope.emit(zapcore.WarnLevel, defaultScope.GetStackTraceLevel() >= WarnLevel, msg, nil) 83 | } 84 | } 85 | 86 | // WarnEnabled returns whether output of messages using this scope is currently enabled for warn-level output. 87 | func WarnEnabled() bool { 88 | return defaultScope.GetOutputLevel() >= WarnLevel 89 | } 90 | 91 | // Info outputs a message at info level. 92 | func Info(msg string, fields ...zapcore.Field) { 93 | if defaultScope.GetOutputLevel() >= InfoLevel { 94 | defaultScope.emit(zapcore.InfoLevel, defaultScope.GetStackTraceLevel() >= InfoLevel, msg, fields) 95 | } 96 | } 97 | 98 | // Infoa uses fmt.Sprint to construct and log a message at info level. 99 | func Infoa(args ...interface{}) { 100 | if defaultScope.GetOutputLevel() >= InfoLevel { 101 | defaultScope.emit(zapcore.InfoLevel, defaultScope.GetStackTraceLevel() >= InfoLevel, fmt.Sprint(args...), nil) 102 | } 103 | } 104 | 105 | // Infof uses fmt.Sprintf to construct and log a message at info level. 106 | func Infof(template string, args ...interface{}) { 107 | if defaultScope.GetOutputLevel() >= InfoLevel { 108 | msg := template 109 | if len(args) > 0 { 110 | msg = fmt.Sprintf(template, args...) 111 | } 112 | defaultScope.emit(zapcore.InfoLevel, defaultScope.GetStackTraceLevel() >= InfoLevel, msg, nil) 113 | } 114 | } 115 | 116 | // InfoEnabled returns whether output of messages using this scope is currently enabled for info-level output. 117 | func InfoEnabled() bool { 118 | return defaultScope.GetOutputLevel() >= InfoLevel 119 | } 120 | 121 | // Debug outputs a message at debug level. 122 | func Debug(msg string, fields ...zapcore.Field) { 123 | if defaultScope.GetOutputLevel() >= DebugLevel { 124 | defaultScope.emit(zapcore.DebugLevel, defaultScope.GetStackTraceLevel() >= DebugLevel, msg, fields) 125 | } 126 | } 127 | 128 | // Debuga uses fmt.Sprint to construct and log a message at debug level. 129 | func Debuga(args ...interface{}) { 130 | if defaultScope.GetOutputLevel() >= DebugLevel { 131 | defaultScope.emit(zapcore.DebugLevel, defaultScope.GetStackTraceLevel() >= DebugLevel, fmt.Sprint(args...), nil) 132 | } 133 | } 134 | 135 | // Debugf uses fmt.Sprintf to construct and log a message at debug level. 136 | func Debugf(template string, args ...interface{}) { 137 | if defaultScope.GetOutputLevel() >= DebugLevel { 138 | msg := template 139 | if len(args) > 0 { 140 | msg = fmt.Sprintf(template, args...) 141 | } 142 | defaultScope.emit(zapcore.DebugLevel, defaultScope.GetStackTraceLevel() >= DebugLevel, msg, nil) 143 | } 144 | } 145 | 146 | // DebugEnabled returns whether output of messages using this scope is currently enabled for debug-level output. 147 | func DebugEnabled() bool { 148 | return defaultScope.GetOutputLevel() >= DebugLevel 149 | } 150 | -------------------------------------------------------------------------------- /scope_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tetrate, Inc 2018 All Rights Reserved. 2 | 3 | // Copyright 2018 Istio Authors 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package log 18 | 19 | import ( 20 | "errors" 21 | "regexp" 22 | "strconv" 23 | "testing" 24 | 25 | "go.uber.org/zap/zapcore" 26 | ) 27 | 28 | func TestBasicScopes(t *testing.T) { 29 | s := RegisterScope("testScope", "z", 0) 30 | 31 | cases := []struct { 32 | f func() 33 | pat string 34 | json bool 35 | caller bool 36 | stackLevel Level 37 | }{ 38 | {func() { s.Debug("Hello") }, timePattern + "\tdebug\ttestScope\tHello", false, false, NoneLevel}, 39 | {func() { s.Debugf("Hello") }, timePattern + "\tdebug\ttestScope\tHello", false, false, NoneLevel}, 40 | {func() { s.Debugf("%s", "Hello") }, timePattern + "\tdebug\ttestScope\tHello", false, false, NoneLevel}, 41 | {func() { s.Debuga("Hello") }, timePattern + "\tdebug\ttestScope\tHello", false, false, NoneLevel}, 42 | 43 | {func() { s.Info("Hello") }, timePattern + "\tinfo\ttestScope\tHello", false, false, NoneLevel}, 44 | {func() { s.Infof("Hello") }, timePattern + "\tinfo\ttestScope\tHello", false, false, NoneLevel}, 45 | {func() { s.Infof("%s", "Hello") }, timePattern + "\tinfo\ttestScope\tHello", false, false, NoneLevel}, 46 | {func() { s.Infoa("Hello") }, timePattern + "\tinfo\ttestScope\tHello", false, false, NoneLevel}, 47 | 48 | {func() { s.Warn("Hello") }, timePattern + "\twarn\ttestScope\tHello", false, false, NoneLevel}, 49 | {func() { s.Warnf("Hello") }, timePattern + "\twarn\ttestScope\tHello", false, false, NoneLevel}, 50 | {func() { s.Warnf("%s", "Hello") }, timePattern + "\twarn\ttestScope\tHello", false, false, NoneLevel}, 51 | {func() { s.Warna("Hello") }, timePattern + "\twarn\ttestScope\tHello", false, false, NoneLevel}, 52 | 53 | {func() { s.Error("Hello") }, timePattern + "\terror\ttestScope\tHello", false, false, NoneLevel}, 54 | {func() { s.Errorf("Hello") }, timePattern + "\terror\ttestScope\tHello", false, false, NoneLevel}, 55 | {func() { s.Errorf("%s", "Hello") }, timePattern + "\terror\ttestScope\tHello", false, false, NoneLevel}, 56 | {func() { s.Errora("Hello") }, timePattern + "\terror\ttestScope\tHello", false, false, NoneLevel}, 57 | 58 | {func() { s.Debug("Hello") }, timePattern + "\tdebug\ttestScope\tlog/scope_test.go:.*\tHello", false, true, NoneLevel}, 59 | 60 | {func() { s.Debug("Hello") }, 61 | "{\"level\":\"debug\",\"time\":\"" + timePattern + "\",\"scope\":\"testScope\",\"caller\":\"log/scope_test.go:.*\",\"msg\":\"Hello\"," + 62 | "\"stack\":\".*\"}", 63 | true, true, DebugLevel}, 64 | {func() { s.Info("Hello") }, 65 | "{\"level\":\"info\",\"time\":\"" + timePattern + "\",\"scope\":\"testScope\",\"caller\":\"log/scope_test.go:.*\",\"msg\":\"Hello\"," + 66 | "\"stack\":\".*\"}", 67 | true, true, DebugLevel}, 68 | {func() { s.Warn("Hello") }, 69 | "{\"level\":\"warn\",\"time\":\"" + timePattern + "\",\"scope\":\"testScope\",\"caller\":\"log/scope_test.go:.*\",\"msg\":\"Hello\"," + 70 | "\"stack\":\".*\"}", 71 | true, true, DebugLevel}, 72 | {func() { s.Error("Hello") }, 73 | "{\"level\":\"error\",\"time\":\"" + timePattern + "\",\"scope\":\"testScope\",\"caller\":\"log/scope_test.go:.*\"," + 74 | "\"msg\":\"Hello\"," + 75 | "\"stack\":\".*\"}", 76 | true, true, DebugLevel}, 77 | } 78 | 79 | for i, c := range cases { 80 | t.Run(strconv.Itoa(i), func(t *testing.T) { 81 | lines, err := captureStdout(func() { 82 | o := DefaultOptions() 83 | o.JSONEncoding = c.json 84 | 85 | if err := Configure(o); err != nil { 86 | t.Errorf("Got err '%v', expecting success", err) 87 | } 88 | 89 | s.SetOutputLevel(DebugLevel) 90 | s.SetStackTraceLevel(c.stackLevel) 91 | s.SetLogCallers(c.caller) 92 | 93 | c.f() 94 | _ = Sync() 95 | }) 96 | 97 | if err != nil { 98 | t.Errorf("Got error '%v', expected success", err) 99 | } 100 | 101 | if match, _ := regexp.MatchString(c.pat, lines[0]); !match { 102 | t.Errorf("Got '%v', expected a match with '%v'", lines[0], c.pat) 103 | } 104 | }) 105 | } 106 | } 107 | 108 | func TestScopeEnabled(t *testing.T) { 109 | const name = "TestEnabled" 110 | const desc = "Desc" 111 | s := RegisterScope(name, desc, 0) 112 | 113 | if n := s.Name(); n != name { 114 | t.Errorf("Got %s, expected %s", n, name) 115 | } 116 | 117 | if d := s.Description(); d != desc { 118 | t.Errorf("Got %s, expected %s", d, desc) 119 | } 120 | 121 | cases := []struct { 122 | level Level 123 | debugEnabled bool 124 | infoEnabled bool 125 | warnEnabled bool 126 | errorEnabled bool 127 | }{ 128 | {NoneLevel, false, false, false, false}, 129 | {ErrorLevel, false, false, false, true}, 130 | {WarnLevel, false, false, true, true}, 131 | {InfoLevel, false, true, true, true}, 132 | {DebugLevel, true, true, true, true}, 133 | } 134 | 135 | for i, c := range cases { 136 | t.Run(strconv.Itoa(i), func(t *testing.T) { 137 | s.SetOutputLevel(c.level) 138 | 139 | if c.debugEnabled != s.DebugEnabled() { 140 | t.Errorf("Got %v, expected %v", s.DebugEnabled(), c.debugEnabled) 141 | } 142 | 143 | if c.infoEnabled != s.InfoEnabled() { 144 | t.Errorf("Got %v, expected %v", s.InfoEnabled(), c.infoEnabled) 145 | } 146 | 147 | if c.warnEnabled != s.WarnEnabled() { 148 | t.Errorf("Got %v, expected %v", s.WarnEnabled(), c.warnEnabled) 149 | } 150 | 151 | if c.errorEnabled != s.ErrorEnabled() { 152 | t.Errorf("Got %v, expected %v", s.ErrorEnabled(), c.errorEnabled) 153 | } 154 | 155 | if c.level != s.GetOutputLevel() { 156 | t.Errorf("Got %v, expected %v", s.GetOutputLevel(), c.level) 157 | } 158 | }) 159 | } 160 | } 161 | 162 | func TestMultipleScopesWithSameName(t *testing.T) { 163 | z1 := RegisterScope("zzzz", "z", 0) 164 | z2 := RegisterScope("zzzz", "z", 0) 165 | 166 | if z1 != z2 { 167 | t.Error("Expecting the same scope objects, got different ones") 168 | } 169 | } 170 | 171 | func TestFind(t *testing.T) { 172 | if z := FindScope("TestFind"); z != nil { 173 | t.Error("Found scope, but expected it wouldn't exist") 174 | } 175 | 176 | _ = RegisterScope("TestFind", "", 0) 177 | 178 | if z := FindScope("TestFind"); z == nil { 179 | t.Error("Did not find scope, expected to find it") 180 | } 181 | } 182 | 183 | func TestBadNames(t *testing.T) { 184 | if s := RegisterScope("a:b", "", 0); s != nil { 185 | t.Error("Expecting to get nil") 186 | } 187 | 188 | if s := RegisterScope("a,b", "", 0); s != nil { 189 | t.Error("Expecting to get nil") 190 | } 191 | 192 | if s := RegisterScope("a.b", "", 0); s != nil { 193 | t.Error("Expecting to get nil") 194 | } 195 | } 196 | 197 | func TestBadWriter(t *testing.T) { 198 | o := DefaultOptions() 199 | if err := Configure(o); err != nil { 200 | t.Errorf("Got err '%v', expecting success", err) 201 | } 202 | 203 | writeFn.Store(func(zapcore.Entry, []zapcore.Field) error { 204 | return errors.New("bad") 205 | }) 206 | 207 | // for now, we just make sure this doesn't crash. To be totally correct, we'd need to capture stderr and 208 | // inspect it, but it's just not worth it 209 | defaultScope.Error("TestBadWriter") 210 | } 211 | -------------------------------------------------------------------------------- /scope.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tetrate, Inc 2018 All Rights Reserved. 2 | 3 | // Copyright 2018 Istio Authors 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package log // nolint: golint 18 | 19 | import ( 20 | "fmt" 21 | "runtime" 22 | "strings" 23 | "sync" 24 | "sync/atomic" 25 | "time" 26 | 27 | "go.uber.org/zap" 28 | "go.uber.org/zap/zapcore" 29 | ) 30 | 31 | // Scope let's you log data for an area of code, enabling the user full control over 32 | // the level of logging output produced. 33 | type Scope struct { 34 | // immutable, set at creation 35 | name string 36 | nameToEmit string 37 | description string 38 | callerSkip int 39 | 40 | // set by the Configure method and adjustable dynamically 41 | outputLevel atomic.Value 42 | stackTraceLevel atomic.Value 43 | logCallers atomic.Value 44 | } 45 | 46 | var scopes = make(map[string]*Scope) 47 | var lock = sync.Mutex{} 48 | 49 | // set by the Configure method 50 | var writeFn atomic.Value 51 | var errorSink atomic.Value 52 | 53 | // RegisterScope registers a new logging scope. If the same name is used multiple times 54 | // for a single process, the same Scope struct is returned. 55 | // 56 | // Scope names cannot include colons, commas, or periods. 57 | func RegisterScope(name string, description string, callerSkip int) *Scope { 58 | if strings.ContainsAny(name, ":,.") { 59 | return nil 60 | } 61 | 62 | lock.Lock() 63 | defer lock.Unlock() 64 | 65 | s, ok := scopes[name] 66 | if !ok { 67 | s = &Scope{ 68 | name: name, 69 | description: description, 70 | callerSkip: callerSkip, 71 | } 72 | s.SetOutputLevel(InfoLevel) 73 | s.SetStackTraceLevel(NoneLevel) 74 | s.SetLogCallers(false) 75 | 76 | if name != DefaultScopeName { 77 | s.nameToEmit = name 78 | } 79 | 80 | scopes[name] = s 81 | } 82 | 83 | return s 84 | } 85 | 86 | // FindScope returns a previously registered scope, or nil if the named scope wasn't previously registered 87 | func FindScope(scope string) *Scope { 88 | lock.Lock() 89 | defer lock.Unlock() 90 | 91 | s := scopes[scope] 92 | return s 93 | } 94 | 95 | // Scopes returns a snapshot of the currently defined set of scopes 96 | func Scopes() map[string]*Scope { 97 | lock.Lock() 98 | defer lock.Unlock() 99 | 100 | s := make(map[string]*Scope, len(scopes)) 101 | for k, v := range scopes { 102 | s[k] = v 103 | } 104 | 105 | return s 106 | } 107 | 108 | // Error outputs a message at error level. 109 | func (s *Scope) Error(msg string, fields ...zapcore.Field) { 110 | if s.GetOutputLevel() >= ErrorLevel { 111 | s.emit(zapcore.ErrorLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, fields) 112 | } 113 | } 114 | 115 | // Errora uses fmt.Sprint to construct and log a message at error level. 116 | func (s *Scope) Errora(args ...interface{}) { 117 | if s.GetOutputLevel() >= ErrorLevel { 118 | s.emit(zapcore.ErrorLevel, s.GetStackTraceLevel() >= ErrorLevel, fmt.Sprint(args...), nil) 119 | } 120 | } 121 | 122 | // Errorf uses fmt.Sprintf to construct and log a message at error level. 123 | func (s *Scope) Errorf(template string, args ...interface{}) { 124 | if s.GetOutputLevel() >= ErrorLevel { 125 | msg := template 126 | if len(args) > 0 { 127 | msg = fmt.Sprintf(template, args...) 128 | } 129 | s.emit(zapcore.ErrorLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, nil) 130 | } 131 | } 132 | 133 | // ErrorEnabled returns whether output of messages using this scope is currently enabled for error-level output. 134 | func (s *Scope) ErrorEnabled() bool { 135 | return s.GetOutputLevel() >= ErrorLevel 136 | } 137 | 138 | // Warn outputs a message at warn level. 139 | func (s *Scope) Warn(msg string, fields ...zapcore.Field) { 140 | if s.GetOutputLevel() >= WarnLevel { 141 | s.emit(zapcore.WarnLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, fields) 142 | } 143 | } 144 | 145 | // Warna uses fmt.Sprint to construct and log a message at warn level. 146 | func (s *Scope) Warna(args ...interface{}) { 147 | if s.GetOutputLevel() >= WarnLevel { 148 | s.emit(zapcore.WarnLevel, s.GetStackTraceLevel() >= ErrorLevel, fmt.Sprint(args...), nil) 149 | } 150 | } 151 | 152 | // Warnf uses fmt.Sprintf to construct and log a message at warn level. 153 | func (s *Scope) Warnf(template string, args ...interface{}) { 154 | if s.GetOutputLevel() >= WarnLevel { 155 | msg := template 156 | if len(args) > 0 { 157 | msg = fmt.Sprintf(template, args...) 158 | } 159 | s.emit(zapcore.WarnLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, nil) 160 | } 161 | } 162 | 163 | // WarnEnabled returns whether output of messages using this scope is currently enabled for warn-level output. 164 | func (s *Scope) WarnEnabled() bool { 165 | return s.GetOutputLevel() >= WarnLevel 166 | } 167 | 168 | // Info outputs a message at info level. 169 | func (s *Scope) Info(msg string, fields ...zapcore.Field) { 170 | if s.GetOutputLevel() >= InfoLevel { 171 | s.emit(zapcore.InfoLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, fields) 172 | } 173 | } 174 | 175 | // Infoa uses fmt.Sprint to construct and log a message at info level. 176 | func (s *Scope) Infoa(args ...interface{}) { 177 | if s.GetOutputLevel() >= InfoLevel { 178 | s.emit(zapcore.InfoLevel, s.GetStackTraceLevel() >= ErrorLevel, fmt.Sprint(args...), nil) 179 | } 180 | } 181 | 182 | // Infof uses fmt.Sprintf to construct and log a message at info level. 183 | func (s *Scope) Infof(template string, args ...interface{}) { 184 | if s.GetOutputLevel() >= InfoLevel { 185 | msg := template 186 | if len(args) > 0 { 187 | msg = fmt.Sprintf(template, args...) 188 | } 189 | s.emit(zapcore.InfoLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, nil) 190 | } 191 | } 192 | 193 | // InfoEnabled returns whether output of messages using this scope is currently enabled for info-level output. 194 | func (s *Scope) InfoEnabled() bool { 195 | return s.GetOutputLevel() >= InfoLevel 196 | } 197 | 198 | // Debug outputs a message at debug level. 199 | func (s *Scope) Debug(msg string, fields ...zapcore.Field) { 200 | if s.GetOutputLevel() >= DebugLevel { 201 | s.emit(zapcore.DebugLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, fields) 202 | } 203 | } 204 | 205 | // Debuga uses fmt.Sprint to construct and log a message at debug level. 206 | func (s *Scope) Debuga(args ...interface{}) { 207 | if s.GetOutputLevel() >= DebugLevel { 208 | s.emit(zapcore.DebugLevel, s.GetStackTraceLevel() >= ErrorLevel, fmt.Sprint(args...), nil) 209 | } 210 | } 211 | 212 | // Debugf uses fmt.Sprintf to construct and log a message at debug level. 213 | func (s *Scope) Debugf(template string, args ...interface{}) { 214 | if s.GetOutputLevel() >= DebugLevel { 215 | msg := template 216 | if len(args) > 0 { 217 | msg = fmt.Sprintf(template, args...) 218 | } 219 | s.emit(zapcore.DebugLevel, s.GetStackTraceLevel() >= ErrorLevel, msg, nil) 220 | } 221 | } 222 | 223 | // DebugEnabled returns whether output of messages using this scope is currently enabled for debug-level output. 224 | func (s *Scope) DebugEnabled() bool { 225 | return s.GetOutputLevel() >= DebugLevel 226 | } 227 | 228 | // Name returns this scope's name. 229 | func (s *Scope) Name() string { 230 | return s.name 231 | } 232 | 233 | // Description returns this scope's description 234 | func (s *Scope) Description() string { 235 | return s.description 236 | } 237 | 238 | const callerSkipOffset = 2 239 | 240 | func (s *Scope) emit(level zapcore.Level, dumpStack bool, msg string, fields []zapcore.Field) { 241 | e := zapcore.Entry{ 242 | Message: msg, 243 | Level: level, 244 | Time: time.Now(), 245 | LoggerName: s.nameToEmit, 246 | } 247 | 248 | if s.GetLogCallers() { 249 | e.Caller = zapcore.NewEntryCaller(runtime.Caller(s.callerSkip + callerSkipOffset)) 250 | } 251 | 252 | if dumpStack { 253 | e.Stack = zap.Stack("").String 254 | } 255 | 256 | if w := writeFn.Load().(func(zapcore.Entry, []zapcore.Field) error); w != nil { 257 | if err := w(e, fields); err != nil { 258 | if es := errorSink.Load().(zapcore.WriteSyncer); es != nil { 259 | _, _ = fmt.Fprintf(es, "%v log write error: %v\n", time.Now(), err) 260 | _ = es.Sync() 261 | } 262 | } 263 | } 264 | } 265 | 266 | // SetOutputLevel adjusts the output level associated with the scope. 267 | func (s *Scope) SetOutputLevel(l Level) { 268 | s.outputLevel.Store(l) 269 | } 270 | 271 | // GetOutputLevel returns the output level associated with the scope. 272 | func (s *Scope) GetOutputLevel() Level { 273 | return s.outputLevel.Load().(Level) 274 | } 275 | 276 | // SetStackTraceLevel adjusts the stack tracing level associated with the scope. 277 | func (s *Scope) SetStackTraceLevel(l Level) { 278 | s.stackTraceLevel.Store(l) 279 | } 280 | 281 | // GetStackTraceLevel returns the stack tracing level associated with the scope. 282 | func (s *Scope) GetStackTraceLevel() Level { 283 | return s.stackTraceLevel.Load().(Level) 284 | } 285 | 286 | // SetLogCallers adjusts the output level associated with the scope. 287 | func (s *Scope) SetLogCallers(logCallers bool) { 288 | s.logCallers.Store(logCallers) 289 | } 290 | 291 | // GetLogCallers returns the output level associated with the scope. 292 | func (s *Scope) GetLogCallers() bool { 293 | return s.logCallers.Load().(bool) 294 | } 295 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tetrate, Inc 2018 All Rights Reserved. 2 | 3 | // Copyright 2017 Istio Authors 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | // Package log provides the canonical logging functionality used by Go-based 18 | // Istio components. 19 | // 20 | // Istio's logging subsystem is built on top of the [Zap](https://godoc.org/go.uber.org/zap) package. 21 | // High performance scenarios should use the Error, Warn, Info, and Debug methods. Lower perf 22 | // scenarios can use the more expensive convenience methods such as Debugf and Warnw. 23 | // 24 | // The package provides direct integration with the Cobra command-line processor which makes it 25 | // easy to build programs that use a consistent interface for logging. Here's an example 26 | // of a simple Cobra-based program using this log package: 27 | // 28 | // func main() { 29 | // // get the default logging options 30 | // options := log.DefaultOptions() 31 | // 32 | // rootCmd := &cobra.Command{ 33 | // Run: func(cmd *cobra.Command, args []string) { 34 | // 35 | // // configure the logging system 36 | // if err := log.Configure(options); err != nil { 37 | // // print an error and quit 38 | // } 39 | // 40 | // // output some logs 41 | // log.Info("Hello") 42 | // log.Sync() 43 | // }, 44 | // } 45 | // 46 | // // add logging-specific flags to the cobra command 47 | // options.AttachFlags(rootCmd) 48 | // rootCmd.SetArgs(os.Args[1:]) 49 | // rootCmd.Execute() 50 | // } 51 | // 52 | // Once configured, this package intercepts the output of the standard golang "log" package as well as anything 53 | // sent to the global zap logger (zap.L()). 54 | package log 55 | 56 | import ( 57 | "fmt" 58 | "os" 59 | "sort" 60 | "strings" 61 | "sync/atomic" 62 | "time" 63 | 64 | "go.uber.org/zap" 65 | "go.uber.org/zap/zapcore" 66 | "go.uber.org/zap/zapgrpc" 67 | "google.golang.org/grpc/grpclog" 68 | "gopkg.in/natefinch/lumberjack.v2" 69 | ) 70 | 71 | // none is used to disable logging output as well as to disable stack tracing. 72 | const none zapcore.Level = 100 73 | 74 | var levelToZap = map[Level]zapcore.Level{ 75 | DebugLevel: zapcore.DebugLevel, 76 | InfoLevel: zapcore.InfoLevel, 77 | WarnLevel: zapcore.WarnLevel, 78 | ErrorLevel: zapcore.ErrorLevel, 79 | NoneLevel: none, 80 | } 81 | 82 | func init() { 83 | // use our defaults for starters so that logging works even before everything is fully configured 84 | _ = Configure(DefaultOptions()) 85 | } 86 | 87 | // prepZap is a utility function used by the Configure function. 88 | func prepZap(options *Options) (zapcore.Core, zapcore.Core, zapcore.WriteSyncer, error) { 89 | encCfg := zapcore.EncoderConfig{ 90 | TimeKey: "time", 91 | LevelKey: "level", 92 | NameKey: "scope", 93 | CallerKey: "caller", 94 | MessageKey: "msg", 95 | StacktraceKey: "stack", 96 | LineEnding: zapcore.DefaultLineEnding, 97 | EncodeLevel: zapcore.LowercaseLevelEncoder, 98 | EncodeCaller: zapcore.ShortCallerEncoder, 99 | EncodeDuration: zapcore.StringDurationEncoder, 100 | EncodeTime: formatDate, 101 | } 102 | 103 | var enc zapcore.Encoder 104 | if options.JSONEncoding { 105 | enc = zapcore.NewJSONEncoder(encCfg) 106 | } else { 107 | enc = zapcore.NewConsoleEncoder(encCfg) 108 | } 109 | 110 | var rotaterSink zapcore.WriteSyncer 111 | if options.RotateOutputPath != "" { 112 | rotaterSink = zapcore.AddSync(&lumberjack.Logger{ 113 | Filename: options.RotateOutputPath, 114 | MaxSize: options.RotationMaxSize, 115 | MaxBackups: options.RotationMaxAge, 116 | MaxAge: options.RotationMaxBackups, 117 | }) 118 | } 119 | 120 | errSink, closeErrorSink, err := zap.Open(options.ErrorOutputPaths...) 121 | if err != nil { 122 | return nil, nil, nil, err 123 | } 124 | 125 | var outputSink zapcore.WriteSyncer 126 | if len(options.OutputPaths) > 0 { 127 | outputSink, _, err = zap.Open(options.OutputPaths...) 128 | if err != nil { 129 | closeErrorSink() 130 | return nil, nil, nil, err 131 | } 132 | } 133 | 134 | var sink zapcore.WriteSyncer 135 | if rotaterSink != nil && outputSink != nil { 136 | sink = zapcore.NewMultiWriteSyncer(outputSink, rotaterSink) 137 | } else if rotaterSink != nil { 138 | sink = rotaterSink 139 | } else { 140 | sink = outputSink 141 | } 142 | 143 | var enabler zap.LevelEnablerFunc = func(lvl zapcore.Level) bool { 144 | switch lvl { 145 | case zapcore.ErrorLevel: 146 | return defaultScope.ErrorEnabled() 147 | case zapcore.WarnLevel: 148 | return defaultScope.WarnEnabled() 149 | case zapcore.InfoLevel: 150 | return defaultScope.InfoEnabled() 151 | } 152 | return defaultScope.DebugEnabled() 153 | } 154 | 155 | return zapcore.NewCore(enc, sink, zap.NewAtomicLevelAt(zapcore.DebugLevel)), 156 | zapcore.NewCore(enc, sink, enabler), 157 | errSink, nil 158 | } 159 | 160 | func formatDate(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 161 | t = t.UTC() 162 | year, month, day := t.Date() 163 | hour, minute, second := t.Clock() 164 | micros := t.Nanosecond() / 1000 165 | 166 | buf := make([]byte, 27) 167 | 168 | buf[0] = byte((year/1000)%10) + '0' 169 | buf[1] = byte((year/100)%10) + '0' 170 | buf[2] = byte((year/10)%10) + '0' 171 | buf[3] = byte(year%10) + '0' 172 | buf[4] = '-' 173 | buf[5] = byte((month)/10) + '0' 174 | buf[6] = byte((month)%10) + '0' 175 | buf[7] = '-' 176 | buf[8] = byte((day)/10) + '0' 177 | buf[9] = byte((day)%10) + '0' 178 | buf[10] = 'T' 179 | buf[11] = byte((hour)/10) + '0' 180 | buf[12] = byte((hour)%10) + '0' 181 | buf[13] = ':' 182 | buf[14] = byte((minute)/10) + '0' 183 | buf[15] = byte((minute)%10) + '0' 184 | buf[16] = ':' 185 | buf[17] = byte((second)/10) + '0' 186 | buf[18] = byte((second)%10) + '0' 187 | buf[19] = '.' 188 | buf[20] = byte((micros/100000)%10) + '0' 189 | buf[21] = byte((micros/10000)%10) + '0' 190 | buf[22] = byte((micros/1000)%10) + '0' 191 | buf[23] = byte((micros/100)%10) + '0' 192 | buf[24] = byte((micros/10)%10) + '0' 193 | buf[25] = byte((micros)%10) + '0' 194 | buf[26] = 'Z' 195 | 196 | enc.AppendString(string(buf)) 197 | } 198 | 199 | func updateScopes(options *Options, core zapcore.Core, errSink zapcore.WriteSyncer) error { 200 | // init the global I/O funcs 201 | writeFn.Store(core.Write) 202 | syncFn.Store(core.Sync) 203 | errorSink.Store(errSink) 204 | 205 | // snapshot what's there 206 | allScopes := Scopes() 207 | 208 | // update the output levels of all scopes 209 | if err := processLevels(allScopes, options.outputLevels, func(s *Scope, l Level) { s.SetOutputLevel(l) }); err != nil { 210 | return err 211 | } 212 | 213 | // update the stack tracing levels of all scopes 214 | if err := processLevels(allScopes, options.stackTraceLevels, func(s *Scope, l Level) { s.SetStackTraceLevel(l) }); err != nil { 215 | return err 216 | } 217 | 218 | // update the caller location setting of all scopes 219 | sc := strings.Split(options.logCallers, ",") 220 | for _, s := range sc { 221 | if s == "" { 222 | continue 223 | } 224 | 225 | if s == OverrideScopeName { 226 | // ignore everything else and just apply the override value 227 | for _, scope := range allScopes { 228 | scope.SetLogCallers(true) 229 | } 230 | 231 | return nil 232 | } 233 | 234 | if scope, ok := allScopes[s]; ok { 235 | scope.SetLogCallers(true) 236 | } else { 237 | _, _ = fmt.Fprintf(os.Stderr, "unknown scope '%s' specified", s) 238 | } 239 | } 240 | 241 | return nil 242 | } 243 | 244 | // processLevels breaks down an argument string into a set of scope & levels and then 245 | // tries to apply the result to the scopes. It supports the use of a global override. 246 | func processLevels(allScopes map[string]*Scope, arg string, setter func(*Scope, Level)) error { 247 | levels := strings.Split(arg, ",") 248 | for _, sl := range levels { 249 | s, l, err := convertScopedLevel(sl) 250 | if err != nil { 251 | return err 252 | } 253 | 254 | if scope, ok := allScopes[s]; ok { 255 | setter(scope, l) 256 | } else if s == OverrideScopeName { 257 | // override replaces everything 258 | for _, scope := range allScopes { 259 | setter(scope, l) 260 | } 261 | return nil 262 | } else { 263 | _, _ = fmt.Fprintf(os.Stderr, "unknown scope '%s' specified\n", s) 264 | } 265 | } 266 | 267 | return nil 268 | } 269 | 270 | // Configure initializes Istio's logging subsystem. 271 | // 272 | // You typically call this once at process startup. 273 | // Once this call returns, the logging system is ready to accept data. 274 | func Configure(options *Options) error { 275 | core, captureCore, errSink, err := prepZap(options) 276 | if err != nil { 277 | return err 278 | } 279 | 280 | if err = updateScopes(options, core, errSink); err != nil { 281 | return err 282 | } 283 | 284 | opts := []zap.Option{ 285 | zap.ErrorOutput(errSink), 286 | zap.AddCallerSkip(1), 287 | } 288 | 289 | if defaultScope.GetLogCallers() { 290 | opts = append(opts, zap.AddCaller()) 291 | } 292 | 293 | l := defaultScope.GetStackTraceLevel() 294 | if l != NoneLevel { 295 | opts = append(opts, zap.AddStacktrace(levelToZap[l])) 296 | } 297 | 298 | captureLogger := zap.New(captureCore, opts...) 299 | 300 | // capture global zap logging and force it through our logger 301 | _ = zap.ReplaceGlobals(captureLogger) 302 | 303 | // capture standard golang "log" package output and force it through our logger 304 | _ = zap.RedirectStdLog(captureLogger) 305 | 306 | // capture gRPC logging 307 | if options.LogGrpc { 308 | // TODO(https://github.com/uber-go/zap/issues/534): remove the nolint directive 309 | grpclog.SetLogger(zapgrpc.NewLogger(captureLogger.WithOptions(zap.AddCallerSkip(2)))) //nolint: megacheck 310 | } 311 | 312 | return nil 313 | } 314 | 315 | // reset by the Configure method 316 | var syncFn atomic.Value 317 | 318 | // Sync flushes any buffered log entries. 319 | // Processes should normally take care to call Sync before exiting. 320 | func Sync() error { 321 | var err error 322 | if s := syncFn.Load().(func() error); s != nil { 323 | err = s() 324 | } 325 | 326 | return err 327 | } 328 | 329 | // PrintRegisteredScopes logs all the registered scopes and their configured output level using` the default logger 330 | func PrintRegisteredScopes() { 331 | s := Scopes() 332 | pad := 0 333 | 334 | names := make([]string, 0, len(s)) 335 | for n := range s { 336 | names = append(names, n) 337 | if len(n) > pad { 338 | pad = len(n) 339 | } 340 | } 341 | sort.Strings(names) 342 | 343 | Info("registered logging scopes:") 344 | for _, n := range names { 345 | sc := s[n] 346 | Infof("- %-*s %-5s %s", pad, sc.Name(), levelToString[sc.GetOutputLevel()], sc.Description()) 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tetrate, Inc 2018 All Rights Reserved. 2 | 3 | // Copyright 2017 Istio Authors 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package log 18 | 19 | import ( 20 | "io/ioutil" 21 | "log" 22 | "os" 23 | "regexp" 24 | "strconv" 25 | "strings" 26 | "testing" 27 | "time" 28 | 29 | "go.uber.org/zap" 30 | "go.uber.org/zap/zapcore" 31 | "google.golang.org/grpc/grpclog" 32 | ) 33 | 34 | const timePattern = "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9][0-9][0-9][0-9]Z" 35 | 36 | type testDateEncoder struct { 37 | zapcore.PrimitiveArrayEncoder 38 | output string 39 | } 40 | 41 | func (tde *testDateEncoder) AppendString(s string) { 42 | tde.output = s 43 | } 44 | 45 | func TestTimestampProperYear(t *testing.T) { 46 | testEnc := &testDateEncoder{} 47 | cases := []struct { 48 | name string 49 | input time.Time 50 | want string 51 | }{ 52 | {"1", time.Date(1, time.April, 1, 1, 1, 1, 1, time.UTC), "0001"}, 53 | {"1989", time.Date(1989, time.February, 1, 1, 1, 1, 1, time.UTC), "1989"}, 54 | {"2017", time.Date(2017, time.January, 1, 1, 1, 1, 1, time.UTC), "2017"}, 55 | {"2083", time.Date(2083, time.March, 1, 1, 1, 1, 1, time.UTC), "2083"}, 56 | {"2573", time.Date(2573, time.June, 1, 1, 1, 1, 1, time.UTC), "2573"}, 57 | {"9999", time.Date(9999, time.May, 1, 1, 1, 1, 1, time.UTC), "9999"}, 58 | } 59 | 60 | for _, v := range cases { 61 | t.Run(v.name, func(t *testing.T) { 62 | formatDate(v.input, testEnc) 63 | if !strings.HasPrefix(testEnc.output, v.want) { 64 | t.Errorf("formatDate(%v) => %s, want year: %s", v.input, testEnc.output, v.want) 65 | } 66 | }) 67 | } 68 | } 69 | 70 | func TestTimestampProperMicros(t *testing.T) { 71 | testEnc := &testDateEncoder{} 72 | cases := []struct { 73 | name string 74 | input time.Time 75 | want string 76 | }{ 77 | {"1", time.Date(2017, time.April, 1, 1, 1, 1, 1000, time.UTC), "1"}, 78 | {"99", time.Date(1989, time.February, 1, 1, 1, 1, 99000, time.UTC), "99"}, 79 | {"999", time.Date(2017, time.January, 1, 1, 1, 1, 999000, time.UTC), "999"}, 80 | {"9999", time.Date(2083, time.March, 1, 1, 1, 1, 9999000, time.UTC), "9999"}, 81 | {"99999", time.Date(2083, time.March, 1, 1, 1, 1, 99999000, time.UTC), "99999"}, 82 | {"999999", time.Date(2083, time.March, 1, 1, 1, 1, 999999000, time.UTC), "999999"}, 83 | } 84 | 85 | for _, v := range cases { 86 | t.Run(v.name, func(t *testing.T) { 87 | formatDate(v.input, testEnc) 88 | if !strings.HasSuffix(testEnc.output, v.want+"Z") { 89 | t.Errorf("formatDate(%v) => %s, want micros: %s", v.input, testEnc.output, v.want) 90 | } 91 | }) 92 | } 93 | } 94 | 95 | func TestOddballs(t *testing.T) { 96 | resetGlobals() 97 | 98 | o := DefaultOptions() 99 | _ = Configure(o) 100 | 101 | o = DefaultOptions() 102 | o.outputLevels = "default,," 103 | err := Configure(o) 104 | if err == nil { 105 | t.Error("Got success, expected failure") 106 | } 107 | 108 | o = DefaultOptions() 109 | o.outputLevels = "foobar" 110 | err = Configure(o) 111 | if err == nil { 112 | t.Error("Got success, expected failure") 113 | } 114 | 115 | o = DefaultOptions() // Unknown scopes should be allowed 116 | o.outputLevels = "foobar:debug" 117 | err = Configure(o) 118 | if err != nil { 119 | t.Error(err) 120 | } 121 | 122 | o = DefaultOptions() 123 | o.stackTraceLevels = "default,," 124 | err = Configure(o) 125 | if err == nil { 126 | t.Error("Got success, expected failure") 127 | } 128 | 129 | o = DefaultOptions() 130 | o.stackTraceLevels = "foobar" 131 | err = Configure(o) 132 | if err == nil { 133 | t.Error("Got success, expected failure") 134 | } 135 | 136 | o = DefaultOptions() // Unknown scopes should be allowed 137 | o.stackTraceLevels = "foobar:debug" 138 | err = Configure(o) 139 | if err != nil { 140 | t.Error(err) 141 | } 142 | 143 | o = DefaultOptions() // Unknown scopes should be allowed 144 | o.logCallers = "foobar" 145 | err = Configure(o) 146 | if err != nil { 147 | t.Error(err) 148 | } 149 | 150 | o = DefaultOptions() 151 | //using invalid filename 152 | o.OutputPaths = []string{"//"} 153 | err = Configure(o) 154 | if err == nil { 155 | t.Errorf("Got success, expecting error") 156 | } 157 | 158 | o = DefaultOptions() 159 | o.ErrorOutputPaths = []string{"//"} 160 | err = Configure(o) 161 | if err == nil { 162 | t.Errorf("Got success, expecting error") 163 | } 164 | } 165 | 166 | func TestRotateNoStdout(t *testing.T) { 167 | // Ensure that rotation is setup properly 168 | 169 | dir, _ := ioutil.TempDir("", "TestRotateNoStdout") 170 | defer func() { 171 | _ = os.RemoveAll(dir) 172 | }() 173 | 174 | file := dir + "/rot.log" 175 | 176 | o := DefaultOptions() 177 | o.OutputPaths = []string{} 178 | o.RotateOutputPath = file 179 | if err := Configure(o); err != nil { 180 | t.Fatalf("Unable to configure logging: %v", err) 181 | } 182 | 183 | defaultScope.Error("HELLO") 184 | Sync() // nolint: errcheck 185 | 186 | content, err := ioutil.ReadFile(file) 187 | if err != nil { 188 | t.Errorf("Got failure '%v', expecting success", err) 189 | } 190 | 191 | lines := strings.Split(string(content), "\n") 192 | if !strings.Contains(lines[0], "HELLO") { 193 | t.Errorf("Expecting for first line of log to contain HELLO, got %s", lines[0]) 194 | } 195 | } 196 | 197 | func TestRotateAndStdout(t *testing.T) { 198 | dir, _ := ioutil.TempDir("", "TestRotateAndStdout") 199 | defer func() { 200 | _ = os.RemoveAll(dir) 201 | }() 202 | 203 | file := dir + "/rot.log" 204 | 205 | stdoutLines, _ := captureStdout(func() { 206 | o := DefaultOptions() 207 | o.RotateOutputPath = file 208 | if err := Configure(o); err != nil { 209 | t.Fatalf("Unable to configure logger: %v", err) 210 | } 211 | 212 | defaultScope.Error("HELLO") 213 | Sync() // nolint: errcheck 214 | 215 | content, err := ioutil.ReadFile(file) 216 | if err != nil { 217 | t.Errorf("Got failure '%v', expecting success", err) 218 | } 219 | 220 | rotLines := strings.Split(string(content), "\n") 221 | if !strings.Contains(rotLines[0], "HELLO") { 222 | t.Errorf("Expecting for first line of log to contain HELLO, got %s", rotLines[0]) 223 | } 224 | }) 225 | 226 | if !strings.Contains(stdoutLines[0], "HELLO") { 227 | t.Errorf("Expecting for first line of log to contain HELLO, got %s", stdoutLines[0]) 228 | } 229 | } 230 | 231 | func TestCapture(t *testing.T) { 232 | lines, _ := captureStdout(func() { 233 | o := DefaultOptions() 234 | o.SetLogCallers(DefaultScopeName, true) 235 | o.SetOutputLevel(DefaultScopeName, DebugLevel) 236 | _ = Configure(o) 237 | 238 | // output to the plain golang "log" package 239 | log.Println("golang") 240 | 241 | // output to the gRPC logging package 242 | grpclog.Error("grpc-error") 243 | grpclog.Warning("grpc-warn") 244 | grpclog.Info("grpc-info") 245 | 246 | // output directly to zap 247 | zap.L().Error("zap-error") 248 | zap.L().Warn("zap-warn") 249 | zap.L().Info("zap-info") 250 | zap.L().Debug("zap-debug") 251 | 252 | l := zap.L().With(zap.String("a", "b")) 253 | l.Error("zap-with") 254 | 255 | entry := zapcore.Entry{ 256 | Message: "zap-write", 257 | Level: zapcore.ErrorLevel, 258 | } 259 | _ = zap.L().Core().Write(entry, nil) 260 | 261 | defaultScope.SetOutputLevel(NoneLevel) 262 | 263 | // all these get thrown out since the level is set to none 264 | log.Println("golang-2") 265 | zap.L().Error("zap-error-2") 266 | zap.L().Warn("zap-warn-2") 267 | zap.L().Info("zap-info-2") 268 | zap.L().Debug("zap-debug-2") 269 | }) 270 | 271 | patterns := []string{ 272 | timePattern + "\tinfo\tlog/config_test.go:.*\tgolang", 273 | timePattern + "\tinfo\tlog/config_test.go:.*\tgrpc-error", // gRPC errors and warnings come out as info 274 | timePattern + "\tinfo\tlog/config_test.go:.*\tgrpc-warn", 275 | timePattern + "\tinfo\tlog/config_test.go:.*\tgrpc-info", 276 | timePattern + "\terror\tlog/config_test.go:.*\tzap-error", 277 | timePattern + "\twarn\tlog/config_test.go:.*\tzap-warn", 278 | timePattern + "\tinfo\tlog/config_test.go:.*\tzap-info", 279 | timePattern + "\tdebug\tlog/config_test.go:.*\tzap-debug", 280 | timePattern + "\terror\tlog/config_test.go:.*\tzap-with", 281 | timePattern + "\terror\tzap-write", 282 | "", 283 | } 284 | 285 | if len(lines) > len(patterns) { 286 | t.Errorf("Expecting %d lines of output, but got %d", len(patterns), len(lines)) 287 | 288 | for i := len(patterns); i < len(lines); i++ { 289 | t.Errorf(" Extra line of output: %s", lines[i]) 290 | } 291 | } 292 | 293 | for i, pat := range patterns { 294 | t.Run(strconv.Itoa(i), func(t *testing.T) { 295 | match, _ := regexp.MatchString(pat, lines[i]) 296 | if !match { 297 | t.Errorf("Got '%s', expecting to match '%s'", lines[i], pat) 298 | } 299 | }) 300 | } 301 | 302 | lines, _ = captureStdout(func() { 303 | o := DefaultOptions() 304 | o.SetStackTraceLevel(DefaultScopeName, DebugLevel) 305 | o.SetOutputLevel(DefaultScopeName, DebugLevel) 306 | _ = Configure(o) 307 | log.Println("golang") 308 | }) 309 | 310 | for _, line := range lines { 311 | // see if the captured output contains the current file name 312 | if strings.Contains(line, "config_test.go") { 313 | return 314 | } 315 | } 316 | 317 | t.Error("Could not find stack trace info in output") 318 | } 319 | 320 | func TestOverrides(t *testing.T) { 321 | resetGlobals() 322 | s := RegisterScope("TestOverrides", "For testing", 0) 323 | 324 | o := DefaultOptions() 325 | o.outputLevels = "default:debug,all:info" 326 | if err := Configure(o); err != nil { 327 | t.Errorf("Expecting success, got %v", err) 328 | } 329 | if s.GetOutputLevel() != InfoLevel { 330 | t.Errorf("Expecting InfoLevel, got %v", s.GetOutputLevel()) 331 | } 332 | if defaultScope.GetOutputLevel() != InfoLevel { 333 | t.Errorf("Expecting InfoLevel, got %v", defaultScope.GetOutputLevel()) 334 | } 335 | 336 | o = DefaultOptions() 337 | o.stackTraceLevels = "default:debug,all:info" 338 | if err := Configure(o); err != nil { 339 | t.Errorf("Expecting success, got %v", err) 340 | } 341 | if s.GetStackTraceLevel() != InfoLevel { 342 | t.Errorf("Expecting InfoLevel, got %v", s.GetStackTraceLevel()) 343 | } 344 | if defaultScope.GetStackTraceLevel() != InfoLevel { 345 | t.Errorf("Expecting InfoLevel, got %v", defaultScope.GetStackTraceLevel()) 346 | } 347 | 348 | o = DefaultOptions() 349 | o.logCallers = "all" 350 | if err := Configure(o); err != nil { 351 | t.Errorf("Expecting success, got %v", err) 352 | } 353 | if !s.GetLogCallers() { 354 | t.Error("Expecting true, got false") 355 | } 356 | if !defaultScope.GetLogCallers() { 357 | t.Error("Expecting true, got false") 358 | } 359 | } 360 | 361 | // Runs the given function while capturing everything sent to stdout 362 | func captureStdout(f func()) ([]string, error) { 363 | tf, err := ioutil.TempFile("", "log_test") 364 | if err != nil { 365 | return nil, err 366 | } 367 | 368 | old := os.Stdout 369 | os.Stdout = tf 370 | 371 | f() 372 | 373 | os.Stdout = old 374 | path := tf.Name() 375 | _ = tf.Sync() 376 | _ = tf.Close() 377 | 378 | content, err := ioutil.ReadFile(path) 379 | _ = os.Remove(path) 380 | 381 | if err != nil { 382 | return nil, err 383 | } 384 | 385 | return strings.Split(string(content), "\n"), nil 386 | } 387 | 388 | func resetGlobals() { 389 | scopes = make(map[string]*Scope, 1) 390 | defaultScope = registerDefaultScope() 391 | } 392 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tetrate, Inc 2018 All Rights Reserved. 2 | 3 | // Copyright 2017 Istio Authors 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package log // nolint: golint 18 | 19 | import ( 20 | "fmt" 21 | "sort" 22 | "strings" 23 | 24 | "github.com/spf13/cobra" 25 | "github.com/spf13/pflag" 26 | ) 27 | 28 | const ( 29 | // DefaultScopeName defines the name of the default scope. 30 | DefaultScopeName = "default" 31 | OverrideScopeName = "all" 32 | defaultOutputLevel = InfoLevel 33 | defaultStackTraceLevel = NoneLevel 34 | defaultOutputPath = "stdout" 35 | defaultErrorOutputPath = "stderr" 36 | defaultRotationMaxAge = 30 37 | defaultRotationMaxSize = 100 * 1024 * 1024 38 | defaultRotationMaxBackups = 1000 39 | ) 40 | 41 | // Level is an enumeration of all supported log levels. 42 | type Level int 43 | 44 | // String returns the name of the level 45 | func (l Level) String() string { 46 | return levelToString[l] 47 | } 48 | 49 | // LevelFrom returns the level for the given name 50 | func LevelFrom(name string) (Level, bool) { 51 | l, ok := stringToLevel[name] 52 | return l, ok 53 | } 54 | 55 | const ( 56 | // NoneLevel disables logging 57 | NoneLevel Level = iota 58 | // ErrorLevel enables error level logging 59 | ErrorLevel 60 | // WarnLevel enables warn level logging 61 | WarnLevel 62 | // InfoLevel enables info level logging 63 | InfoLevel 64 | // DebugLevel enables debug level logging 65 | DebugLevel 66 | ) 67 | 68 | var levelToString = map[Level]string{ 69 | DebugLevel: "debug", 70 | InfoLevel: "info", 71 | WarnLevel: "warn", 72 | ErrorLevel: "error", 73 | NoneLevel: "none", 74 | } 75 | 76 | var stringToLevel = map[string]Level{ 77 | "debug": DebugLevel, 78 | "info": InfoLevel, 79 | "warn": WarnLevel, 80 | "error": ErrorLevel, 81 | "none": NoneLevel, 82 | } 83 | 84 | // Options defines the set of options supported by Istio's component logging package. 85 | type Options struct { 86 | // OutputPaths is a list of file system paths to write the log data to. 87 | // The special values stdout and stderr can be used to output to the 88 | // standard I/O streams. This defaults to stdout. 89 | OutputPaths []string 90 | 91 | // ErrorOutputPaths is a list of file system paths to write logger errors to. 92 | // The special values stdout and stderr can be used to output to the 93 | // standard I/O streams. This defaults to stderr. 94 | ErrorOutputPaths []string 95 | 96 | // RotateOutputPath is the path to a rotating log file. This file should 97 | // be automatically rotated over time, based on the rotation parameters such 98 | // as RotationMaxSize and RotationMaxAge. The default is to not rotate. 99 | // 100 | // This path is used as a foundational path. This is where log output is normally 101 | // saved. When a rotation needs to take place because the file got too big or too 102 | // old, then the file is renamed by appending a timestamp to the name. Such renamed 103 | // files are called backups. Once a backup has been created, 104 | // output resumes to this path. 105 | RotateOutputPath string 106 | 107 | // RotationMaxSize is the maximum size in megabytes of a log file before it gets 108 | // rotated. It defaults to 100 megabytes. 109 | RotationMaxSize int 110 | 111 | // RotationMaxAge is the maximum number of days to retain old log files based on the 112 | // timestamp encoded in their filename. Note that a day is defined as 24 113 | // hours and may not exactly correspond to calendar days due to daylight 114 | // savings, leap seconds, etc. The default is to remove log files 115 | // older than 30 days. 116 | RotationMaxAge int 117 | 118 | // RotationMaxBackups is the maximum number of old log files to retain. The default 119 | // is to retain at most 1000 logs. 120 | RotationMaxBackups int 121 | 122 | // JSONEncoding controls whether the log is formatted as JSON. 123 | JSONEncoding bool 124 | 125 | // LogGrpc indicates that Grpc logs should be captured. The default is true. 126 | // This is not exposed through the command-line flags, as this flag is mainly useful for testing: Grpc 127 | // stack will hold on to the logger even though it gets closed. This causes data races. 128 | LogGrpc bool 129 | 130 | outputLevels string 131 | logCallers string 132 | stackTraceLevels string 133 | } 134 | 135 | // DefaultOptions returns a new set of options, initialized to the defaults 136 | func DefaultOptions() *Options { 137 | return &Options{ 138 | OutputPaths: []string{defaultOutputPath}, 139 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 140 | RotationMaxSize: defaultRotationMaxSize, 141 | RotationMaxAge: defaultRotationMaxAge, 142 | RotationMaxBackups: defaultRotationMaxBackups, 143 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 144 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 145 | LogGrpc: true, 146 | } 147 | } 148 | 149 | // SetOutputLevel sets the minimum log output level for a given scope. 150 | func (o *Options) SetOutputLevel(scope string, level Level) { 151 | sl := scope + ":" + levelToString[level] 152 | levels := strings.Split(o.outputLevels, ",") 153 | 154 | if scope == DefaultScopeName { 155 | // see if we have an entry without a scope prefix (which represents the default scope) 156 | for i, ol := range levels { 157 | if !strings.Contains(ol, ":") { 158 | levels[i] = sl 159 | o.outputLevels = strings.Join(levels, ",") 160 | return 161 | } 162 | } 163 | } 164 | 165 | prefix := scope + ":" 166 | for i, ol := range levels { 167 | if strings.HasPrefix(ol, prefix) { 168 | levels[i] = sl 169 | o.outputLevels = strings.Join(levels, ",") 170 | return 171 | } 172 | } 173 | 174 | levels = append(levels, sl) 175 | o.outputLevels = strings.Join(levels, ",") 176 | } 177 | 178 | // GetOutputLevel returns the minimum log output level for a given scope. 179 | func (o *Options) GetOutputLevel(scope string) (Level, error) { 180 | levels := strings.Split(o.outputLevels, ",") 181 | 182 | if scope == DefaultScopeName { 183 | // see if we have an entry without a scope prefix (which represents the default scope) 184 | for _, ol := range levels { 185 | if !strings.Contains(ol, ":") { 186 | _, l, err := convertScopedLevel(ol) 187 | return l, err 188 | } 189 | } 190 | } 191 | 192 | prefix := scope + ":" 193 | for _, ol := range levels { 194 | if strings.HasPrefix(ol, prefix) { 195 | _, l, err := convertScopedLevel(ol) 196 | return l, err 197 | } 198 | } 199 | 200 | return NoneLevel, fmt.Errorf("no level defined for scope '%s'", scope) 201 | } 202 | 203 | // SetStackTraceLevel sets the minimum stack tracing level for a given scope. 204 | func (o *Options) SetStackTraceLevel(scope string, level Level) { 205 | sl := scope + ":" + levelToString[level] 206 | levels := strings.Split(o.stackTraceLevels, ",") 207 | 208 | if scope == DefaultScopeName { 209 | // see if we have an entry without a scope prefix (which represents the default scope) 210 | for i, ol := range levels { 211 | if !strings.Contains(ol, ":") { 212 | levels[i] = sl 213 | o.stackTraceLevels = strings.Join(levels, ",") 214 | return 215 | } 216 | } 217 | } 218 | 219 | prefix := scope + ":" 220 | for i, ol := range levels { 221 | if strings.HasPrefix(ol, prefix) { 222 | levels[i] = sl 223 | o.stackTraceLevels = strings.Join(levels, ",") 224 | return 225 | } 226 | } 227 | 228 | levels = append(levels, sl) 229 | o.stackTraceLevels = strings.Join(levels, ",") 230 | } 231 | 232 | // GetStackTraceLevel returns the minimum stack tracing level for a given scope. 233 | func (o *Options) GetStackTraceLevel(scope string) (Level, error) { 234 | levels := strings.Split(o.stackTraceLevels, ",") 235 | 236 | if scope == DefaultScopeName { 237 | // see if we have an entry without a scope prefix (which represents the default scope) 238 | for _, ol := range levels { 239 | if !strings.Contains(ol, ":") { 240 | _, l, err := convertScopedLevel(ol) 241 | return l, err 242 | } 243 | } 244 | } 245 | 246 | prefix := scope + ":" 247 | for _, ol := range levels { 248 | if strings.HasPrefix(ol, prefix) { 249 | _, l, err := convertScopedLevel(ol) 250 | return l, err 251 | } 252 | } 253 | 254 | return NoneLevel, fmt.Errorf("no level defined for scope '%s'", scope) 255 | } 256 | 257 | // SetLogCallers sets whether to output the caller's source code location for a given scope. 258 | func (o *Options) SetLogCallers(scope string, include bool) { 259 | scopes := strings.Split(o.logCallers, ",") 260 | 261 | // remove any occurrence of the scope 262 | for i, s := range scopes { 263 | if s == scope { 264 | scopes[i] = "" 265 | } 266 | } 267 | 268 | if include { 269 | // find a free slot if there is one 270 | for i, s := range scopes { 271 | if s == "" { 272 | scopes[i] = scope 273 | o.logCallers = strings.Join(scopes, ",") 274 | return 275 | } 276 | } 277 | 278 | scopes = append(scopes, scope) 279 | } 280 | 281 | o.logCallers = strings.Join(scopes, ",") 282 | } 283 | 284 | // GetLogCallers returns whether the caller's source code location is output for a given scope. 285 | func (o *Options) GetLogCallers(scope string) bool { 286 | scopes := strings.Split(o.logCallers, ",") 287 | 288 | for _, s := range scopes { 289 | if s == scope { 290 | return true 291 | } 292 | } 293 | 294 | return false 295 | } 296 | 297 | func convertScopedLevel(sl string) (string, Level, error) { 298 | var s string 299 | var l string 300 | 301 | pieces := strings.Split(sl, ":") 302 | if len(pieces) == 1 { 303 | s = DefaultScopeName 304 | l = pieces[0] 305 | } else if len(pieces) == 2 { 306 | s = pieces[0] 307 | l = pieces[1] 308 | } else { 309 | return "", NoneLevel, fmt.Errorf("invalid output level format '%s'", sl) 310 | } 311 | 312 | level, ok := stringToLevel[l] 313 | if !ok { 314 | return "", NoneLevel, fmt.Errorf("invalid output level '%s'", sl) 315 | } 316 | 317 | return s, level, nil 318 | } 319 | 320 | // AttachFlags attaches a set of Cobra flags to the given Cobra command. 321 | // 322 | // Cobra is the command-line processor that Istio uses. This command attaches 323 | // the necessary set of flags to expose a CLI to let the user control all 324 | // logging options. 325 | func (o *Options) AttachFlags(cmd *cobra.Command) { 326 | _ = o.AttachToFlagSet(cmd.PersistentFlags()) 327 | } 328 | 329 | // AttachToFlagSet attaches a set of pflags to the provided FlagSet and returns the FlagSet. 330 | // 331 | // FlagSet should be provided explicitly, failure to do so will result in a panic. 332 | func (o *Options) AttachToFlagSet(fs *pflag.FlagSet) *pflag.FlagSet { 333 | fs.StringArrayVar(&o.OutputPaths, "log-target", o.OutputPaths, 334 | "The set of paths where to output the log. This can be any path as well as the special values stdout and stderr") 335 | 336 | fs.StringVar(&o.RotateOutputPath, "log-rotate", o.RotateOutputPath, 337 | "The path for the optional rotating log file") 338 | 339 | fs.IntVar(&o.RotationMaxAge, "log-rotate-max-age", o.RotationMaxAge, 340 | "The maximum age in days of a log file beyond which the file is rotated (0 indicates no limit)") 341 | 342 | fs.IntVar(&o.RotationMaxSize, "log-rotate-max-size", o.RotationMaxSize, 343 | "The maximum size in megabytes of a log file beyond which the file is rotated") 344 | 345 | fs.IntVar(&o.RotationMaxBackups, "log-rotate-max-backups", o.RotationMaxBackups, 346 | "The maximum number of log file backups to keep before older files are deleted (0 indicates no limit)") 347 | 348 | fs.BoolVar(&o.JSONEncoding, "log-as-json", o.JSONEncoding, 349 | "Whether to format output as JSON or in plain console-friendly format") 350 | 351 | allScopes := Scopes() 352 | if len(allScopes) > 1 { 353 | keys := make([]string, 0, len(allScopes)) 354 | for name := range allScopes { 355 | keys = append(keys, name) 356 | } 357 | keys = append(keys, OverrideScopeName) 358 | sort.Strings(keys) 359 | s := strings.Join(keys, ", ") 360 | 361 | fs.StringVar(&o.outputLevels, "log-output-level", o.outputLevels, 362 | fmt.Sprintf("Comma-separated minimum per-scope logging level of messages to output, in the form of "+ 363 | ":,:,... where scope can be one of [%s] and level can be one of [%s, %s, %s, %s, %s]", 364 | s, 365 | levelToString[DebugLevel], 366 | levelToString[InfoLevel], 367 | levelToString[WarnLevel], 368 | levelToString[ErrorLevel], 369 | levelToString[NoneLevel])) 370 | 371 | fs.StringVar(&o.stackTraceLevels, "log-stacktrace-level", o.stackTraceLevels, 372 | fmt.Sprintf("Comma-separated minimum per-scope logging level at which stack traces are captured, in the form of "+ 373 | ":,,... where scope can be one of [%s] and level can be one of [%s, %s, %s, %s, %s]", 374 | s, 375 | levelToString[DebugLevel], 376 | levelToString[InfoLevel], 377 | levelToString[WarnLevel], 378 | levelToString[ErrorLevel], 379 | levelToString[NoneLevel])) 380 | 381 | fs.StringVar(&o.logCallers, "log-caller", o.logCallers, 382 | fmt.Sprintf("Comma-separated list of scopes for which to include caller information, scopes can be any of [%s]", s)) 383 | } else { 384 | fs.StringVar(&o.outputLevels, "log-output-level", o.outputLevels, 385 | fmt.Sprintf("The minimum logging level of messages to output, can be one of [%s, %s, %s, %s, %s]", 386 | levelToString[DebugLevel], 387 | levelToString[InfoLevel], 388 | levelToString[WarnLevel], 389 | levelToString[ErrorLevel], 390 | levelToString[NoneLevel])) 391 | 392 | fs.StringVar(&o.stackTraceLevels, "log-stacktrace-level", o.stackTraceLevels, 393 | fmt.Sprintf("The minimum logging level at which stack traces are captured, can be one of [%s, %s, %s, %s, %s]", 394 | levelToString[DebugLevel], 395 | levelToString[InfoLevel], 396 | levelToString[WarnLevel], 397 | levelToString[ErrorLevel], 398 | levelToString[NoneLevel])) 399 | 400 | fs.StringVar(&o.logCallers, "log-caller", o.logCallers, 401 | "Comma-separated list of scopes for which to include called information, scopes can be any of [default]") 402 | } 403 | 404 | return fs 405 | } 406 | -------------------------------------------------------------------------------- /options_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Tetrate, Inc 2018 All Rights Reserved. 2 | 3 | // Copyright 2017 Istio Authors 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package log 18 | 19 | import ( 20 | "reflect" 21 | "strconv" 22 | "strings" 23 | "testing" 24 | 25 | "github.com/spf13/cobra" 26 | ) 27 | 28 | func TestOpts(t *testing.T) { 29 | resetGlobals() 30 | 31 | cases := []struct { 32 | cmdLine string 33 | result Options 34 | }{ 35 | {"--log-as-json", Options{ 36 | OutputPaths: []string{defaultOutputPath}, 37 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 38 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 39 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 40 | JSONEncoding: true, 41 | RotationMaxAge: defaultRotationMaxAge, 42 | RotationMaxSize: defaultRotationMaxSize, 43 | RotationMaxBackups: defaultRotationMaxBackups, 44 | LogGrpc: true, 45 | }}, 46 | 47 | {"--log-target stdout --log-target stderr", Options{ 48 | OutputPaths: []string{"stdout", "stderr"}, 49 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 50 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 51 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 52 | RotationMaxAge: defaultRotationMaxAge, 53 | RotationMaxSize: defaultRotationMaxSize, 54 | RotationMaxBackups: defaultRotationMaxBackups, 55 | LogGrpc: true, 56 | }}, 57 | 58 | {"--log-caller default", Options{ 59 | OutputPaths: []string{defaultOutputPath}, 60 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 61 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 62 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 63 | logCallers: DefaultScopeName, 64 | RotationMaxAge: defaultRotationMaxAge, 65 | RotationMaxSize: defaultRotationMaxSize, 66 | RotationMaxBackups: defaultRotationMaxBackups, 67 | LogGrpc: true, 68 | }}, 69 | 70 | {"--log-stacktrace-level debug", Options{ 71 | OutputPaths: []string{defaultOutputPath}, 72 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 73 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 74 | stackTraceLevels: levelToString[DebugLevel], 75 | RotationMaxAge: defaultRotationMaxAge, 76 | RotationMaxSize: defaultRotationMaxSize, 77 | RotationMaxBackups: defaultRotationMaxBackups, 78 | LogGrpc: true, 79 | }}, 80 | 81 | {"--log-stacktrace-level default:debug", Options{ 82 | OutputPaths: []string{defaultOutputPath}, 83 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 84 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 85 | stackTraceLevels: DefaultScopeName + ":" + levelToString[DebugLevel], 86 | RotationMaxAge: defaultRotationMaxAge, 87 | RotationMaxSize: defaultRotationMaxSize, 88 | RotationMaxBackups: defaultRotationMaxBackups, 89 | LogGrpc: true, 90 | }}, 91 | 92 | {"--log-stacktrace-level info", Options{ 93 | OutputPaths: []string{defaultOutputPath}, 94 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 95 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 96 | stackTraceLevels: levelToString[InfoLevel], 97 | RotationMaxAge: defaultRotationMaxAge, 98 | RotationMaxSize: defaultRotationMaxSize, 99 | RotationMaxBackups: defaultRotationMaxBackups, 100 | LogGrpc: true, 101 | }}, 102 | 103 | {"--log-stacktrace-level default:info", Options{ 104 | OutputPaths: []string{defaultOutputPath}, 105 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 106 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 107 | stackTraceLevels: DefaultScopeName + ":" + levelToString[InfoLevel], 108 | RotationMaxAge: defaultRotationMaxAge, 109 | RotationMaxSize: defaultRotationMaxSize, 110 | RotationMaxBackups: defaultRotationMaxBackups, 111 | LogGrpc: true, 112 | }}, 113 | 114 | {"--log-stacktrace-level warn", Options{ 115 | OutputPaths: []string{defaultOutputPath}, 116 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 117 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 118 | stackTraceLevels: levelToString[WarnLevel], 119 | RotationMaxAge: defaultRotationMaxAge, 120 | RotationMaxSize: defaultRotationMaxSize, 121 | RotationMaxBackups: defaultRotationMaxBackups, 122 | LogGrpc: true, 123 | }}, 124 | 125 | {"--log-stacktrace-level default:warn", Options{ 126 | OutputPaths: []string{defaultOutputPath}, 127 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 128 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 129 | stackTraceLevels: DefaultScopeName + ":" + levelToString[WarnLevel], 130 | RotationMaxAge: defaultRotationMaxAge, 131 | RotationMaxSize: defaultRotationMaxSize, 132 | RotationMaxBackups: defaultRotationMaxBackups, 133 | LogGrpc: true, 134 | }}, 135 | 136 | {"--log-stacktrace-level error", Options{ 137 | OutputPaths: []string{defaultOutputPath}, 138 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 139 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 140 | stackTraceLevels: levelToString[ErrorLevel], 141 | RotationMaxAge: defaultRotationMaxAge, 142 | RotationMaxSize: defaultRotationMaxSize, 143 | RotationMaxBackups: defaultRotationMaxBackups, 144 | LogGrpc: true, 145 | }}, 146 | 147 | {"--log-stacktrace-level default:error", Options{ 148 | OutputPaths: []string{defaultOutputPath}, 149 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 150 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 151 | stackTraceLevels: DefaultScopeName + ":" + levelToString[ErrorLevel], 152 | RotationMaxAge: defaultRotationMaxAge, 153 | RotationMaxSize: defaultRotationMaxSize, 154 | RotationMaxBackups: defaultRotationMaxBackups, 155 | LogGrpc: true, 156 | }}, 157 | 158 | {"--log-stacktrace-level none", Options{ 159 | OutputPaths: []string{defaultOutputPath}, 160 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 161 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 162 | stackTraceLevels: levelToString[NoneLevel], 163 | RotationMaxAge: defaultRotationMaxAge, 164 | RotationMaxSize: defaultRotationMaxSize, 165 | RotationMaxBackups: defaultRotationMaxBackups, 166 | LogGrpc: true, 167 | }}, 168 | 169 | {"--log-stacktrace-level default:none", Options{ 170 | OutputPaths: []string{defaultOutputPath}, 171 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 172 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 173 | stackTraceLevels: DefaultScopeName + ":" + levelToString[NoneLevel], 174 | RotationMaxAge: defaultRotationMaxAge, 175 | RotationMaxSize: defaultRotationMaxSize, 176 | RotationMaxBackups: defaultRotationMaxBackups, 177 | LogGrpc: true, 178 | }}, 179 | 180 | {"--log-output-level debug", Options{ 181 | OutputPaths: []string{defaultOutputPath}, 182 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 183 | outputLevels: levelToString[DebugLevel], 184 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 185 | RotationMaxAge: defaultRotationMaxAge, 186 | RotationMaxSize: defaultRotationMaxSize, 187 | RotationMaxBackups: defaultRotationMaxBackups, 188 | LogGrpc: true, 189 | }}, 190 | 191 | {"--log-output-level info", Options{ 192 | OutputPaths: []string{defaultOutputPath}, 193 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 194 | outputLevels: levelToString[InfoLevel], 195 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 196 | RotationMaxAge: defaultRotationMaxAge, 197 | RotationMaxSize: defaultRotationMaxSize, 198 | RotationMaxBackups: defaultRotationMaxBackups, 199 | LogGrpc: true, 200 | }}, 201 | 202 | {"--log-output-level warn", Options{ 203 | OutputPaths: []string{defaultOutputPath}, 204 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 205 | outputLevels: levelToString[WarnLevel], 206 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 207 | RotationMaxAge: defaultRotationMaxAge, 208 | RotationMaxSize: defaultRotationMaxSize, 209 | RotationMaxBackups: defaultRotationMaxBackups, 210 | LogGrpc: true, 211 | }}, 212 | 213 | {"--log-output-level error", Options{ 214 | OutputPaths: []string{defaultOutputPath}, 215 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 216 | outputLevels: levelToString[ErrorLevel], 217 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 218 | RotationMaxAge: defaultRotationMaxAge, 219 | RotationMaxSize: defaultRotationMaxSize, 220 | RotationMaxBackups: defaultRotationMaxBackups, 221 | LogGrpc: true, 222 | }}, 223 | 224 | {"--log-output-level none", Options{ 225 | OutputPaths: []string{defaultOutputPath}, 226 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 227 | outputLevels: levelToString[NoneLevel], 228 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 229 | RotationMaxAge: defaultRotationMaxAge, 230 | RotationMaxSize: defaultRotationMaxSize, 231 | RotationMaxBackups: defaultRotationMaxBackups, 232 | LogGrpc: true, 233 | }}, 234 | 235 | {"--log-rotate foobar", Options{ 236 | OutputPaths: []string{defaultOutputPath}, 237 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 238 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 239 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 240 | RotateOutputPath: "foobar", 241 | RotationMaxAge: defaultRotationMaxAge, 242 | RotationMaxSize: defaultRotationMaxSize, 243 | RotationMaxBackups: defaultRotationMaxBackups, 244 | LogGrpc: true, 245 | }}, 246 | 247 | {"--log-rotate-max-age 1234", Options{ 248 | OutputPaths: []string{defaultOutputPath}, 249 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 250 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 251 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 252 | RotationMaxAge: 1234, 253 | RotationMaxSize: defaultRotationMaxSize, 254 | RotationMaxBackups: defaultRotationMaxBackups, 255 | LogGrpc: true, 256 | }}, 257 | 258 | {"--log-rotate-max-size 1234", Options{ 259 | OutputPaths: []string{defaultOutputPath}, 260 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 261 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 262 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 263 | RotationMaxAge: defaultRotationMaxAge, 264 | RotationMaxSize: 1234, 265 | RotationMaxBackups: defaultRotationMaxBackups, 266 | LogGrpc: true, 267 | }}, 268 | 269 | {"--log-rotate-max-backups 1234", Options{ 270 | OutputPaths: []string{defaultOutputPath}, 271 | ErrorOutputPaths: []string{defaultErrorOutputPath}, 272 | outputLevels: DefaultScopeName + ":" + levelToString[defaultOutputLevel], 273 | stackTraceLevels: DefaultScopeName + ":" + levelToString[defaultStackTraceLevel], 274 | RotationMaxAge: defaultRotationMaxAge, 275 | RotationMaxSize: defaultRotationMaxSize, 276 | RotationMaxBackups: 1234, 277 | LogGrpc: true, 278 | }}, 279 | } 280 | 281 | for j := 0; j < 2; j++ { 282 | for i, c := range cases { 283 | t.Run(strconv.Itoa(j*100+i), func(t *testing.T) { 284 | o := DefaultOptions() 285 | cmd := &cobra.Command{} 286 | o.AttachFlags(cmd) 287 | cmd.SetArgs(strings.Split(c.cmdLine, " ")) 288 | 289 | if err := cmd.Execute(); err != nil { 290 | t.Errorf("Got %v, expecting success", err) 291 | } 292 | 293 | if !reflect.DeepEqual(c.result, *o) { 294 | t.Errorf("Got %v, expected %v", *o, c.result) 295 | } 296 | }) 297 | } 298 | 299 | _ = RegisterScope("foo", "bar", 0) 300 | } 301 | } 302 | 303 | func TestSetLevel(t *testing.T) { 304 | resetGlobals() 305 | _ = RegisterScope("TestSetLevel", "", 0) 306 | 307 | cases := []struct { 308 | levels string 309 | scope string 310 | targetLevel Level 311 | }{ 312 | {"debug", "default", DebugLevel}, 313 | {"default:debug", "default", DebugLevel}, 314 | {"info", "default", DebugLevel}, 315 | {"default:info", "default", DebugLevel}, 316 | {"warn", "default", DebugLevel}, 317 | {"default:warn", "default", DebugLevel}, 318 | {"error", "default", DebugLevel}, 319 | {"default:error", "default", DebugLevel}, 320 | {"none", "default", DebugLevel}, 321 | {"default:none", "default", DebugLevel}, 322 | 323 | {"debug", "default", ErrorLevel}, 324 | {"default:debug", "default", ErrorLevel}, 325 | {"info", "default", ErrorLevel}, 326 | {"default:info", "default", ErrorLevel}, 327 | {"warn", "default", ErrorLevel}, 328 | {"default:warn", "default", ErrorLevel}, 329 | {"error", "default", ErrorLevel}, 330 | {"default:error", "default", ErrorLevel}, 331 | {"none", "default", ErrorLevel}, 332 | {"default:none", "default", ErrorLevel}, 333 | 334 | {"default:none", "pizza", ErrorLevel}, 335 | {"default:none,TestSetLevel:debug", "pizza", ErrorLevel}, 336 | } 337 | 338 | for i, c := range cases { 339 | t.Run(strconv.Itoa(i), func(t *testing.T) { 340 | o := DefaultOptions() 341 | o.outputLevels = c.levels 342 | o.stackTraceLevels = c.levels 343 | 344 | o.SetOutputLevel(c.scope, c.targetLevel) 345 | o.SetStackTraceLevel(c.scope, c.targetLevel) 346 | 347 | if newLevel, err := o.GetOutputLevel(c.scope); err != nil { 348 | t.Errorf("Got error %v, expecting success", err) 349 | } else if newLevel != c.targetLevel { 350 | t.Errorf("Got level %v, expecting %v", newLevel, c.targetLevel) 351 | } 352 | 353 | if newLevel, err := o.GetStackTraceLevel(c.scope); err != nil { 354 | t.Errorf("Got error %v, expecting success", err) 355 | } else if newLevel != c.targetLevel { 356 | t.Errorf("Got level %v, expecting %v", newLevel, c.targetLevel) 357 | } 358 | }) 359 | } 360 | } 361 | 362 | func TestGetLevel(t *testing.T) { 363 | resetGlobals() 364 | 365 | cases := []struct { 366 | levels string 367 | scope string 368 | expectedLevel Level 369 | expectedFail bool 370 | }{ 371 | {"debug", "default", DebugLevel, false}, 372 | {"default:debug", "default", DebugLevel, false}, 373 | {"info", "default", InfoLevel, false}, 374 | {"default:info", "default", InfoLevel, false}, 375 | {"warn", "default", WarnLevel, false}, 376 | {"default:warn", "default", WarnLevel, false}, 377 | {"error", "default", ErrorLevel, false}, 378 | {"default:error", "default", ErrorLevel, false}, 379 | 380 | {"badLevel", "default", NoneLevel, true}, 381 | {"default:badLevel", "default", NoneLevel, true}, 382 | 383 | {"error", "badScope", NoneLevel, true}, 384 | {"default:error", "badScope", NoneLevel, true}, 385 | 386 | {"default:err:or", "default", NoneLevel, true}, 387 | } 388 | 389 | for i, c := range cases { 390 | t.Run(strconv.Itoa(i), func(t *testing.T) { 391 | o := DefaultOptions() 392 | o.outputLevels = c.levels 393 | o.stackTraceLevels = c.levels 394 | 395 | l, err := o.GetOutputLevel(c.scope) 396 | if c.expectedFail { 397 | if err == nil { 398 | t.Errorf("Got success, expecting error") 399 | } 400 | } else { 401 | if err != nil { 402 | t.Errorf("Got error %s, expecting success", err) 403 | } 404 | 405 | if l != c.expectedLevel { 406 | t.Errorf("Got level %v, expecting %v", l, c.expectedLevel) 407 | } 408 | } 409 | 410 | l, err = o.GetStackTraceLevel(c.scope) 411 | if c.expectedFail { 412 | if err == nil { 413 | t.Errorf("Got success, expecting error") 414 | } 415 | } else { 416 | if err != nil { 417 | t.Errorf("Got error %s, expecting success", err) 418 | } 419 | 420 | if l != c.expectedLevel { 421 | t.Errorf("Got level %v, expecting %v", l, c.expectedLevel) 422 | } 423 | } 424 | }) 425 | } 426 | } 427 | 428 | func TestLogCallers(t *testing.T) { 429 | resetGlobals() 430 | o := DefaultOptions() 431 | 432 | o.SetLogCallers("s1", true) 433 | if !o.GetLogCallers("s1") { 434 | t.Error("Expecting true") 435 | } 436 | 437 | o.SetLogCallers("s1", false) 438 | if o.GetLogCallers("s1") { 439 | t.Error("Expecting false") 440 | } 441 | 442 | o.SetLogCallers("s1", true) 443 | o.SetLogCallers("s2", true) 444 | if !o.GetLogCallers("s1") { 445 | t.Error("Expecting true") 446 | } 447 | 448 | if !o.GetLogCallers("s2") { 449 | t.Error("Expecting true") 450 | } 451 | 452 | if o.GetLogCallers("s3") { 453 | t.Error("Expecting false") 454 | } 455 | } 456 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 9 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 10 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 11 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 12 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 13 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 14 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 15 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 16 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 17 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 18 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 19 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 20 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 21 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 22 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 23 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 24 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 25 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 26 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 27 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 28 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 29 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 30 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 31 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 32 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 33 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 34 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 35 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 36 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 37 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 38 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 39 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 40 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 41 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 42 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 43 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 44 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 45 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 46 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 47 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 48 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 49 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 50 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 51 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 52 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 53 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 54 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 55 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 56 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 57 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 58 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 59 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 60 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 61 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 62 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 63 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 64 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 65 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 66 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 67 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 68 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 69 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 70 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 71 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 72 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 73 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 74 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 75 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 76 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 77 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 78 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 79 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 80 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 81 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 82 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 83 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 84 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 85 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 86 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 87 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 88 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 89 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 90 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 91 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 92 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 93 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 94 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 95 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 96 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 97 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 98 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 99 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 100 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 101 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 102 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 103 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 104 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 105 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 106 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 107 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 108 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 109 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 110 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 111 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 112 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 113 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 114 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 115 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 116 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 117 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 118 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 119 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 120 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 121 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 122 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 123 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 124 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 125 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 126 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 127 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 128 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 129 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 130 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 131 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 132 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 133 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 134 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 135 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 136 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 137 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 138 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 139 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 140 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 141 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 142 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 143 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 144 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 145 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 146 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 147 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 148 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 149 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 150 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 151 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 152 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 153 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 154 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 155 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 156 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 157 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 158 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 159 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 160 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 161 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 162 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 163 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 164 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 165 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 166 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 167 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 168 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 169 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 170 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 171 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 172 | github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= 173 | github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= 174 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 175 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 176 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 177 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 178 | github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 179 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 180 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 181 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 182 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 183 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 184 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 185 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 186 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 187 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 188 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 189 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 190 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 191 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 192 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 193 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 194 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 195 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 196 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 197 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 198 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 199 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 200 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 201 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 202 | go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= 203 | go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 204 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 205 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 206 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 207 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 208 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 209 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 210 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 211 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 212 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 213 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 214 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 215 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 216 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 217 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 218 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 219 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 220 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 221 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 222 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= 223 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 224 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 225 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 226 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 227 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 228 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 229 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 230 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 231 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 232 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 233 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 234 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 235 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 236 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 237 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 238 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 239 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 240 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 241 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 242 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 243 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 244 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 245 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 246 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 247 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 248 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 249 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 250 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 251 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 252 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 253 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 254 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 255 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 256 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 257 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 258 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 259 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 260 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 261 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 262 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 263 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 264 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 265 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 266 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 267 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 268 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 269 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 270 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 271 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 272 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 273 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 274 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 275 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 276 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 277 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 278 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 279 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 280 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 281 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 282 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 283 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 284 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 285 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 286 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 287 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8= 288 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 289 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 290 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 291 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 292 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 293 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 294 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 295 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 296 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 297 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 298 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 299 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 300 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 301 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 302 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 303 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 304 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 305 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 306 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 307 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 308 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 309 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 310 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 311 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 312 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 313 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 314 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 315 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 316 | google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As= 317 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 318 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 319 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 320 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 321 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 322 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 323 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 324 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 325 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 326 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 327 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 328 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 329 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 330 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 331 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 332 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 333 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 334 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 335 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 336 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 337 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 338 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 339 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 340 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 341 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 342 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 343 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 344 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 345 | honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= 346 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 347 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 348 | --------------------------------------------------------------------------------