github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/actor/v3action/logging.go (about) 1 package v3action 2 3 import ( 4 "sort" 5 "time" 6 7 noaaErrors "github.com/cloudfoundry/noaa/errors" 8 "github.com/cloudfoundry/sonde-go/events" 9 ) 10 11 const StagingLog = "STG" 12 13 var flushInterval = 300 * time.Millisecond 14 15 type LogMessage struct { 16 message string 17 messageType events.LogMessage_MessageType 18 timestamp time.Time 19 sourceType string 20 sourceInstance string 21 } 22 23 func (log LogMessage) Message() string { 24 return log.message 25 } 26 27 func (log LogMessage) Type() string { 28 if log.messageType == events.LogMessage_OUT { 29 return "OUT" 30 } 31 return "ERR" 32 } 33 34 func (log LogMessage) Staging() bool { 35 return log.sourceType == StagingLog 36 } 37 38 func (log LogMessage) Timestamp() time.Time { 39 return log.timestamp 40 } 41 42 func (log LogMessage) SourceType() string { 43 return log.sourceType 44 } 45 46 func (log LogMessage) SourceInstance() string { 47 return log.sourceInstance 48 } 49 50 func NewLogMessage(message string, messageType int, timestamp time.Time, sourceType string, sourceInstance string) *LogMessage { 51 return &LogMessage{ 52 message: message, 53 messageType: events.LogMessage_MessageType(messageType), 54 timestamp: timestamp, 55 sourceType: sourceType, 56 sourceInstance: sourceInstance, 57 } 58 } 59 60 type LogMessages []*LogMessage 61 62 func (lm LogMessages) Len() int { return len(lm) } 63 64 func (lm LogMessages) Less(i, j int) bool { 65 return lm[i].timestamp.Before(lm[j].timestamp) 66 } 67 68 func (lm LogMessages) Swap(i, j int) { 69 lm[i], lm[j] = lm[j], lm[i] 70 } 71 72 func (Actor) GetStreamingLogs(appGUID string, client NOAAClient) (<-chan *LogMessage, <-chan error) { 73 // Do not pass in token because client should have a TokenRefresher set 74 eventStream, errStream := client.TailingLogs(appGUID, "") 75 76 messages := make(chan *LogMessage) 77 errs := make(chan error) 78 79 go func() { 80 defer close(messages) 81 defer close(errs) 82 83 ticker := time.NewTicker(flushInterval) 84 defer ticker.Stop() 85 86 var logs LogMessages 87 dance: 88 for { 89 select { 90 case event, ok := <-eventStream: 91 if !ok { 92 break dance 93 } 94 95 logs = append(logs, &LogMessage{ 96 message: string(event.GetMessage()), 97 messageType: event.GetMessageType(), 98 timestamp: time.Unix(0, event.GetTimestamp()), 99 sourceInstance: event.GetSourceInstance(), 100 sourceType: event.GetSourceType(), 101 }) 102 case err, ok := <-errStream: 103 if !ok { 104 break dance 105 } 106 107 if _, ok := err.(noaaErrors.RetryError); ok { 108 break 109 } 110 111 if err != nil { 112 errs <- err 113 } 114 case <-ticker.C: 115 sort.Stable(logs) 116 for _, l := range logs { 117 messages <- l 118 } 119 120 logs = logs[0:0] 121 } 122 } 123 }() 124 125 return messages, errs 126 } 127 128 func (actor Actor) GetStreamingLogsForApplicationByNameAndSpace(appName string, spaceGUID string, client NOAAClient) (<-chan *LogMessage, <-chan error, Warnings, error) { 129 app, allWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID) 130 if err != nil { 131 return nil, nil, allWarnings, err 132 } 133 134 messages, logErrs := actor.GetStreamingLogs(app.GUID, client) 135 136 return messages, logErrs, allWarnings, err 137 }