github.com/Axway/agent-sdk@v1.1.101/pkg/transaction/metric/metricbatch.go (about) 1 package metric 2 3 import ( 4 "context" 5 "encoding/json" 6 7 "github.com/Axway/agent-sdk/pkg/traceability" 8 beatPub "github.com/elastic/beats/v7/libbeat/publisher" 9 "github.com/rcrowley/go-metrics" 10 ) 11 12 const cancelMsg = "event cancelled, counts added at next publish" 13 14 // EventBatch - creates a batch of MetricEvents to send to Condor 15 type EventBatch struct { 16 beatPub.Batch 17 events []beatPub.Event 18 histograms map[string]metrics.Histogram 19 collector *collector 20 haveBatchLock bool 21 } 22 23 // AddEvent - adds an event to the batch 24 func (b *EventBatch) AddEvent(event beatPub.Event, histogram metrics.Histogram) { 25 b.events = append(b.events, event) 26 eventID := event.Content.Meta[metricKey].(string) 27 b.histograms[eventID] = histogram 28 } 29 30 // AddEvent - adds an event to the batch 31 func (b *EventBatch) AddEventWithoutHistogram(event beatPub.Event) { 32 b.events = append(b.events, event) 33 } 34 35 // Publish - connects to the traceability clients and sends this batch of events 36 func (b *EventBatch) Publish() error { 37 b.batchLock() 38 return b.publish() 39 } 40 41 func (b *EventBatch) publish() error { 42 client, err := traceability.GetClient() 43 if err != nil { 44 go b.logEvents("rejected", b.events) 45 b.batchUnlock() 46 return err 47 } 48 err = client.Connect() 49 if err != nil { 50 go b.logEvents("rejected", b.events) 51 b.batchUnlock() 52 return err 53 } 54 go b.logEvents("publishing", b.events) 55 err = client.Publish(context.Background(), b) 56 if err != nil { 57 b.batchUnlock() 58 return err 59 } 60 return nil 61 } 62 63 // make sure batch does not lock multiple times 64 func (b *EventBatch) batchLock() { 65 if !b.haveBatchLock { 66 b.collector.batchLock.Lock() 67 b.haveBatchLock = true 68 } 69 } 70 71 // make sure batch does not unlock multiple times 72 func (b *EventBatch) batchUnlock() { 73 if b.haveBatchLock { 74 b.collector.batchLock.Unlock() 75 b.haveBatchLock = false 76 } 77 } 78 79 // Events - return the events in the batch 80 func (b *EventBatch) Events() []beatPub.Event { 81 return b.events 82 } 83 84 // ACK - all events have been acknowledgeded, cleanup the counters 85 func (b *EventBatch) ACK() { 86 b.ackEvents(b.events) 87 b.collector.metricStartTime = b.collector.metricEndTime 88 b.batchUnlock() 89 } 90 91 func (b *EventBatch) eventsNotAcked(events []beatPub.Event) { 92 go b.logEvents(cancelMsg, events) 93 b.batchUnlock() 94 } 95 96 // Drop - drop the entire batch 97 func (b *EventBatch) Drop() { 98 b.eventsNotAcked(b.events) 99 } 100 101 // Retry - batch sent for retry, publish again 102 func (b *EventBatch) Retry() { 103 b.eventsNotAcked(b.events) 104 } 105 106 // Cancelled - batch has been cancelled 107 func (b *EventBatch) Cancelled() { 108 b.eventsNotAcked(b.events) 109 } 110 111 // RetryEvents - certain events sent to retry 112 func (b *EventBatch) RetryEvents(events []beatPub.Event) { 113 b.ackEvents(getEventsToAck(events, b.events)) 114 b.eventsNotAcked(events) 115 } 116 117 // CancelledEvents - events have been cancelled 118 func (b *EventBatch) CancelledEvents(events []beatPub.Event) { 119 b.ackEvents(getEventsToAck(events, b.events)) 120 b.eventsNotAcked(events) 121 } 122 123 // Events - return the events in the batch 124 func (b *EventBatch) logEvents(status string, events []beatPub.Event) { 125 for _, event := range events { 126 metric := getMetricFromEvent(event) 127 if metric != nil { 128 b.collector.logMetric(status, metric) 129 } 130 } 131 } 132 133 func (b *EventBatch) ackEvents(events []beatPub.Event) { 134 for _, event := range events { 135 metric := getMetricFromEvent(event) 136 if metric != nil { 137 b.collector.logMetric("published", metric) 138 b.collector.cleanupMetricCounter(b.histograms[metric.EventID], metric) 139 } 140 } 141 } 142 143 // NewEventBatch - creates a new batch 144 func NewEventBatch(c *collector) *EventBatch { 145 return &EventBatch{ 146 collector: c, 147 histograms: make(map[string]metrics.Histogram), 148 haveBatchLock: false, 149 } 150 } 151 152 func getEventsToAck(retryEvents []beatPub.Event, events []beatPub.Event) []beatPub.Event { 153 ackEvents := make([]beatPub.Event, 0) 154 for _, e := range events { 155 eID := getMetricFromEvent(e).EventID 156 found := false 157 for _, rE := range retryEvents { 158 rEID := getMetricFromEvent(rE).EventID 159 if rEID == eID { 160 found = true 161 break 162 } 163 } 164 if !found { 165 ackEvents = append(ackEvents, e) 166 } 167 } 168 return ackEvents 169 } 170 171 func getMetricFromEvent(event beatPub.Event) *APIMetric { 172 if data, found := event.Content.Fields[messageKey]; found { 173 v4Bytes := data.(string) 174 v4Event := make(map[string]interface{}) 175 err := json.Unmarshal([]byte(v4Bytes), &v4Event) 176 if err != nil { 177 return nil 178 } 179 eventType, ok := v4Event["event"] 180 if !ok { 181 return nil 182 } 183 if eventType.(string) != metricEvent { 184 return nil 185 } 186 buf, err := json.Marshal(v4Event["data"]) 187 if err != nil { 188 return nil 189 } 190 metric := &APIMetric{} 191 err = json.Unmarshal(buf, metric) 192 if err != nil { 193 return nil 194 } 195 return metric 196 } 197 return nil 198 }