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  }