snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
This commit is contained in:
26
grpclog/internal/grpclog.go
Normal file
26
grpclog/internal/grpclog.go
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2024 gRPC 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 internal contains functionality internal to the grpclog package.
|
||||
package internal
|
||||
|
||||
// LoggerV2Impl is the logger used for the non-depth log functions.
|
||||
var LoggerV2Impl LoggerV2
|
||||
|
||||
// DepthLoggerV2Impl is the logger used for the depth log functions.
|
||||
var DepthLoggerV2Impl DepthLoggerV2
|
||||
87
grpclog/internal/logger.go
Normal file
87
grpclog/internal/logger.go
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2024 gRPC 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 internal
|
||||
|
||||
// Logger mimics golang's standard Logger as an interface.
|
||||
//
|
||||
// Deprecated: use LoggerV2.
|
||||
type Logger interface {
|
||||
Fatal(args ...any)
|
||||
Fatalf(format string, args ...any)
|
||||
Fatalln(args ...any)
|
||||
Print(args ...any)
|
||||
Printf(format string, args ...any)
|
||||
Println(args ...any)
|
||||
}
|
||||
|
||||
// LoggerWrapper wraps Logger into a LoggerV2.
|
||||
type LoggerWrapper struct {
|
||||
Logger
|
||||
}
|
||||
|
||||
// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
|
||||
func (l *LoggerWrapper) Info(args ...any) {
|
||||
l.Logger.Print(args...)
|
||||
}
|
||||
|
||||
// Infoln logs to INFO log. Arguments are handled in the manner of fmt.Println.
|
||||
func (l *LoggerWrapper) Infoln(args ...any) {
|
||||
l.Logger.Println(args...)
|
||||
}
|
||||
|
||||
// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *LoggerWrapper) Infof(format string, args ...any) {
|
||||
l.Logger.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Warning logs to WARNING log. Arguments are handled in the manner of fmt.Print.
|
||||
func (l *LoggerWrapper) Warning(args ...any) {
|
||||
l.Logger.Print(args...)
|
||||
}
|
||||
|
||||
// Warningln logs to WARNING log. Arguments are handled in the manner of fmt.Println.
|
||||
func (l *LoggerWrapper) Warningln(args ...any) {
|
||||
l.Logger.Println(args...)
|
||||
}
|
||||
|
||||
// Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *LoggerWrapper) Warningf(format string, args ...any) {
|
||||
l.Logger.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
|
||||
func (l *LoggerWrapper) Error(args ...any) {
|
||||
l.Logger.Print(args...)
|
||||
}
|
||||
|
||||
// Errorln logs to ERROR log. Arguments are handled in the manner of fmt.Println.
|
||||
func (l *LoggerWrapper) Errorln(args ...any) {
|
||||
l.Logger.Println(args...)
|
||||
}
|
||||
|
||||
// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *LoggerWrapper) Errorf(format string, args ...any) {
|
||||
l.Logger.Printf(format, args...)
|
||||
}
|
||||
|
||||
// V reports whether verbosity level l is at least the requested verbose level.
|
||||
func (*LoggerWrapper) V(int) bool {
|
||||
// Returns true for all verbose level.
|
||||
return true
|
||||
}
|
||||
267
grpclog/internal/loggerv2.go
Normal file
267
grpclog/internal/loggerv2.go
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2024 gRPC 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 internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// LoggerV2 does underlying logging work for grpclog.
|
||||
type LoggerV2 interface {
|
||||
// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
|
||||
Info(args ...any)
|
||||
// Infoln logs to INFO log. Arguments are handled in the manner of fmt.Println.
|
||||
Infoln(args ...any)
|
||||
// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
|
||||
Infof(format string, args ...any)
|
||||
// Warning logs to WARNING log. Arguments are handled in the manner of fmt.Print.
|
||||
Warning(args ...any)
|
||||
// Warningln logs to WARNING log. Arguments are handled in the manner of fmt.Println.
|
||||
Warningln(args ...any)
|
||||
// Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
|
||||
Warningf(format string, args ...any)
|
||||
// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
|
||||
Error(args ...any)
|
||||
// Errorln logs to ERROR log. Arguments are handled in the manner of fmt.Println.
|
||||
Errorln(args ...any)
|
||||
// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
|
||||
Errorf(format string, args ...any)
|
||||
// Fatal logs to ERROR log. Arguments are handled in the manner of fmt.Print.
|
||||
// gRPC ensures that all Fatal logs will exit with os.Exit(1).
|
||||
// Implementations may also call os.Exit() with a non-zero exit code.
|
||||
Fatal(args ...any)
|
||||
// Fatalln logs to ERROR log. Arguments are handled in the manner of fmt.Println.
|
||||
// gRPC ensures that all Fatal logs will exit with os.Exit(1).
|
||||
// Implementations may also call os.Exit() with a non-zero exit code.
|
||||
Fatalln(args ...any)
|
||||
// Fatalf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
|
||||
// gRPC ensures that all Fatal logs will exit with os.Exit(1).
|
||||
// Implementations may also call os.Exit() with a non-zero exit code.
|
||||
Fatalf(format string, args ...any)
|
||||
// V reports whether verbosity level l is at least the requested verbose level.
|
||||
V(l int) bool
|
||||
}
|
||||
|
||||
// DepthLoggerV2 logs at a specified call frame. If a LoggerV2 also implements
|
||||
// DepthLoggerV2, the below functions will be called with the appropriate stack
|
||||
// depth set for trivial functions the logger may ignore.
|
||||
//
|
||||
// # Experimental
|
||||
//
|
||||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
type DepthLoggerV2 interface {
|
||||
LoggerV2
|
||||
// InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Println.
|
||||
InfoDepth(depth int, args ...any)
|
||||
// WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Println.
|
||||
WarningDepth(depth int, args ...any)
|
||||
// ErrorDepth logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Println.
|
||||
ErrorDepth(depth int, args ...any)
|
||||
// FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Println.
|
||||
FatalDepth(depth int, args ...any)
|
||||
}
|
||||
|
||||
const (
|
||||
// infoLog indicates Info severity.
|
||||
infoLog int = iota
|
||||
// warningLog indicates Warning severity.
|
||||
warningLog
|
||||
// errorLog indicates Error severity.
|
||||
errorLog
|
||||
// fatalLog indicates Fatal severity.
|
||||
fatalLog
|
||||
)
|
||||
|
||||
// severityName contains the string representation of each severity.
|
||||
var severityName = []string{
|
||||
infoLog: "INFO",
|
||||
warningLog: "WARNING",
|
||||
errorLog: "ERROR",
|
||||
fatalLog: "FATAL",
|
||||
}
|
||||
|
||||
// sprintf is fmt.Sprintf.
|
||||
// These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
|
||||
var sprintf = fmt.Sprintf
|
||||
|
||||
// sprint is fmt.Sprint.
|
||||
// These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
|
||||
var sprint = fmt.Sprint
|
||||
|
||||
// sprintln is fmt.Sprintln.
|
||||
// These vars exist to make it possible to test that expensive format calls aren't made unnecessarily.
|
||||
var sprintln = fmt.Sprintln
|
||||
|
||||
// exit is os.Exit.
|
||||
// This var exists to make it possible to test functions calling os.Exit.
|
||||
var exit = os.Exit
|
||||
|
||||
// loggerT is the default logger used by grpclog.
|
||||
type loggerT struct {
|
||||
m []*log.Logger
|
||||
v int
|
||||
jsonFormat bool
|
||||
}
|
||||
|
||||
func (g *loggerT) output(severity int, s string) {
|
||||
sevStr := severityName[severity]
|
||||
if !g.jsonFormat {
|
||||
g.m[severity].Output(2, sevStr+": "+s)
|
||||
return
|
||||
}
|
||||
// TODO: we can also include the logging component, but that needs more
|
||||
// (API) changes.
|
||||
b, _ := json.Marshal(map[string]string{
|
||||
"severity": sevStr,
|
||||
"message": s,
|
||||
})
|
||||
g.m[severity].Output(2, string(b))
|
||||
}
|
||||
|
||||
func (g *loggerT) printf(severity int, format string, args ...any) {
|
||||
// Note the discard check is duplicated in each print func, rather than in
|
||||
// output, to avoid the expensive Sprint calls.
|
||||
// De-duplicating this by moving to output would be a significant performance regression!
|
||||
if lg := g.m[severity]; lg.Writer() == io.Discard {
|
||||
return
|
||||
}
|
||||
g.output(severity, sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (g *loggerT) print(severity int, v ...any) {
|
||||
if lg := g.m[severity]; lg.Writer() == io.Discard {
|
||||
return
|
||||
}
|
||||
g.output(severity, sprint(v...))
|
||||
}
|
||||
|
||||
func (g *loggerT) println(severity int, v ...any) {
|
||||
if lg := g.m[severity]; lg.Writer() == io.Discard {
|
||||
return
|
||||
}
|
||||
g.output(severity, sprintln(v...))
|
||||
}
|
||||
|
||||
func (g *loggerT) Info(args ...any) {
|
||||
g.print(infoLog, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Infoln(args ...any) {
|
||||
g.println(infoLog, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Infof(format string, args ...any) {
|
||||
g.printf(infoLog, format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Warning(args ...any) {
|
||||
g.print(warningLog, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Warningln(args ...any) {
|
||||
g.println(warningLog, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Warningf(format string, args ...any) {
|
||||
g.printf(warningLog, format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Error(args ...any) {
|
||||
g.print(errorLog, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Errorln(args ...any) {
|
||||
g.println(errorLog, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Errorf(format string, args ...any) {
|
||||
g.printf(errorLog, format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Fatal(args ...any) {
|
||||
g.print(fatalLog, args...)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
func (g *loggerT) Fatalln(args ...any) {
|
||||
g.println(fatalLog, args...)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
func (g *loggerT) Fatalf(format string, args ...any) {
|
||||
g.printf(fatalLog, format, args...)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
func (g *loggerT) V(l int) bool {
|
||||
return l <= g.v
|
||||
}
|
||||
|
||||
// LoggerV2Config configures the LoggerV2 implementation.
|
||||
type LoggerV2Config struct {
|
||||
// Verbosity sets the verbosity level of the logger.
|
||||
Verbosity int
|
||||
// FormatJSON controls whether the logger should output logs in JSON format.
|
||||
FormatJSON bool
|
||||
}
|
||||
|
||||
// combineLoggers returns a combined logger for both higher & lower severity logs,
|
||||
// or only one if the other is io.Discard.
|
||||
//
|
||||
// This uses io.Discard instead of io.MultiWriter when all loggers
|
||||
// are set to io.Discard. Both this package and the standard log package have
|
||||
// significant optimizations for io.Discard, which io.MultiWriter lacks (as of
|
||||
// this writing).
|
||||
func combineLoggers(lower, higher io.Writer) io.Writer {
|
||||
if lower == io.Discard {
|
||||
return higher
|
||||
}
|
||||
if higher == io.Discard {
|
||||
return lower
|
||||
}
|
||||
return io.MultiWriter(lower, higher)
|
||||
}
|
||||
|
||||
// NewLoggerV2 creates a new LoggerV2 instance with the provided configuration.
|
||||
// The infoW, warningW, and errorW writers are used to write log messages of
|
||||
// different severity levels.
|
||||
func NewLoggerV2(infoW, warningW, errorW io.Writer, c LoggerV2Config) LoggerV2 {
|
||||
flag := log.LstdFlags
|
||||
if c.FormatJSON {
|
||||
flag = 0
|
||||
}
|
||||
|
||||
warningW = combineLoggers(infoW, warningW)
|
||||
errorW = combineLoggers(errorW, warningW)
|
||||
|
||||
fatalW := errorW
|
||||
|
||||
m := []*log.Logger{
|
||||
log.New(infoW, "", flag),
|
||||
log.New(warningW, "", flag),
|
||||
log.New(errorW, "", flag),
|
||||
log.New(fatalW, "", flag),
|
||||
}
|
||||
return &loggerT{m: m, v: c.Verbosity, jsonFormat: c.FormatJSON}
|
||||
}
|
||||
350
grpclog/internal/loggerv2_test.go
Normal file
350
grpclog/internal/loggerv2_test.go
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2017 gRPC 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 internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// logFuncStr is a string used via testCheckLogContainsFuncStr to test the
|
||||
// logger output.
|
||||
const logFuncStr = "called-func"
|
||||
|
||||
func makeSprintfErr(t *testing.T) func(format string, a ...any) string {
|
||||
return func(string, ...any) string {
|
||||
t.Errorf("got: sprintf called on io.Discard logger, want: expensive sprintf to not be called for io.Discard")
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func makeSprintErr(t *testing.T) func(a ...any) string {
|
||||
return func(...any) string {
|
||||
t.Errorf("got: sprint called on io.Discard logger, want: expensive sprint to not be called for io.Discard")
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// checkLogContainsFuncStr checks that the logger buffer logBuf contains
|
||||
// logFuncStr.
|
||||
func checkLogContainsFuncStr(t *testing.T, logBuf []byte) {
|
||||
if !bytes.Contains(logBuf, []byte(logFuncStr)) {
|
||||
t.Errorf("got '%v', want logger func to be called and print '%v'", string(logBuf), logFuncStr)
|
||||
}
|
||||
}
|
||||
|
||||
// checkBufferWasWrittenAsExpected checks that the log buffer buf was written as expected,
|
||||
// per the discard, logTYpe, msg, and isJSON arguments.
|
||||
func checkBufferWasWrittenAsExpected(t *testing.T, buf *bytes.Buffer, discard bool, logType string, msg string, isJSON bool) {
|
||||
bts, err := buf.ReadBytes('\n')
|
||||
if discard {
|
||||
if err == nil {
|
||||
t.Fatalf("got '%v', want discard %v to not write", string(bts), logType)
|
||||
} else if err != io.EOF {
|
||||
t.Fatalf("got '%v', want discard %v buffer to be EOF", err, logType)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("got '%v', want non-discard %v to not error", err, logType)
|
||||
} else if !bytes.Contains(bts, []byte(msg)) {
|
||||
t.Fatalf("got '%v', want non-discard %v buffer contain message '%v'", string(bts), logType, msg)
|
||||
}
|
||||
if isJSON {
|
||||
obj := map[string]string{}
|
||||
if err := json.Unmarshal(bts, &obj); err != nil {
|
||||
t.Fatalf("got '%v', want non-discard json %v to unmarshal", err, logType)
|
||||
} else if _, ok := obj["severity"]; !ok {
|
||||
t.Fatalf("got '%v', want non-discard json %v to have severity field", "missing severity", logType)
|
||||
|
||||
} else if jsonMsg, ok := obj["message"]; !ok {
|
||||
t.Fatalf("got '%v', want non-discard json %v to have message field", "missing message", logType)
|
||||
|
||||
} else if !strings.Contains(jsonMsg, msg) {
|
||||
t.Fatalf("got '%v', want non-discard json %v buffer contain message '%v'", string(bts), logType, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if b is in the format of:
|
||||
//
|
||||
// 2017/04/07 14:55:42 WARNING: WARNING
|
||||
func checkLogForSeverity(s int, b []byte) error {
|
||||
expected := regexp.MustCompile(fmt.Sprintf(`^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} %s: %s\n$`, severityName[s], severityName[s]))
|
||||
if m := expected.Match(b); !m {
|
||||
return fmt.Errorf("got: %v, want string in format of: %v", string(b), severityName[s]+": 2016/10/05 17:09:26 "+severityName[s])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestLoggerV2Severity(t *testing.T) {
|
||||
buffers := []*bytes.Buffer{new(bytes.Buffer), new(bytes.Buffer), new(bytes.Buffer)}
|
||||
l := NewLoggerV2(buffers[infoLog], buffers[warningLog], buffers[errorLog], LoggerV2Config{})
|
||||
|
||||
l.Info(severityName[infoLog])
|
||||
l.Warning(severityName[warningLog])
|
||||
l.Error(severityName[errorLog])
|
||||
|
||||
for i := 0; i < fatalLog; i++ {
|
||||
buf := buffers[i]
|
||||
// The content of info buffer should be something like:
|
||||
// INFO: 2017/04/07 14:55:42 INFO
|
||||
// WARNING: 2017/04/07 14:55:42 WARNING
|
||||
// ERROR: 2017/04/07 14:55:42 ERROR
|
||||
for j := i; j < fatalLog; j++ {
|
||||
b, err := buf.ReadBytes('\n')
|
||||
if err != nil {
|
||||
t.Fatalf("level %d: %v", j, err)
|
||||
}
|
||||
if err := checkLogForSeverity(j, b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoggerV2PrintFuncDiscardOnlyInfo ensures that logs at the INFO level are
|
||||
// discarded when set to io.Discard, while logs at other levels (WARN, ERROR)
|
||||
// are still printed. It does this by using a custom error function that raises
|
||||
// an error if the logger attempts to print at the INFO level, ensuring early
|
||||
// return when io.Discard is used.
|
||||
func TestLoggerV2PrintFuncDiscardOnlyInfo(t *testing.T) {
|
||||
buffers := []*bytes.Buffer{nil, new(bytes.Buffer), new(bytes.Buffer)}
|
||||
logger := NewLoggerV2(io.Discard, buffers[warningLog], buffers[errorLog], LoggerV2Config{})
|
||||
loggerTp := logger.(*loggerT)
|
||||
|
||||
// test that output doesn't call expensive printf funcs on an io.Discard logger
|
||||
sprintf = makeSprintfErr(t)
|
||||
sprint = makeSprintErr(t)
|
||||
sprintln = makeSprintErr(t)
|
||||
|
||||
loggerTp.output(infoLog, "something")
|
||||
|
||||
sprintf = fmt.Sprintf
|
||||
sprint = fmt.Sprint
|
||||
sprintln = fmt.Sprintln
|
||||
|
||||
loggerTp.output(errorLog, logFuncStr)
|
||||
warnB, err := buffers[warningLog].ReadBytes('\n')
|
||||
if err != nil {
|
||||
t.Fatalf("level %v: %v", warningLog, err)
|
||||
}
|
||||
checkLogContainsFuncStr(t, warnB)
|
||||
|
||||
errB, err := buffers[errorLog].ReadBytes('\n')
|
||||
if err != nil {
|
||||
t.Fatalf("level %v: %v", errorLog, err)
|
||||
}
|
||||
checkLogContainsFuncStr(t, errB)
|
||||
}
|
||||
|
||||
func TestLoggerV2PrintFuncNoDiscard(t *testing.T) {
|
||||
buffers := []*bytes.Buffer{new(bytes.Buffer), new(bytes.Buffer), new(bytes.Buffer)}
|
||||
logger := NewLoggerV2(buffers[infoLog], buffers[warningLog], buffers[errorLog], LoggerV2Config{})
|
||||
loggerTp := logger.(*loggerT)
|
||||
|
||||
loggerTp.output(errorLog, logFuncStr)
|
||||
|
||||
infoB, err := buffers[infoLog].ReadBytes('\n')
|
||||
if err != nil {
|
||||
t.Fatalf("level %v: %v", infoLog, err)
|
||||
}
|
||||
checkLogContainsFuncStr(t, infoB)
|
||||
|
||||
warnB, err := buffers[warningLog].ReadBytes('\n')
|
||||
if err != nil {
|
||||
t.Fatalf("level %v: %v", warningLog, err)
|
||||
}
|
||||
checkLogContainsFuncStr(t, warnB)
|
||||
|
||||
errB, err := buffers[errorLog].ReadBytes('\n')
|
||||
if err != nil {
|
||||
t.Fatalf("level %v: %v", errorLog, err)
|
||||
}
|
||||
checkLogContainsFuncStr(t, errB)
|
||||
}
|
||||
|
||||
// TestLoggerV2PrintFuncAllDiscard tests that discard loggers don't log.
|
||||
func TestLoggerV2PrintFuncAllDiscard(t *testing.T) {
|
||||
logger := NewLoggerV2(io.Discard, io.Discard, io.Discard, LoggerV2Config{})
|
||||
loggerTp := logger.(*loggerT)
|
||||
|
||||
sprintf = makeSprintfErr(t)
|
||||
sprint = makeSprintErr(t)
|
||||
sprintln = makeSprintErr(t)
|
||||
|
||||
// test that printFunc doesn't call the log func on discard loggers
|
||||
// makeLogFuncErr will fail the test if it's called
|
||||
loggerTp.output(infoLog, logFuncStr)
|
||||
loggerTp.output(warningLog, logFuncStr)
|
||||
loggerTp.output(errorLog, logFuncStr)
|
||||
|
||||
sprintf = fmt.Sprintf
|
||||
sprint = fmt.Sprint
|
||||
sprintln = fmt.Sprintln
|
||||
}
|
||||
|
||||
func TestLoggerV2PrintFuncAllCombinations(t *testing.T) {
|
||||
const (
|
||||
print int = iota
|
||||
printf
|
||||
println
|
||||
)
|
||||
|
||||
type testDiscard struct {
|
||||
discardInf bool
|
||||
discardWarn bool
|
||||
discardErr bool
|
||||
|
||||
printType int
|
||||
formatJSON bool
|
||||
}
|
||||
|
||||
discardName := func(td testDiscard) string {
|
||||
strs := []string{}
|
||||
if td.discardInf {
|
||||
strs = append(strs, "discardInfo")
|
||||
}
|
||||
if td.discardWarn {
|
||||
strs = append(strs, "discardWarn")
|
||||
}
|
||||
if td.discardErr {
|
||||
strs = append(strs, "discardErr")
|
||||
}
|
||||
if len(strs) == 0 {
|
||||
strs = append(strs, "noDiscard")
|
||||
}
|
||||
return strings.Join(strs, " ")
|
||||
}
|
||||
var printName = []string{
|
||||
print: "print",
|
||||
printf: "printf",
|
||||
println: "println",
|
||||
}
|
||||
var jsonName = map[bool]string{
|
||||
true: "json",
|
||||
false: "noJson",
|
||||
}
|
||||
|
||||
discardTests := []testDiscard{}
|
||||
for _, di := range []bool{true, false} {
|
||||
for _, dw := range []bool{true, false} {
|
||||
for _, de := range []bool{true, false} {
|
||||
for _, pt := range []int{print, printf, println} {
|
||||
for _, fj := range []bool{true, false} {
|
||||
discardTests = append(discardTests, testDiscard{discardInf: di, discardWarn: dw, discardErr: de, printType: pt, formatJSON: fj})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, discardTest := range discardTests {
|
||||
testName := fmt.Sprintf("%v %v %v", jsonName[discardTest.formatJSON], printName[discardTest.printType], discardName(discardTest))
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
cfg := LoggerV2Config{FormatJSON: discardTest.formatJSON}
|
||||
|
||||
buffers := []*bytes.Buffer{new(bytes.Buffer), new(bytes.Buffer), new(bytes.Buffer)}
|
||||
writers := []io.Writer{buffers[infoLog], buffers[warningLog], buffers[errorLog]}
|
||||
if discardTest.discardInf {
|
||||
writers[infoLog] = io.Discard
|
||||
}
|
||||
if discardTest.discardWarn {
|
||||
writers[warningLog] = io.Discard
|
||||
}
|
||||
if discardTest.discardErr {
|
||||
writers[errorLog] = io.Discard
|
||||
}
|
||||
logger := NewLoggerV2(writers[infoLog], writers[warningLog], writers[errorLog], cfg)
|
||||
|
||||
msgInf := "someinfo"
|
||||
msgWarn := "somewarn"
|
||||
msgErr := "someerr"
|
||||
if discardTest.printType == print {
|
||||
logger.Info(msgInf)
|
||||
logger.Warning(msgWarn)
|
||||
logger.Error(msgErr)
|
||||
} else if discardTest.printType == printf {
|
||||
logger.Infof("%v", msgInf)
|
||||
logger.Warningf("%v", msgWarn)
|
||||
logger.Errorf("%v", msgErr)
|
||||
} else if discardTest.printType == println {
|
||||
logger.Infoln(msgInf)
|
||||
logger.Warningln(msgWarn)
|
||||
logger.Errorln(msgErr)
|
||||
}
|
||||
|
||||
// verify the test Discard, log type, message, and json arguments were logged as-expected
|
||||
|
||||
checkBufferWasWrittenAsExpected(t, buffers[infoLog], discardTest.discardInf, "info", msgInf, cfg.FormatJSON)
|
||||
checkBufferWasWrittenAsExpected(t, buffers[warningLog], discardTest.discardWarn, "warning", msgWarn, cfg.FormatJSON)
|
||||
checkBufferWasWrittenAsExpected(t, buffers[errorLog], discardTest.discardErr, "error", msgErr, cfg.FormatJSON)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerV2Fatal(t *testing.T) {
|
||||
const (
|
||||
print = "print"
|
||||
println = "println"
|
||||
printf = "printf"
|
||||
)
|
||||
printFuncs := []string{print, println, printf}
|
||||
|
||||
exitCalls := []int{}
|
||||
|
||||
if reflect.ValueOf(exit).Pointer() != reflect.ValueOf(os.Exit).Pointer() {
|
||||
t.Error("got: exit isn't os.Exit, want exit var to be os.Exit")
|
||||
}
|
||||
|
||||
exit = func(code int) {
|
||||
exitCalls = append(exitCalls, code)
|
||||
}
|
||||
defer func() {
|
||||
exit = os.Exit
|
||||
}()
|
||||
|
||||
for _, printFunc := range printFuncs {
|
||||
buffers := []*bytes.Buffer{new(bytes.Buffer), new(bytes.Buffer), new(bytes.Buffer)}
|
||||
logger := NewLoggerV2(buffers[infoLog], buffers[warningLog], buffers[errorLog], LoggerV2Config{})
|
||||
switch printFunc {
|
||||
case print:
|
||||
logger.Fatal("fatal")
|
||||
case println:
|
||||
logger.Fatalln("fatalln")
|
||||
case printf:
|
||||
logger.Fatalf("fatalf %d", 42)
|
||||
default:
|
||||
t.Errorf("unknown print type '%v'", printFunc)
|
||||
}
|
||||
|
||||
checkBufferWasWrittenAsExpected(t, buffers[errorLog], false, "fatal", "fatal", false)
|
||||
if len(exitCalls) == 0 {
|
||||
t.Error("got: no exit call, want fatal log to call exit")
|
||||
}
|
||||
exitCalls = []int{}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user