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  }