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