google.golang.org/grpc@v1.72.2/grpclog/internal/loggerv2.go (about)

     1  /*
     2   *
     3   * Copyright 2024 gRPC 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   */
    18  
    19  package internal
    20  
    21  import (
    22  	"encoding/json"
    23  	"fmt"
    24  	"io"
    25  	"log"
    26  	"os"
    27  )
    28  
    29  // LoggerV2 does underlying logging work for grpclog.
    30  type LoggerV2 interface {
    31  	// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
    32  	Info(args ...any)
    33  	// Infoln logs to INFO log. Arguments are handled in the manner of fmt.Println.
    34  	Infoln(args ...any)
    35  	// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
    36  	Infof(format string, args ...any)
    37  	// Warning logs to WARNING log. Arguments are handled in the manner of fmt.Print.
    38  	Warning(args ...any)
    39  	// Warningln logs to WARNING log. Arguments are handled in the manner of fmt.Println.
    40  	Warningln(args ...any)
    41  	// Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
    42  	Warningf(format string, args ...any)
    43  	// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
    44  	Error(args ...any)
    45  	// Errorln logs to ERROR log. Arguments are handled in the manner of fmt.Println.
    46  	Errorln(args ...any)
    47  	// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
    48  	Errorf(format string, args ...any)
    49  	// Fatal logs to ERROR log. Arguments are handled in the manner of fmt.Print.
    50  	// gRPC ensures that all Fatal logs will exit with os.Exit(1).
    51  	// Implementations may also call os.Exit() with a non-zero exit code.
    52  	Fatal(args ...any)
    53  	// Fatalln logs to ERROR log. Arguments are handled in the manner of fmt.Println.
    54  	// gRPC ensures that all Fatal logs will exit with os.Exit(1).
    55  	// Implementations may also call os.Exit() with a non-zero exit code.
    56  	Fatalln(args ...any)
    57  	// Fatalf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
    58  	// gRPC ensures that all Fatal logs will exit with os.Exit(1).
    59  	// Implementations may also call os.Exit() with a non-zero exit code.
    60  	Fatalf(format string, args ...any)
    61  	// V reports whether verbosity level l is at least the requested verbose level.
    62  	V(l int) bool
    63  }
    64  
    65  // DepthLoggerV2 logs at a specified call frame. If a LoggerV2 also implements
    66  // DepthLoggerV2, the below functions will be called with the appropriate stack
    67  // depth set for trivial functions the logger may ignore.
    68  //
    69  // # Experimental
    70  //
    71  // Notice: This type is EXPERIMENTAL and may be changed or removed in a
    72  // later release.
    73  type DepthLoggerV2 interface {
    74  	LoggerV2
    75  	// InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Println.
    76  	InfoDepth(depth int, args ...any)
    77  	// WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Println.
    78  	WarningDepth(depth int, args ...any)
    79  	// ErrorDepth logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Println.
    80  	ErrorDepth(depth int, args ...any)
    81  	// FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Println.
    82  	FatalDepth(depth int, args ...any)
    83  }
    84  
    85  const (
    86  	// infoLog indicates Info severity.
    87  	infoLog int = iota
    88  	// warningLog indicates Warning severity.
    89  	warningLog
    90  	// errorLog indicates Error severity.
    91  	errorLog
    92  	// fatalLog indicates Fatal severity.
    93  	fatalLog
    94  )
    95  
    96  // severityName contains the string representation of each severity.
    97  var severityName = []string{
    98  	infoLog:    "INFO",
    99  	warningLog: "WARNING",
   100  	errorLog:   "ERROR",
   101  	fatalLog:   "FATAL",
   102  }
   103  
   104  // sprintf is fmt.Sprintf.
   105  // These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
   106  var sprintf = fmt.Sprintf
   107  
   108  // sprint is fmt.Sprint.
   109  // These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
   110  var sprint = fmt.Sprint
   111  
   112  // sprintln is fmt.Sprintln.
   113  // These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
   114  var sprintln = fmt.Sprintln
   115  
   116  // exit is os.Exit.
   117  // This var exists to make it possible to test functions calling os.Exit.
   118  var exit = os.Exit
   119  
   120  // loggerT is the default logger used by grpclog.
   121  type loggerT struct {
   122  	m          []*log.Logger
   123  	v          int
   124  	jsonFormat bool
   125  }
   126  
   127  func (g *loggerT) output(severity int, s string) {
   128  	sevStr := severityName[severity]
   129  	if !g.jsonFormat {
   130  		g.m[severity].Output(2, sevStr+": "+s)
   131  		return
   132  	}
   133  	// TODO: we can also include the logging component, but that needs more
   134  	// (API) changes.
   135  	b, _ := json.Marshal(map[string]string{
   136  		"severity": sevStr,
   137  		"message":  s,
   138  	})
   139  	g.m[severity].Output(2, string(b))
   140  }
   141  
   142  func (g *loggerT) printf(severity int, format string, args ...any) {
   143  	// Note the discard check is duplicated in each print func, rather than in
   144  	// output, to avoid the expensive Sprint calls.
   145  	// De-duplicating this by moving to output would be a significant performance regression!
   146  	if lg := g.m[severity]; lg.Writer() == io.Discard {
   147  		return
   148  	}
   149  	g.output(severity, sprintf(format, args...))
   150  }
   151  
   152  func (g *loggerT) print(severity int, v ...any) {
   153  	if lg := g.m[severity]; lg.Writer() == io.Discard {
   154  		return
   155  	}
   156  	g.output(severity, sprint(v...))
   157  }
   158  
   159  func (g *loggerT) println(severity int, v ...any) {
   160  	if lg := g.m[severity]; lg.Writer() == io.Discard {
   161  		return
   162  	}
   163  	g.output(severity, sprintln(v...))
   164  }
   165  
   166  func (g *loggerT) Info(args ...any) {
   167  	g.print(infoLog, args...)
   168  }
   169  
   170  func (g *loggerT) Infoln(args ...any) {
   171  	g.println(infoLog, args...)
   172  }
   173  
   174  func (g *loggerT) Infof(format string, args ...any) {
   175  	g.printf(infoLog, format, args...)
   176  }
   177  
   178  func (g *loggerT) Warning(args ...any) {
   179  	g.print(warningLog, args...)
   180  }
   181  
   182  func (g *loggerT) Warningln(args ...any) {
   183  	g.println(warningLog, args...)
   184  }
   185  
   186  func (g *loggerT) Warningf(format string, args ...any) {
   187  	g.printf(warningLog, format, args...)
   188  }
   189  
   190  func (g *loggerT) Error(args ...any) {
   191  	g.print(errorLog, args...)
   192  }
   193  
   194  func (g *loggerT) Errorln(args ...any) {
   195  	g.println(errorLog, args...)
   196  }
   197  
   198  func (g *loggerT) Errorf(format string, args ...any) {
   199  	g.printf(errorLog, format, args...)
   200  }
   201  
   202  func (g *loggerT) Fatal(args ...any) {
   203  	g.print(fatalLog, args...)
   204  	exit(1)
   205  }
   206  
   207  func (g *loggerT) Fatalln(args ...any) {
   208  	g.println(fatalLog, args...)
   209  	exit(1)
   210  }
   211  
   212  func (g *loggerT) Fatalf(format string, args ...any) {
   213  	g.printf(fatalLog, format, args...)
   214  	exit(1)
   215  }
   216  
   217  func (g *loggerT) V(l int) bool {
   218  	return l <= g.v
   219  }
   220  
   221  // LoggerV2Config configures the LoggerV2 implementation.
   222  type LoggerV2Config struct {
   223  	// Verbosity sets the verbosity level of the logger.
   224  	Verbosity int
   225  	// FormatJSON controls whether the logger should output logs in JSON format.
   226  	FormatJSON bool
   227  }
   228  
   229  // combineLoggers returns a combined logger for both higher & lower severity logs,
   230  // or only one if the other is io.Discard.
   231  //
   232  // This uses io.Discard instead of io.MultiWriter when all loggers
   233  // are set to io.Discard. Both this package and the standard log package have
   234  // significant optimizations for io.Discard, which io.MultiWriter lacks (as of
   235  // this writing).
   236  func combineLoggers(lower, higher io.Writer) io.Writer {
   237  	if lower == io.Discard {
   238  		return higher
   239  	}
   240  	if higher == io.Discard {
   241  		return lower
   242  	}
   243  	return io.MultiWriter(lower, higher)
   244  }
   245  
   246  // NewLoggerV2 creates a new LoggerV2 instance with the provided configuration.
   247  // The infoW, warningW, and errorW writers are used to write log messages of
   248  // different severity levels.
   249  func NewLoggerV2(infoW, warningW, errorW io.Writer, c LoggerV2Config) LoggerV2 {
   250  	flag := log.LstdFlags
   251  	if c.FormatJSON {
   252  		flag = 0
   253  	}
   254  
   255  	warningW = combineLoggers(infoW, warningW)
   256  	errorW = combineLoggers(errorW, warningW)
   257  
   258  	fatalW := errorW
   259  
   260  	m := []*log.Logger{
   261  		log.New(infoW, "", flag),
   262  		log.New(warningW, "", flag),
   263  		log.New(errorW, "", flag),
   264  		log.New(fatalW, "", flag),
   265  	}
   266  	return &loggerT{m: m, v: c.Verbosity, jsonFormat: c.FormatJSON}
   267  }