github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+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  }