github.com/arunkumar7540/cli@v6.45.0+incompatible/actor/v2action/logging.go (about) 1 package v2action 2 3 import ( 4 "sort" 5 "sync" 6 "time" 7 8 "code.cloudfoundry.org/cli/actor/actionerror" 9 "github.com/cloudfoundry/noaa" 10 noaaErrors "github.com/cloudfoundry/noaa/errors" 11 "github.com/cloudfoundry/sonde-go/events" 12 log "github.com/sirupsen/logrus" 13 ) 14 15 const StagingLog = "STG" 16 17 var flushInterval = 300 * time.Millisecond 18 19 type LogMessage struct { 20 message string 21 messageType events.LogMessage_MessageType 22 timestamp time.Time 23 sourceType string 24 sourceInstance string 25 } 26 27 func (log LogMessage) Message() string { 28 return log.message 29 } 30 31 func (log LogMessage) Type() string { 32 if log.messageType == events.LogMessage_OUT { 33 return "OUT" 34 } 35 return "ERR" 36 } 37 38 func (log LogMessage) Staging() bool { 39 return log.sourceType == StagingLog 40 } 41 42 func (log LogMessage) Timestamp() time.Time { 43 return log.timestamp 44 } 45 46 func (log LogMessage) SourceType() string { 47 return log.sourceType 48 } 49 50 func (log LogMessage) SourceInstance() string { 51 return log.sourceInstance 52 } 53 54 func NewLogMessage(message string, messageType int, timestamp time.Time, sourceType string, sourceInstance string) *LogMessage { 55 return &LogMessage{ 56 message: message, 57 messageType: events.LogMessage_MessageType(messageType), 58 timestamp: timestamp, 59 sourceType: sourceType, 60 sourceInstance: sourceInstance, 61 } 62 } 63 64 type LogMessages []*LogMessage 65 66 func (lm LogMessages) Len() int { return len(lm) } 67 68 func (lm LogMessages) Less(i, j int) bool { 69 return lm[i].timestamp.Before(lm[j].timestamp) 70 } 71 72 func (lm LogMessages) Swap(i, j int) { 73 lm[i], lm[j] = lm[j], lm[i] 74 } 75 76 func (actor Actor) GetStreamingLogs(appGUID string, client NOAAClient) (<-chan *LogMessage, <-chan error) { 77 log.Info("Start Tailing Logs") 78 79 ready := actor.setOnConnectBlocker(client) 80 81 incomingLogStream, incomingErrStream := client.TailingLogs(appGUID, actor.Config.AccessToken()) 82 83 outgoingLogStream, outgoingErrStream := actor.blockOnConnect(ready) 84 85 go actor.streamLogsBetween(incomingLogStream, incomingErrStream, outgoingLogStream, outgoingErrStream) 86 87 return outgoingLogStream, outgoingErrStream 88 } 89 90 func (actor Actor) GetRecentLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client NOAAClient) ([]LogMessage, Warnings, error) { 91 app, allWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID) 92 if err != nil { 93 return nil, allWarnings, err 94 } 95 96 noaaMessages, err := client.RecentLogs(app.GUID, actor.Config.AccessToken()) 97 if err != nil { 98 return nil, allWarnings, err 99 } 100 101 noaaMessages = noaa.SortRecent(noaaMessages) 102 103 var logMessages []LogMessage 104 105 for _, message := range noaaMessages { 106 logMessages = append(logMessages, LogMessage{ 107 message: string(message.GetMessage()), 108 messageType: message.GetMessageType(), 109 timestamp: time.Unix(0, message.GetTimestamp()), 110 sourceType: message.GetSourceType(), 111 sourceInstance: message.GetSourceInstance(), 112 }) 113 } 114 115 return logMessages, allWarnings, nil 116 } 117 118 func (actor Actor) GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client NOAAClient) (<-chan *LogMessage, <-chan error, Warnings, error) { 119 app, allWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID) 120 if err != nil { 121 return nil, nil, allWarnings, err 122 } 123 124 messages, logErrs := actor.GetStreamingLogs(app.GUID, client) 125 126 return messages, logErrs, allWarnings, err 127 } 128 129 func (actor Actor) blockOnConnect(ready <-chan bool) (chan *LogMessage, chan error) { 130 outgoingLogStream := make(chan *LogMessage) 131 outgoingErrStream := make(chan error, 1) 132 133 ticker := time.NewTicker(actor.Config.DialTimeout()) 134 135 dance: 136 for { 137 select { 138 case _, ok := <-ready: 139 if !ok { 140 break dance 141 } 142 case <-ticker.C: 143 outgoingErrStream <- actionerror.NOAATimeoutError{} 144 break dance 145 } 146 } 147 148 return outgoingLogStream, outgoingErrStream 149 } 150 151 func (Actor) flushLogs(logs LogMessages, outgoingLogStream chan<- *LogMessage) LogMessages { 152 sort.Stable(logs) 153 for _, l := range logs { 154 outgoingLogStream <- l 155 } 156 return LogMessages{} 157 } 158 159 func (Actor) setOnConnectBlocker(client NOAAClient) <-chan bool { 160 ready := make(chan bool) 161 var onlyRunOnInitialConnect sync.Once 162 callOnConnectOrRetry := func() { 163 onlyRunOnInitialConnect.Do(func() { 164 close(ready) 165 }) 166 } 167 168 client.SetOnConnectCallback(callOnConnectOrRetry) 169 170 return ready 171 } 172 173 func (actor Actor) streamLogsBetween(incomingLogStream <-chan *events.LogMessage, incomingErrStream <-chan error, outgoingLogStream chan<- *LogMessage, outgoingErrStream chan<- error) { 174 log.Info("Processing Log Stream") 175 176 defer close(outgoingLogStream) 177 defer close(outgoingErrStream) 178 179 ticker := time.NewTicker(flushInterval) 180 defer ticker.Stop() 181 182 var logsToBeSorted LogMessages 183 var eventClosed, errClosed bool 184 185 dance: 186 for { 187 select { 188 case event, ok := <-incomingLogStream: 189 if !ok { 190 if !errClosed { 191 log.Debug("logging event stream closed") 192 } 193 eventClosed = true 194 break 195 } 196 197 logsToBeSorted = append(logsToBeSorted, &LogMessage{ 198 message: string(event.GetMessage()), 199 messageType: event.GetMessageType(), 200 timestamp: time.Unix(0, event.GetTimestamp()), 201 sourceInstance: event.GetSourceInstance(), 202 sourceType: event.GetSourceType(), 203 }) 204 case err, ok := <-incomingErrStream: 205 if !ok { 206 if !errClosed { 207 log.Debug("logging error stream closed") 208 } 209 errClosed = true 210 break 211 } 212 213 if _, ok := err.(noaaErrors.RetryError); ok { 214 break 215 } 216 217 if err != nil { 218 outgoingErrStream <- err 219 } 220 case <-ticker.C: 221 log.Debug("processing logsToBeSorted") 222 logsToBeSorted = actor.flushLogs(logsToBeSorted, outgoingLogStream) 223 if eventClosed && errClosed { 224 log.Debug("stopping log processing") 225 break dance 226 } 227 } 228 } 229 }