| // Copyright 2016 The LUCI Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Package logging implements a gRPC glog.Logger implementation backed |
| // by a go.chromium.org/luci/common/logging Logger. |
| // |
| // The logger can be installed by calling Install. |
| package logging |
| |
| import ( |
| "os" |
| "runtime" |
| "strings" |
| |
| "go.chromium.org/luci/common/logging" |
| |
| "google.golang.org/grpc/grpclog" |
| ) |
| |
| // Suppress is a sentinel logging level that instructs the logger to suppress |
| // all non-fatal logging output. This is NOT a valid logging.Level, and should |
| // not be used as such. |
| var Suppress = logging.Level(logging.Error + 1) |
| |
| type grpcLogger struct { |
| base logging.Logger |
| vLevel int |
| } |
| |
| // Install installs a logger as the gRPC library's logger. The installation is |
| // not protected by a mutex, so this must be set somewhere that atomic access is |
| // guaranteed. |
| // |
| // A special logging level, "Suppress", can be provided to suppress all |
| // non-fatal logging output . |
| // |
| // gRPC V=level and error terminology translation is as follows: |
| // - V=0, ERROR (low verbosity) is logged at logging.ERROR level. |
| // - V=1, WARNING (medium verbosity) is logged at logging.WARNING level. |
| // - V=2, INFO (high verbosity) is logged at logging.DEBUG level. |
| func Install(base logging.Logger, level logging.Level) { |
| grpclog.SetLoggerV2(&grpcLogger{ |
| base: base, |
| vLevel: translateLevel(level), |
| }) |
| } |
| |
| func (gl *grpcLogger) Info(args ...interface{}) { |
| if gl.V(2) { |
| gl.base.LogCall(logging.Debug, 2, makeArgFormatString(args), args) |
| } |
| } |
| |
| func (gl *grpcLogger) Infof(format string, args ...interface{}) { |
| if gl.V(2) { |
| gl.base.LogCall(logging.Debug, 2, format, args) |
| } |
| } |
| |
| func (gl *grpcLogger) Infoln(args ...interface{}) { |
| if gl.V(2) { |
| gl.base.LogCall(logging.Debug, 2, makeArgFormatString(args), args) |
| } |
| } |
| |
| func (gl *grpcLogger) Warning(args ...interface{}) { |
| if gl.V(1) { |
| gl.base.LogCall(logging.Warning, 2, makeArgFormatString(args), args) |
| } |
| } |
| |
| func (gl *grpcLogger) Warningf(format string, args ...interface{}) { |
| if gl.V(1) { |
| gl.base.LogCall(logging.Warning, 2, format, args) |
| } |
| } |
| |
| func (gl *grpcLogger) Warningln(args ...interface{}) { |
| if gl.V(1) { |
| gl.base.LogCall(logging.Warning, 2, makeArgFormatString(args), args) |
| } |
| } |
| |
| func (gl *grpcLogger) Error(args ...interface{}) { |
| if gl.V(0) { |
| gl.base.LogCall(logging.Error, 2, makeArgFormatString(args), args) |
| } |
| } |
| |
| func (gl *grpcLogger) Errorf(format string, args ...interface{}) { |
| if gl.V(0) { |
| gl.base.LogCall(logging.Error, 2, format, args) |
| } |
| } |
| |
| func (gl *grpcLogger) Errorln(args ...interface{}) { |
| if gl.V(0) { |
| gl.base.LogCall(logging.Error, 2, makeArgFormatString(args), args) |
| } |
| } |
| |
| func (gl *grpcLogger) Fatal(args ...interface{}) { |
| gl.base.LogCall(logging.Error, 2, makeArgFormatString(args), args) |
| gl.logStackTraceAndDie() |
| } |
| |
| func (gl *grpcLogger) Fatalf(format string, args ...interface{}) { |
| gl.base.LogCall(logging.Error, 2, format, args) |
| gl.logStackTraceAndDie() |
| } |
| |
| func (gl *grpcLogger) Fatalln(args ...interface{}) { |
| gl.base.LogCall(logging.Error, 2, makeArgFormatString(args), args) |
| gl.logStackTraceAndDie() |
| } |
| |
| func (gl *grpcLogger) V(l int) bool { |
| return gl.vLevel >= l |
| } |
| |
| func (gl *grpcLogger) logStackTraceAndDie() { |
| gl.base.LogCall(logging.Error, 3, "Stack Trace:\n%s", []interface{}{stacks(true)}) |
| fatalExit() |
| } |
| |
| // fatalExit is an operating system exit function used by "Fatal" logs. It is a |
| // variable here so it can be stubbed for testing, but will exit with a non-zero |
| // return code by default. |
| var fatalExit = func() { |
| os.Exit(1) |
| } |
| |
| // stacks is a wrapper for runtime.Stack that attempts to recover the data for |
| // all goroutines. |
| // |
| // This was copied from the glog library: |
| // https://github.com/golang/glog @ / 23def4e6c14b4da8ac2ed8007337bc5eb5007998 |
| func stacks(all bool) []byte { |
| // We don't know how big the traces are, so grow a few times if they don't fit. |
| // Start large, though. |
| n := 10000 |
| if all { |
| n = 100000 |
| } |
| var trace []byte |
| for i := 0; i < 5; i++ { |
| trace = make([]byte, n) |
| nbytes := runtime.Stack(trace, all) |
| if nbytes < len(trace) { |
| return trace[:nbytes] |
| } |
| n *= 2 |
| } |
| return trace |
| } |
| |
| func makeArgFormatString(args []interface{}) string { |
| if len(args) == 0 { |
| return "" |
| } |
| return strings.TrimSuffix(strings.Repeat("%v ", len(args)), " ") |
| } |
| |
| // translateLevel translates a "verbose level" to a logging level. |
| func translateLevel(l logging.Level) int { |
| switch l { |
| case Suppress: |
| return -1 |
| case logging.Error: |
| return 0 |
| case logging.Warning, logging.Info: |
| return 1 |
| default: |
| return 2 |
| } |
| } |