go.undefinedlabs.com/scopeagent@v0.4.2/instrumentation/logging/stdio.go (about)

     1  package logging
     2  
     3  import (
     4  	"bufio"
     5  	"os"
     6  	"strings"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/opentracing/opentracing-go"
    11  	"github.com/opentracing/opentracing-go/log"
    12  
    13  	"go.undefinedlabs.com/scopeagent/instrumentation"
    14  	"go.undefinedlabs.com/scopeagent/tags"
    15  )
    16  
    17  type instrumentedIO struct {
    18  	orig            **os.File
    19  	base            *os.File
    20  	rPipe           *os.File
    21  	wPipe           *os.File
    22  	hSync           sync.WaitGroup
    23  	logRecordsMutex sync.RWMutex
    24  	logRecords      []opentracing.LogRecord
    25  	isError         bool
    26  }
    27  
    28  var (
    29  	patchedStdOut *instrumentedIO
    30  	patchedStdErr *instrumentedIO
    31  )
    32  
    33  // Patch standard output
    34  func PatchStdOut() {
    35  	patchedStdOut = patchIO(&os.Stdout, false)
    36  	recorders = append(recorders, patchedStdOut)
    37  }
    38  
    39  // Unpatch standard output
    40  func UnpatchStdOut() {
    41  	if patchedStdOut != nil {
    42  		patchedStdOut.restore()
    43  		patchedStdOut = nil
    44  	}
    45  }
    46  
    47  // Patch standard error
    48  func PatchStdErr() {
    49  	patchedStdErr = patchIO(&os.Stderr, true)
    50  	recorders = append(recorders, patchedStdErr)
    51  }
    52  
    53  // Unpatch standard error
    54  func UnpatchStdErr() {
    55  	if patchedStdErr != nil {
    56  		patchedStdErr.restore()
    57  		patchedStdErr = nil
    58  	}
    59  }
    60  
    61  // Patch IO File
    62  func patchIO(base **os.File, isError bool) *instrumentedIO {
    63  	rPipe, wPipe, err := os.Pipe()
    64  	if err != nil {
    65  		instrumentation.Logger().Println(err)
    66  		return nil
    67  	}
    68  	instIO := &instrumentedIO{
    69  		orig:    base,
    70  		base:    *base,
    71  		rPipe:   rPipe,
    72  		wPipe:   wPipe,
    73  		isError: isError,
    74  	}
    75  	*base = wPipe
    76  	instIO.hSync.Add(1)
    77  	go instIO.ioHandler()
    78  	return instIO
    79  }
    80  
    81  // Start recording opentracing.LogRecord from logger
    82  func (i *instrumentedIO) Reset() {
    83  	i.logRecordsMutex.Lock()
    84  	defer i.logRecordsMutex.Unlock()
    85  	i.logRecords = nil
    86  }
    87  
    88  // Stop recording opentracing.LogRecord and return all recorded items
    89  func (i *instrumentedIO) GetRecords() []opentracing.LogRecord {
    90  	i.logRecordsMutex.RLock()
    91  	defer i.Reset()
    92  	defer i.logRecordsMutex.RUnlock()
    93  	_ = i.wPipe.Sync()
    94  	_ = i.rPipe.Sync()
    95  	return i.logRecords
    96  }
    97  
    98  // Close handler
    99  func (i *instrumentedIO) restore() {
   100  	i.wPipe.Sync()
   101  	i.rPipe.Sync()
   102  	i.wPipe.Close()
   103  	i.rPipe.Close()
   104  	i.hSync.Wait()
   105  
   106  	if i.orig != nil {
   107  		*i.orig = i.base
   108  	}
   109  }
   110  
   111  // Handles the StdIO pipe for stdout and stderr
   112  func (i *instrumentedIO) ioHandler() {
   113  	defer i.hSync.Done()
   114  	reader := bufio.NewReader(i.rPipe)
   115  	for {
   116  		line, err := reader.ReadString('\n')
   117  		if err != nil {
   118  			// Error or EOF
   119  			break
   120  		}
   121  		nLine := line[:len(line)-1] // removes the last '\n'
   122  		fields := []log.Field{
   123  			log.String(tags.EventType, tags.LogEvent),
   124  			log.String("log.logger", "stdOut"),
   125  		}
   126  		if len(strings.TrimSpace(nLine)) > 0 {
   127  			now := time.Now()
   128  			if i.isError {
   129  				fields = append(fields,
   130  					log.String(tags.EventMessage, nLine),
   131  					log.String(tags.LogEventLevel, tags.LogLevel_ERROR))
   132  			} else {
   133  				fields = append(fields,
   134  					log.String(tags.EventMessage, nLine),
   135  					log.String(tags.LogEventLevel, tags.LogLevel_VERBOSE))
   136  			}
   137  			i.logRecordsMutex.Lock()
   138  			i.logRecords = append(i.logRecords, opentracing.LogRecord{
   139  				Timestamp: now,
   140  				Fields:    fields,
   141  			})
   142  			i.logRecordsMutex.Unlock()
   143  		}
   144  		_, _ = (*i.base).WriteString(line)
   145  	}
   146  }