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 }