github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/common/flogging/floggingtest/logger.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package floggingtest
     8  
     9  import (
    10  	"bytes"
    11  	"regexp"
    12  	"strings"
    13  	"sync"
    14  	"testing"
    15  
    16  	"github.com/hyperledger/fabric/common/flogging"
    17  	"github.com/hyperledger/fabric/common/flogging/fabenc"
    18  	"github.com/onsi/gomega/gbytes"
    19  	"go.uber.org/zap"
    20  	"go.uber.org/zap/buffer"
    21  	"go.uber.org/zap/zapcore"
    22  )
    23  
    24  // DefaultFormat is a log encoding format that is mostly compatible with the default
    25  // log format but excludes colorization and time.
    26  const DefaultFormat = "[%{module}] %{shortfunc} -> %{level:.4s} %{id:04x} %{message}"
    27  
    28  type Recorder struct {
    29  	mutex    sync.RWMutex
    30  	entries  []string
    31  	messages []string
    32  	buffer   *gbytes.Buffer
    33  }
    34  
    35  func newRecorder() *Recorder {
    36  	return &Recorder{
    37  		buffer:   gbytes.NewBuffer(),
    38  		entries:  []string{},
    39  		messages: []string{},
    40  	}
    41  }
    42  
    43  func (r *Recorder) addEntry(e zapcore.Entry, line *buffer.Buffer) {
    44  	r.mutex.Lock()
    45  	defer r.mutex.Unlock()
    46  
    47  	r.buffer.Write(line.Bytes())
    48  	r.entries = append(r.entries, strings.TrimRight(line.String(), "\n"))
    49  	r.messages = append(r.messages, e.Message)
    50  }
    51  
    52  func (r *Recorder) Reset() {
    53  	r.mutex.Lock()
    54  	defer r.mutex.Unlock()
    55  	r.buffer = gbytes.NewBuffer()
    56  	r.entries = []string{}
    57  	r.messages = []string{}
    58  }
    59  
    60  func (r *Recorder) Buffer() *gbytes.Buffer {
    61  	r.mutex.RLock()
    62  	defer r.mutex.RUnlock()
    63  	return r.buffer
    64  }
    65  
    66  func (r *Recorder) Entries() []string {
    67  	r.mutex.RLock()
    68  	defer r.mutex.RUnlock()
    69  
    70  	entries := make([]string, len(r.entries), cap(r.entries))
    71  	copy(entries, r.entries)
    72  	return entries
    73  }
    74  
    75  func (r *Recorder) EntriesContaining(sub string) []string {
    76  	r.mutex.RLock()
    77  	defer r.mutex.RUnlock()
    78  
    79  	matches := []string{}
    80  	for _, entry := range r.entries {
    81  		if strings.Contains(entry, sub) {
    82  			matches = append(matches, entry)
    83  		}
    84  	}
    85  	return matches
    86  }
    87  
    88  func (r *Recorder) EntriesMatching(regex string) []string {
    89  	re := regexp.MustCompile(regex)
    90  	r.mutex.RLock()
    91  	defer r.mutex.RUnlock()
    92  
    93  	matches := []string{}
    94  	for _, entry := range r.entries {
    95  		if re.MatchString(entry) {
    96  			matches = append(matches, entry)
    97  		}
    98  	}
    99  	return matches
   100  }
   101  
   102  func (r *Recorder) Messages() []string {
   103  	r.mutex.RLock()
   104  	defer r.mutex.RUnlock()
   105  
   106  	messages := make([]string, len(r.messages), cap(r.messages))
   107  	copy(messages, r.messages)
   108  	return messages
   109  }
   110  
   111  func (r *Recorder) MessagesContaining(sub string) []string {
   112  	r.mutex.RLock()
   113  	defer r.mutex.RUnlock()
   114  
   115  	matches := []string{}
   116  	for _, msg := range r.messages {
   117  		if strings.Contains(msg, sub) {
   118  			matches = append(matches, msg)
   119  		}
   120  	}
   121  	return matches
   122  }
   123  
   124  func (r *Recorder) MessagesMatching(regex string) []string {
   125  	re := regexp.MustCompile(regex)
   126  	r.mutex.RLock()
   127  	defer r.mutex.RUnlock()
   128  
   129  	matches := []string{}
   130  	for _, msg := range r.messages {
   131  		if re.MatchString(msg) {
   132  			matches = append(matches, msg)
   133  		}
   134  	}
   135  	return matches
   136  }
   137  
   138  type RecordingCore struct {
   139  	zapcore.LevelEnabler
   140  	encoder  zapcore.Encoder
   141  	recorder *Recorder
   142  	writer   zapcore.WriteSyncer
   143  }
   144  
   145  func (r *RecordingCore) Write(e zapcore.Entry, fields []zapcore.Field) error {
   146  	buf, err := r.encoder.EncodeEntry(e, fields)
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	r.writer.Write(buf.Bytes())
   152  	r.recorder.addEntry(e, buf)
   153  
   154  	buf.Free()
   155  
   156  	return nil
   157  }
   158  
   159  func (r *RecordingCore) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
   160  	if r.Enabled(e.Level) {
   161  		ce = ce.AddCore(e, r)
   162  	}
   163  	if ce != nil && e.Level == zapcore.FatalLevel {
   164  		panic(e.Message)
   165  	}
   166  	return ce
   167  }
   168  
   169  func (r *RecordingCore) With(fields []zapcore.Field) zapcore.Core {
   170  	clone := &RecordingCore{
   171  		LevelEnabler: r.LevelEnabler,
   172  		encoder:      r.encoder.Clone(),
   173  		recorder:     r.recorder,
   174  		writer:       r.writer,
   175  	}
   176  
   177  	for _, f := range fields {
   178  		f.AddTo(clone.encoder)
   179  	}
   180  
   181  	return clone
   182  }
   183  
   184  func (r *RecordingCore) Sync() error {
   185  	return r.writer.Sync()
   186  }
   187  
   188  type TestingWriter struct{ testing.TB }
   189  
   190  func (t *TestingWriter) Write(buf []byte) (int, error) {
   191  	t.Logf("%s", bytes.TrimRight(buf, "\n"))
   192  	return len(buf), nil
   193  }
   194  
   195  func (t *TestingWriter) Sync() error { return nil }
   196  
   197  type Option func(r *RecordingCore, l *zap.Logger) *zap.Logger
   198  
   199  func Named(loggerName string) Option {
   200  	return func(r *RecordingCore, l *zap.Logger) *zap.Logger {
   201  		return l.Named(loggerName)
   202  	}
   203  }
   204  
   205  func AtLevel(level zapcore.Level) Option {
   206  	return func(r *RecordingCore, l *zap.Logger) *zap.Logger {
   207  		r.LevelEnabler = zap.LevelEnablerFunc(func(l zapcore.Level) bool {
   208  			return level.Enabled(l)
   209  		})
   210  		return l
   211  	}
   212  }
   213  
   214  func NewTestLogger(tb testing.TB, options ...Option) (*flogging.FabricLogger, *Recorder) {
   215  	enabler := zap.LevelEnablerFunc(func(l zapcore.Level) bool {
   216  		return zapcore.DebugLevel.Enabled(l)
   217  	})
   218  
   219  	formatters, err := fabenc.ParseFormat(DefaultFormat)
   220  	if err != nil {
   221  		tb.Fatalf("failed to parse format %s: %s", DefaultFormat, err)
   222  	}
   223  	encoder := fabenc.NewFormatEncoder(formatters...)
   224  	if err != nil {
   225  		tb.Fatalf("failed to create format encoder: %s", err)
   226  	}
   227  
   228  	recorder := newRecorder()
   229  	recordingCore := &RecordingCore{
   230  		LevelEnabler: enabler,
   231  		encoder:      encoder,
   232  		recorder:     recorder,
   233  		writer:       &TestingWriter{TB: tb},
   234  	}
   235  
   236  	zl := zap.New(recordingCore)
   237  	for _, o := range options {
   238  		zl = o(recordingCore, zl)
   239  	}
   240  
   241  	return flogging.NewFabricLogger(zl, zap.AddCaller()), recorder
   242  }