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 }