github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  	"github.com/juju/errors"
    10  	"github.com/juju/loggo"
    11  	wireformat "github.com/juju/romulus/wireformat/metrics"
    12  	"github.com/juju/utils/clock"
    13  
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  var logger = loggo.GetLogger("juju.apiserver.metricsender")
    18  
    19  // MetricSender defines the interface used to send metrics
    20  // to a collection service.
    21  type MetricSender interface {
    22  	Send([]*wireformat.MetricBatch) (*wireformat.Response, error)
    23  }
    24  
    25  var (
    26  	defaultMaxBatchesPerSend              = 1000
    27  	defaultSender            MetricSender = &HTTPSender{}
    28  )
    29  
    30  func handleResponse(mm *state.MetricsManager, st ModelBackend, response wireformat.Response) int {
    31  	var acknowledgedBatches int
    32  	for _, envResp := range response.EnvResponses {
    33  		acknowledgedBatches += len(envResp.AcknowledgedBatches)
    34  		err := st.SetMetricBatchesSent(envResp.AcknowledgedBatches)
    35  		if err != nil {
    36  			logger.Errorf("failed to set sent on metrics %v", err)
    37  		}
    38  		for unitName, status := range envResp.UnitStatuses {
    39  			unit, err := st.Unit(unitName)
    40  			if err != nil {
    41  				logger.Errorf("failed to retrieve unit %q: %v", unitName, err)
    42  				continue
    43  			}
    44  			err = unit.SetMeterStatus(status.Status, status.Info)
    45  			if err != nil {
    46  				logger.Errorf("failed to set unit %q meter status to %v: %v", unitName, status, err)
    47  			}
    48  		}
    49  	}
    50  	if response.NewGracePeriod > 0 {
    51  		err := mm.SetGracePeriod(response.NewGracePeriod)
    52  		if err != nil {
    53  			logger.Errorf("failed to set new grace period %v", err)
    54  		}
    55  	}
    56  	return acknowledgedBatches
    57  }
    58  
    59  // SendMetrics will send any unsent metrics
    60  // over the MetricSender interface in batches
    61  // no larger than batchSize.
    62  func SendMetrics(st ModelBackend, sender MetricSender, clock clock.Clock, batchSize int, transmitVendorMetrics bool) error {
    63  	metricsManager, err := st.MetricsManager()
    64  	if err != nil {
    65  		return errors.Trace(err)
    66  	}
    67  	sent := 0
    68  	held := 0
    69  	for {
    70  		metrics, err := st.MetricsToSend(batchSize)
    71  		if err != nil {
    72  			return errors.Trace(err)
    73  		}
    74  		lenM := len(metrics)
    75  		if lenM == 0 {
    76  			if sent == 0 {
    77  				logger.Infof("nothing to send")
    78  			} else {
    79  				logger.Infof("done sending")
    80  			}
    81  			break
    82  		}
    83  
    84  		var wireData []*wireformat.MetricBatch
    85  		var heldBatches []string
    86  		heldBatchUnits := map[string]bool{}
    87  		for _, m := range metrics {
    88  			if !transmitVendorMetrics && len(m.Credentials()) == 0 {
    89  				heldBatches = append(heldBatches, m.UUID())
    90  				heldBatchUnits[m.Unit()] = true
    91  			} else {
    92  				wireData = append(wireData, ToWire(m))
    93  			}
    94  		}
    95  		response, err := sender.Send(wireData)
    96  		if err != nil {
    97  			logger.Errorf("%+v", err)
    98  			if incErr := metricsManager.IncrementConsecutiveErrors(); incErr != nil {
    99  				logger.Errorf("failed to increment error count %v", incErr)
   100  				return errors.Trace(errors.Wrap(err, incErr))
   101  			}
   102  			return errors.Trace(err)
   103  		}
   104  		if response != nil {
   105  			// TODO (mattyw) We are currently ignoring errors during response handling.
   106  			acknowledged := handleResponse(metricsManager, st, *response)
   107  			// Stop sending if there are no acknowledged batches.
   108  			if acknowledged == 0 {
   109  				logger.Debugf("got 0 acks, ending send loop")
   110  				break
   111  			}
   112  			if err := metricsManager.SetLastSuccessfulSend(clock.Now()); err != nil {
   113  				err = errors.Annotate(err, "failed to set successful send time")
   114  				logger.Warningf("%v", err)
   115  				return errors.Trace(err)
   116  			}
   117  		}
   118  		// Mark held metric batches as sent so that they can be cleaned up later.
   119  		if len(heldBatches) > 0 {
   120  			err := st.SetMetricBatchesSent(heldBatches)
   121  			if err != nil {
   122  				return errors.Annotatef(err, "failed to mark metric batches as sent for %s", st.ModelTag())
   123  			}
   124  		}
   125  
   126  		setHeldBatchUnitMeterStatus(st, heldBatchUnits)
   127  
   128  		sent += len(wireData)
   129  		held += len(heldBatches)
   130  	}
   131  
   132  	unsent, err := st.CountOfUnsentMetrics()
   133  	if err != nil {
   134  		return errors.Trace(err)
   135  	}
   136  	sentStored, err := st.CountOfSentMetrics()
   137  	if err != nil {
   138  		return errors.Trace(err)
   139  	}
   140  	logger.Infof("metrics collection summary for %s: sent:%d unsent:%d held:%d (%d sent metrics stored)", st.ModelTag(), sent, unsent, held, sentStored)
   141  
   142  	return nil
   143  }
   144  
   145  func setHeldBatchUnitMeterStatus(st ModelBackend, units map[string]bool) {
   146  	for unitID, _ := range units {
   147  		unit, err := st.Unit(unitID)
   148  		if err != nil {
   149  			logger.Warningf("failed to get unit for setting held batch meter status: %v", err)
   150  		}
   151  		if err = unit.SetMeterStatus("RED", "transmit-vendor-metrics turned off"); err != nil {
   152  			logger.Warningf("failed to set held batch meter status: %v", err)
   153  		}
   154  	}
   155  }
   156  
   157  // DefaultMaxBatchesPerSend returns the default number of batches per send.
   158  func DefaultMaxBatchesPerSend() int {
   159  	return defaultMaxBatchesPerSend
   160  }
   161  
   162  // DefaultMetricSender returns the default metric sender.
   163  func DefaultMetricSender() MetricSender {
   164  	return defaultSender
   165  }
   166  
   167  // ToWire converts the state.MetricBatch into a type
   168  // that can be sent over the wire to the collector.
   169  func ToWire(mb *state.MetricBatch) *wireformat.MetricBatch {
   170  	metrics := make([]wireformat.Metric, len(mb.Metrics()))
   171  	for i, m := range mb.Metrics() {
   172  		metrics[i] = wireformat.Metric{
   173  			Key:   m.Key,
   174  			Value: m.Value,
   175  			Time:  m.Time.UTC(),
   176  		}
   177  	}
   178  	return &wireformat.MetricBatch{
   179  		UUID:        mb.UUID(),
   180  		ModelUUID:   mb.ModelUUID(),
   181  		UnitName:    mb.Unit(),
   182  		CharmUrl:    mb.CharmURL(),
   183  		Created:     mb.Created().UTC(),
   184  		Metrics:     metrics,
   185  		Credentials: mb.Credentials(),
   186  	}
   187  }