github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/logger/target/testlogger/testlogger.go (about) 1 // Copyright (c) 2015-2023 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 // Package testlogger contains an autoregistering logger that can be used to capture logging events 19 // for individual tests. 20 // This package should only be included by test files. 21 // To enable logging for a test, use: 22 // 23 // func TestSomething(t *testing.T) { 24 // defer testlogger.T.SetLogTB(t)() 25 // 26 // This cannot be used for parallel tests. 27 package testlogger 28 29 import ( 30 "context" 31 "fmt" 32 "os" 33 "strings" 34 "sync/atomic" 35 "testing" 36 37 "github.com/minio/minio/internal/logger" 38 "github.com/minio/minio/internal/logger/target/types" 39 "github.com/minio/pkg/v2/logger/message/log" 40 ) 41 42 const ( 43 logMessage = iota 44 errorMessage 45 fatalMessage 46 ) 47 48 // T is the test logger. 49 var T = &testLogger{} 50 51 func init() { 52 logger.AddSystemTarget(context.Background(), T) 53 } 54 55 type testLogger struct { 56 current atomic.Pointer[testing.TB] 57 action atomic.Int32 58 } 59 60 // SetLogTB will set the logger to output to tb. 61 // Call the returned function to disable logging. 62 func (t *testLogger) SetLogTB(tb testing.TB) func() { 63 return t.setTB(tb, logMessage) 64 } 65 66 // SetErrorTB will set the logger to output to tb.Error. 67 // Call the returned function to disable logging. 68 func (t *testLogger) SetErrorTB(tb testing.TB) func() { 69 return t.setTB(tb, errorMessage) 70 } 71 72 // SetFatalTB will set the logger to output to tb.Panic. 73 // Call the returned function to disable logging. 74 func (t *testLogger) SetFatalTB(tb testing.TB) func() { 75 return t.setTB(tb, fatalMessage) 76 } 77 78 func (t *testLogger) setTB(tb testing.TB, action int32) func() { 79 old := t.action.Swap(action) 80 t.current.Store(&tb) 81 return func() { 82 t.current.Store(nil) 83 t.action.Store(old) 84 } 85 } 86 87 func (t *testLogger) String() string { 88 tb := t.current.Load() 89 if tb != nil { 90 tbb := *tb 91 return tbb.Name() 92 } 93 return "" 94 } 95 96 func (t *testLogger) Endpoint() string { 97 return "" 98 } 99 100 func (t *testLogger) Stats() types.TargetStats { 101 return types.TargetStats{} 102 } 103 104 func (t *testLogger) Init(ctx context.Context) error { 105 return nil 106 } 107 108 func (t *testLogger) IsOnline(ctx context.Context) bool { 109 return t.current.Load() != nil 110 } 111 112 func (t *testLogger) Cancel() { 113 t.current.Store(nil) 114 } 115 116 func (t *testLogger) Send(ctx context.Context, entry interface{}) error { 117 tb := t.current.Load() 118 var logf func(format string, args ...any) 119 if tb != nil { 120 tbb := *tb 121 tbb.Helper() 122 switch t.action.Load() { 123 case errorMessage: 124 logf = tbb.Errorf 125 case fatalMessage: 126 logf = tbb.Fatalf 127 default: 128 logf = tbb.Logf 129 } 130 } else { 131 switch t.action.Load() { 132 case errorMessage: 133 logf = func(format string, args ...any) { 134 fmt.Fprintf(os.Stderr, format+"\n", args...) 135 } 136 case fatalMessage: 137 logf = func(format string, args ...any) { 138 fmt.Fprintf(os.Stderr, format+"\n", args...) 139 } 140 defer os.Exit(1) 141 default: 142 logf = func(format string, args ...any) { 143 fmt.Fprintf(os.Stdout, format+"\n", args...) 144 } 145 } 146 } 147 148 switch v := entry.(type) { 149 case log.Entry: 150 if v.Trace == nil { 151 logf("%s: %s", v.Level, v.Message) 152 } else { 153 msg := fmt.Sprintf("%s: %+v", v.Level, v.Trace.Message) 154 for i, m := range v.Trace.Source { 155 if i == 0 && strings.Contains(m, "logger.go:") { 156 continue 157 } 158 msg += fmt.Sprintf("\n%s", m) 159 } 160 logf("%s", msg) 161 } 162 default: 163 logf("%+v (%T)", v, v) 164 } 165 return nil 166 } 167 168 func (t *testLogger) Type() types.TargetType { 169 return types.TargetConsole 170 }