github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/metricsender/metricsender.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Package metricsender contains functions for sending
     5  // metrics from a controller to a remote metric collector.
     6  package metricsender
     7  
     8  import (
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	wireformat "github.com/juju/romulus/wireformat/metrics"
    14  
    15  	"github.com/juju/juju/state"
    16  )
    17  
    18  var logger = loggo.GetLogger("juju.apiserver.metricsender")
    19  
    20  // MetricSender defines the interface used to send metrics
    21  // to a collection service.
    22  type MetricSender interface {
    23  	Send([]*wireformat.MetricBatch) (*wireformat.Response, error)
    24  }
    25  
    26  var (
    27  	defaultMaxBatchesPerSend              = 10
    28  	defaultSender            MetricSender = &HttpSender{}
    29  )
    30  
    31  func handleResponse(mm *state.MetricsManager, st *state.State, response wireformat.Response) {
    32  	for _, envResp := range response.EnvResponses {
    33  		err := st.SetMetricBatchesSent(envResp.AcknowledgedBatches)
    34  		if err != nil {
    35  			logger.Errorf("failed to set sent on metrics %v", err)
    36  		}
    37  		for unitName, status := range envResp.UnitStatuses {
    38  			unit, err := st.Unit(unitName)
    39  			if err != nil {
    40  				logger.Errorf("failed to retrieve unit %q: %v", unitName, err)
    41  				continue
    42  			}
    43  			err = unit.SetMeterStatus(status.Status, status.Info)
    44  			if err != nil {
    45  				logger.Errorf("failed to set unit %q meter status to %v: %v", unitName, status, err)
    46  			}
    47  		}
    48  	}
    49  	if response.NewGracePeriod > 0 {
    50  		err := mm.SetGracePeriod(response.NewGracePeriod)
    51  		if err != nil {
    52  			logger.Errorf("failed to set new grace period %v", err)
    53  		}
    54  	}
    55  }
    56  
    57  // SendMetrics will send any unsent metrics
    58  // over the MetricSender interface in batches
    59  // no larger than batchSize.
    60  func SendMetrics(st *state.State, sender MetricSender, batchSize int) error {
    61  	metricsManager, err := st.MetricsManager()
    62  	if err != nil {
    63  		return errors.Trace(err)
    64  	}
    65  	sent := 0
    66  	for {
    67  		metrics, err := st.MetricsToSend(batchSize)
    68  		if err != nil {
    69  			return errors.Trace(err)
    70  		}
    71  		lenM := len(metrics)
    72  		if lenM == 0 {
    73  			if sent == 0 {
    74  				logger.Infof("nothing to send")
    75  			} else {
    76  				logger.Infof("done sending")
    77  			}
    78  			break
    79  		}
    80  		wireData := make([]*wireformat.MetricBatch, lenM)
    81  		for i, m := range metrics {
    82  			wireData[i] = ToWire(m)
    83  		}
    84  		response, err := sender.Send(wireData)
    85  		if err != nil {
    86  			logger.Errorf("%+v", err)
    87  			if incErr := metricsManager.IncrementConsecutiveErrors(); incErr != nil {
    88  				logger.Errorf("failed to increment error count %v", incErr)
    89  				return errors.Trace(errors.Wrap(err, incErr))
    90  			}
    91  			return errors.Trace(err)
    92  		}
    93  		if response != nil {
    94  			// TODO (mattyw) We are currently ignoring errors during response handling.
    95  			handleResponse(metricsManager, st, *response)
    96  			// TODO(fwereade): 2016-03-17 lp:1558657
    97  			if err := metricsManager.SetLastSuccessfulSend(time.Now()); err != nil {
    98  				err = errors.Annotate(err, "failed to set successful send time")
    99  				logger.Warningf("%v", err)
   100  				return errors.Trace(err)
   101  			}
   102  		}
   103  		sent += lenM
   104  	}
   105  
   106  	unsent, err := st.CountOfUnsentMetrics()
   107  	if err != nil {
   108  		return errors.Trace(err)
   109  	}
   110  	sentStored, err := st.CountOfSentMetrics()
   111  	if err != nil {
   112  		return errors.Trace(err)
   113  	}
   114  	logger.Infof("metrics collection summary: sent:%d unsent:%d (%d sent metrics stored)", sent, unsent, sentStored)
   115  
   116  	return nil
   117  }
   118  
   119  // DefaultMaxBatchesPerSend returns the default number of batches per send.
   120  func DefaultMaxBatchesPerSend() int {
   121  	return defaultMaxBatchesPerSend
   122  }
   123  
   124  // DefaultMetricSender returns the default metric sender.
   125  func DefaultMetricSender() MetricSender {
   126  	return defaultSender
   127  }
   128  
   129  // ToWire converts the state.MetricBatch into a type
   130  // that can be sent over the wire to the collector.
   131  func ToWire(mb *state.MetricBatch) *wireformat.MetricBatch {
   132  	metrics := make([]wireformat.Metric, len(mb.Metrics()))
   133  	for i, m := range mb.Metrics() {
   134  		metrics[i] = wireformat.Metric{
   135  			Key:   m.Key,
   136  			Value: m.Value,
   137  			Time:  m.Time.UTC(),
   138  		}
   139  	}
   140  	return &wireformat.MetricBatch{
   141  		UUID:        mb.UUID(),
   142  		ModelUUID:   mb.ModelUUID(),
   143  		UnitName:    mb.Unit(),
   144  		CharmUrl:    mb.CharmURL(),
   145  		Created:     mb.Created().UTC(),
   146  		Metrics:     metrics,
   147  		Credentials: mb.Credentials(),
   148  	}
   149  }