| OLD | NEW |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Package logging implements Logger, a gRPC glog.Logger implementation backed | 5 // Package logging implements a gRPC glog.Logger implementation backed |
| 6 // by a github.com/luci/luci-go/common/logging Logger. | 6 // by a github.com/luci/luci-go/common/logging Logger. |
| 7 // |
| 8 // The logger can be installed by calling Install. |
| 7 package logging | 9 package logging |
| 8 | 10 |
| 9 import ( | 11 import ( |
| 10 "os" | 12 "os" |
| 11 "runtime" | 13 "runtime" |
| 12 "strings" | 14 "strings" |
| 13 | 15 |
| 14 » log "github.com/luci/luci-go/common/logging" | 16 » "github.com/luci/luci-go/common/logging" |
| 15 | 17 |
| 16 "google.golang.org/grpc/grpclog" | 18 "google.golang.org/grpc/grpclog" |
| 17 ) | 19 ) |
| 18 | 20 |
| 21 // Suppress is a sentinel logging level that instructs the logger to suppress |
| 22 // all non-fatal logging output. This is NOT a valid logging.Level, and should |
| 23 // not be used as such. |
| 24 var Suppress = logging.Level(logging.Error + 1) |
| 25 |
| 19 type grpcLogger struct { | 26 type grpcLogger struct { |
| 20 » // Base is the base logger instance. | 27 » base logging.Logger |
| 21 » base log.Logger | 28 » vLevel int |
| 22 | |
| 23 » // logPrints is true if Print statements should be logged. | |
| 24 » logPrints bool | |
| 25 } | 29 } |
| 26 | 30 |
| 27 // Install installs a logger as the gRPC library's logger. The installation is | 31 // Install installs a logger as the gRPC library's logger. The installation is |
| 28 // not protected by a mutex, so this must be set somewhere that atomic access is | 32 // not protected by a mutex, so this must be set somewhere that atomic access is |
| 29 // guaranteed. | 33 // guaranteed. |
| 30 func Install(base log.Logger, logPrints bool) { | 34 // |
| 31 » grpclog.SetLogger(&grpcLogger{ | 35 // A special logging level, "Suppress", can be provided to suppress all |
| 32 » » base: base, | 36 // non-fatal logging output . |
| 33 » » logPrints: logPrints, | 37 // |
| 38 // gRPC V=level and error terminology translation is as follows: |
| 39 // - V=0, ERROR (low verbosity) is logged at logging.ERROR level. |
| 40 // - V=1, WARNING (medium verbosity) is logged at logging.WARNING level. |
| 41 // - V=2, INFO (high verbosity) is logged at logging.DEBUG level. |
| 42 func Install(base logging.Logger, level logging.Level) { |
| 43 » grpclog.SetLoggerV2(&grpcLogger{ |
| 44 » » base: base, |
| 45 » » vLevel: translateLevel(level), |
| 34 }) | 46 }) |
| 35 } | 47 } |
| 36 | 48 |
| 37 func makeArgFormatString(args []interface{}) string { | 49 func (gl *grpcLogger) Info(args ...interface{}) { |
| 38 » if len(args) == 0 { | 50 » if gl.V(2) { |
| 39 » » return "" | 51 » » gl.base.LogCall(logging.Debug, 2, makeArgFormatString(args), arg
s) |
| 40 } | 52 } |
| 41 » return strings.TrimSuffix(strings.Repeat("%v ", len(args)), " ") | 53 } |
| 54 |
| 55 func (gl *grpcLogger) Infof(format string, args ...interface{}) { |
| 56 » if gl.V(2) { |
| 57 » » gl.base.LogCall(logging.Debug, 2, format, args) |
| 58 » } |
| 59 } |
| 60 |
| 61 func (gl *grpcLogger) Infoln(args ...interface{}) { |
| 62 » if gl.V(2) { |
| 63 » » gl.base.LogCall(logging.Debug, 2, makeArgFormatString(args), arg
s) |
| 64 » } |
| 65 } |
| 66 |
| 67 func (gl *grpcLogger) Warning(args ...interface{}) { |
| 68 » if gl.V(1) { |
| 69 » » gl.base.LogCall(logging.Warning, 2, makeArgFormatString(args), a
rgs) |
| 70 » } |
| 71 } |
| 72 |
| 73 func (gl *grpcLogger) Warningf(format string, args ...interface{}) { |
| 74 » if gl.V(1) { |
| 75 » » gl.base.LogCall(logging.Warning, 2, format, args) |
| 76 » } |
| 77 } |
| 78 |
| 79 func (gl *grpcLogger) Warningln(args ...interface{}) { |
| 80 » if gl.V(1) { |
| 81 » » gl.base.LogCall(logging.Warning, 2, makeArgFormatString(args), a
rgs) |
| 82 » } |
| 83 } |
| 84 |
| 85 func (gl *grpcLogger) Error(args ...interface{}) { |
| 86 » if gl.V(0) { |
| 87 » » gl.base.LogCall(logging.Error, 2, makeArgFormatString(args), arg
s) |
| 88 » } |
| 89 } |
| 90 |
| 91 func (gl *grpcLogger) Errorf(format string, args ...interface{}) { |
| 92 » if gl.V(0) { |
| 93 » » gl.base.LogCall(logging.Error, 2, format, args) |
| 94 » } |
| 95 } |
| 96 |
| 97 func (gl *grpcLogger) Errorln(args ...interface{}) { |
| 98 » if gl.V(0) { |
| 99 » » gl.base.LogCall(logging.Error, 2, makeArgFormatString(args), arg
s) |
| 100 » } |
| 42 } | 101 } |
| 43 | 102 |
| 44 func (gl *grpcLogger) Fatal(args ...interface{}) { | 103 func (gl *grpcLogger) Fatal(args ...interface{}) { |
| 45 » gl.base.LogCall(log.Error, 2, makeArgFormatString(args), args) | 104 » gl.base.LogCall(logging.Error, 2, makeArgFormatString(args), args) |
| 46 gl.logStackTraceAndDie() | 105 gl.logStackTraceAndDie() |
| 47 } | 106 } |
| 48 | 107 |
| 49 func (gl *grpcLogger) Fatalf(format string, args ...interface{}) { | 108 func (gl *grpcLogger) Fatalf(format string, args ...interface{}) { |
| 50 » gl.base.LogCall(log.Error, 2, format, args) | 109 » gl.base.LogCall(logging.Error, 2, format, args) |
| 51 gl.logStackTraceAndDie() | 110 gl.logStackTraceAndDie() |
| 52 } | 111 } |
| 53 | 112 |
| 54 func (gl *grpcLogger) Fatalln(args ...interface{}) { | 113 func (gl *grpcLogger) Fatalln(args ...interface{}) { |
| 55 » gl.base.LogCall(log.Error, 2, makeArgFormatString(args), args) | 114 » gl.base.LogCall(logging.Error, 2, makeArgFormatString(args), args) |
| 56 gl.logStackTraceAndDie() | 115 gl.logStackTraceAndDie() |
| 57 } | 116 } |
| 58 | 117 |
| 59 func (gl *grpcLogger) Print(args ...interface{}) { | 118 func (gl *grpcLogger) V(l int) bool { |
| 60 » if !gl.logPrints { | 119 » return gl.vLevel >= l |
| 61 » » return | |
| 62 » } | |
| 63 » gl.base.LogCall(log.Info, 2, makeArgFormatString(args), args) | |
| 64 } | |
| 65 | |
| 66 func (gl *grpcLogger) Printf(format string, args ...interface{}) { | |
| 67 » if !gl.logPrints { | |
| 68 » » return | |
| 69 » } | |
| 70 » gl.base.LogCall(log.Info, 2, format, args) | |
| 71 } | |
| 72 | |
| 73 func (gl *grpcLogger) Println(args ...interface{}) { | |
| 74 » if !gl.logPrints { | |
| 75 » » return | |
| 76 » } | |
| 77 » gl.base.LogCall(log.Info, 2, makeArgFormatString(args), args) | |
| 78 } | 120 } |
| 79 | 121 |
| 80 func (gl *grpcLogger) logStackTraceAndDie() { | 122 func (gl *grpcLogger) logStackTraceAndDie() { |
| 81 » gl.base.LogCall(log.Error, 3, "Stack Trace:\n%s", []interface{}{stacks(t
rue)}) | 123 » gl.base.LogCall(logging.Error, 3, "Stack Trace:\n%s", []interface{}{stac
ks(true)}) |
| 82 » osExit(255) | 124 » fatalExit() |
| 83 } | 125 } |
| 84 | 126 |
| 85 // osExit is an operating system exit function used by "Fatal" logs. It is a | 127 // fatalExit is an operating system exit function used by "Fatal" logs. It is a |
| 86 // variable here so it can be stubbed for testing. | 128 // variable here so it can be stubbed for testing, but will exit with a non-zero |
| 87 var osExit = func(rc int) { | 129 // return code by default. |
| 88 » os.Exit(rc) | 130 var fatalExit = func() { |
| 131 » os.Exit(1) |
| 89 } | 132 } |
| 90 | 133 |
| 91 // stacks is a wrapper for runtime.Stack that attempts to recover the data for | 134 // stacks is a wrapper for runtime.Stack that attempts to recover the data for |
| 92 // all goroutines. | 135 // all goroutines. |
| 93 // | 136 // |
| 94 // This was copied from the glog library: | 137 // This was copied from the glog library: |
| 95 // https://github.com/golang/glog @ / 23def4e6c14b4da8ac2ed8007337bc5eb5007998 | 138 // https://github.com/golang/glog @ / 23def4e6c14b4da8ac2ed8007337bc5eb5007998 |
| 96 func stacks(all bool) []byte { | 139 func stacks(all bool) []byte { |
| 97 // We don't know how big the traces are, so grow a few times if they don
't fit. | 140 // We don't know how big the traces are, so grow a few times if they don
't fit. |
| 98 // Start large, though. | 141 // Start large, though. |
| 99 n := 10000 | 142 n := 10000 |
| 100 if all { | 143 if all { |
| 101 n = 100000 | 144 n = 100000 |
| 102 } | 145 } |
| 103 var trace []byte | 146 var trace []byte |
| 104 for i := 0; i < 5; i++ { | 147 for i := 0; i < 5; i++ { |
| 105 trace = make([]byte, n) | 148 trace = make([]byte, n) |
| 106 nbytes := runtime.Stack(trace, all) | 149 nbytes := runtime.Stack(trace, all) |
| 107 if nbytes < len(trace) { | 150 if nbytes < len(trace) { |
| 108 return trace[:nbytes] | 151 return trace[:nbytes] |
| 109 } | 152 } |
| 110 n *= 2 | 153 n *= 2 |
| 111 } | 154 } |
| 112 return trace | 155 return trace |
| 113 } | 156 } |
| 157 |
| 158 func makeArgFormatString(args []interface{}) string { |
| 159 if len(args) == 0 { |
| 160 return "" |
| 161 } |
| 162 return strings.TrimSuffix(strings.Repeat("%v ", len(args)), " ") |
| 163 } |
| 164 |
| 165 // translateLevel translates a "verbose level" to a logging level. |
| 166 func translateLevel(l logging.Level) int { |
| 167 switch l { |
| 168 case Suppress: |
| 169 return -1 |
| 170 case logging.Error: |
| 171 return 0 |
| 172 case logging.Warning, logging.Info: |
| 173 return 1 |
| 174 default: |
| 175 return 2 |
| 176 } |
| 177 } |
| OLD | NEW |