Batch cloudwatch puts

This commit is contained in:
Matt Jibson 2015-08-17 12:03:49 -04:00
parent 7c3b555ee9
commit 9a7e5bb35e
2 changed files with 56 additions and 9 deletions

View file

@ -1,12 +1,19 @@
import logging import logging
import boto import boto
import time
from Queue import Queue from Queue import Empty
from threading import Thread from threading import Thread
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
MAX_BATCH_METRICS = 100
# Sleep for this much time between failed send requests.
# This prevents hammering cloudwatch when it's not available.
FAILED_SEND_SLEEP_SECS = 5
def start_cloudwatch_sender(metrics, app): def start_cloudwatch_sender(metrics, app):
""" """
Starts sending from metrics to a new CloudWatchSender. Starts sending from metrics to a new CloudWatchSender.
@ -16,7 +23,7 @@ def start_cloudwatch_sender(metrics, app):
secret_key = app.config['CLOUDWATCH_AWS_SECRET_KEY'] secret_key = app.config['CLOUDWATCH_AWS_SECRET_KEY']
namespace = app.config['CLOUDWATCH_NAMESPACE'] namespace = app.config['CLOUDWATCH_NAMESPACE']
except KeyError: except KeyError:
logger.debug('Cloudwatch not configured') logger.debug('CloudWatch not configured')
return return
sender = CloudWatchSender(metrics, access_key, secret_key, namespace) sender = CloudWatchSender(metrics, access_key, secret_key, namespace)
@ -44,9 +51,43 @@ class CloudWatchSender(Thread):
self._metrics.enable() self._metrics.enable()
while True: while True:
put_metric_args, kwargs = self._metrics.get() metrics = {
logger.debug('Got queued put metrics request.') 'name': [],
'value': [],
'unit': [],
'timestamp': [],
'dimensions': [],
}
metric = self._metrics.get()
append_metric(metrics, metric)
while len(metrics['name']) < MAX_BATCH_METRICS:
try:
metric = self._metrics.get_nowait()
append_metric(metrics, metric)
except Empty:
break
try: try:
connection.put_metric_data(self._namespace, *put_metric_args, **kwargs) connection.put_metric_data(self._namespace, **metrics)
logger.debug('Sent %d CloudWatch metrics', len(metrics['name']))
except: except:
logger.exception('Failed to write to CloudWatch') for i in range(len(metrics['name'])):
self._metrics.put(metrics['name'][i], metrics['value'][i],
unit=metrics['unit'][i],
dimensions=metrics['dimensions'][i],
timestamp=metrics['timestamp'][i],
)
logger.exception('Failed to write to CloudWatch: %s', metrics)
logger.debug('Attempted to requeue %d metrics.', len(metrics['name']))
time.sleep(FAILED_SEND_SLEEP_SECS)
def append_metric(metrics, m):
name, value, kwargs = m
metrics['name'].append(name)
metrics['value'].append(value)
metrics['unit'].append(kwargs.get('unit'))
metrics['dimensions'].append(kwargs.get('dimensions'))
metrics['timestamp'].append(kwargs.get('timestamp'))

View file

@ -1,3 +1,4 @@
import datetime
import logging import logging
import time import time
@ -16,19 +17,24 @@ class MetricQueue(object):
def enable(self, maxsize=10000): def enable(self, maxsize=10000):
self._queue = Queue(maxsize) self._queue = Queue(maxsize)
def put(self, *args, **kwargs): def put(self, name, value, **kwargs):
if self._queue is None: if self._queue is None:
logging.debug('No metric queue: %s %s', args, kwargs) logging.debug('No metric queue: %s %s %s', name, value, kwargs)
return return
try: try:
self._queue.put_nowait((args, kwargs)) kwargs.setdefault('timestamp', datetime.datetime.now())
kwargs.setdefault('dimensions', {})
self._queue.put_nowait((name, value, kwargs))
except Full: except Full:
logger.error('Metric queue full') logger.error('Metric queue full')
def get(self): def get(self):
return self._queue.get() return self._queue.get()
def get_nowait(self):
return self._queue.get_nowait()
def time_blueprint(bp, metric_queue): def time_blueprint(bp, metric_queue):
bp.before_request(time_before_request) bp.before_request(time_before_request)
bp.after_request(time_after_request(bp.name, metric_queue)) bp.after_request(time_after_request(bp.name, metric_queue))