github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/cf/api/logs/noaa_logs_repository.go (about) 1 package logs 2 3 import ( 4 "errors" 5 "fmt" 6 "time" 7 8 . "code.cloudfoundry.org/cli/cf/i18n" 9 10 "code.cloudfoundry.org/cli/cf/api/authentication" 11 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 12 13 "github.com/cloudfoundry/noaa" 14 noaaerrors "github.com/cloudfoundry/noaa/errors" 15 "github.com/cloudfoundry/sonde-go/events" 16 ) 17 18 type NoaaLogsRepository struct { 19 config coreconfig.Reader 20 consumer NoaaConsumer 21 tokenRefresher authentication.TokenRefresher 22 messageQueue *NoaaMessageQueue 23 BufferTime time.Duration 24 retryTimeout time.Duration 25 } 26 27 func NewNoaaLogsRepository(config coreconfig.Reader, consumer NoaaConsumer, tr authentication.TokenRefresher, retryTimeout time.Duration) *NoaaLogsRepository { 28 consumer.RefreshTokenFrom(tr) 29 return &NoaaLogsRepository{ 30 config: config, 31 consumer: consumer, 32 tokenRefresher: tr, 33 messageQueue: NewNoaaMessageQueue(), 34 BufferTime: defaultBufferTime, 35 retryTimeout: retryTimeout, 36 } 37 } 38 39 func (repo *NoaaLogsRepository) Close() { 40 _ = repo.consumer.Close() 41 } 42 43 func loggableMessagesFromNoaaMessages(messages []*events.LogMessage) []Loggable { 44 loggableMessages := make([]Loggable, len(messages)) 45 46 for i, m := range messages { 47 loggableMessages[i] = NewNoaaLogMessage(m) 48 } 49 50 return loggableMessages 51 } 52 53 func (repo *NoaaLogsRepository) RecentLogsFor(appGUID string) ([]Loggable, error) { 54 logs, err := repo.consumer.RecentLogs(appGUID, repo.config.AccessToken()) 55 56 if err != nil { 57 return loggableMessagesFromNoaaMessages(logs), err 58 } 59 return loggableMessagesFromNoaaMessages(noaa.SortRecent(logs)), err 60 } 61 62 func (repo *NoaaLogsRepository) TailLogsFor(appGUID string, onConnect func(), logChan chan<- Loggable, errChan chan<- error) { 63 ticker := time.NewTicker(repo.BufferTime) 64 retryTimer := newUnstartedTimer() 65 66 endpoint := repo.config.DopplerEndpoint() 67 if endpoint == "" { 68 errChan <- errors.New(T("No doppler loggregator endpoint found. Cannot retrieve logs.")) 69 return 70 } 71 72 repo.consumer.SetOnConnectCallback(func() { 73 retryTimer.Stop() 74 onConnect() 75 }) 76 c, e := repo.consumer.TailingLogs(appGUID, repo.config.AccessToken()) 77 78 go func() { 79 defer close(logChan) 80 defer close(errChan) 81 82 timerRunning := false 83 for { 84 select { 85 case msg, ok := <-c: 86 if !ok { 87 ticker.Stop() 88 repo.flushMessages(logChan) 89 return 90 } 91 timerRunning = false 92 repo.messageQueue.PushMessage(msg) 93 case err := <-e: 94 if err != nil { 95 if _, ok := err.(noaaerrors.RetryError); ok { 96 if !timerRunning { 97 timerRunning = true 98 retryTimer.Reset(repo.retryTimeout) 99 } 100 continue 101 } 102 103 errChan <- err 104 105 ticker.Stop() 106 return 107 } 108 case <-retryTimer.C: 109 errChan <- fmt.Errorf("Timed out waiting for connection to Loggregator (%s).", repo.config.DopplerEndpoint()) 110 ticker.Stop() 111 return 112 } 113 } 114 }() 115 116 go func() { 117 for range ticker.C { 118 repo.flushMessages(logChan) 119 } 120 }() 121 } 122 123 func (repo *NoaaLogsRepository) flushMessages(c chan<- Loggable) { 124 repo.messageQueue.EnumerateAndClear(func(m *events.LogMessage) { 125 c <- NewNoaaLogMessage(m) 126 }) 127 } 128 129 // newUnstartedTimer returns a *time.Timer that is in an unstarted 130 // state. 131 func newUnstartedTimer() *time.Timer { 132 timer := time.NewTimer(time.Second) 133 timer.Stop() 134 return timer 135 }