mirror of
https://github.com/GSA/notifications-api.git
synced 2026-07-26 02:49:42 -04:00
Merge pull request #3201 from alphagov/revamp-celery-stats
Migrate towards new metrics for Celery tasks
This commit is contained in:
@@ -110,7 +110,7 @@ def create_app(application):
|
||||
email_clients = [aws_ses_stub_client] if application.config['SES_STUB_URL'] else [aws_ses_client]
|
||||
notification_provider_clients.init_app(sms_clients=[firetext_client, mmg_client], email_clients=email_clients)
|
||||
|
||||
notify_celery.init_app(application)
|
||||
notify_celery.init_app(application, statsd_client)
|
||||
encryption.init_app(application)
|
||||
redis_store.init_app(application)
|
||||
document_download_client.init_app(application)
|
||||
|
||||
@@ -4,7 +4,6 @@ from celery import Celery, Task
|
||||
from celery.signals import worker_process_shutdown
|
||||
from flask import g, request
|
||||
from flask.ctx import has_app_context, has_request_context
|
||||
from gds_metrics.metrics import Histogram
|
||||
|
||||
|
||||
@worker_process_shutdown.connect
|
||||
@@ -19,60 +18,82 @@ def log_on_worker_shutdown(sender, signal, pid, exitcode, **kwargs):
|
||||
notify_celery._app.logger.info('worker shutdown: PID: {} Exitcode: {}'.format(pid, exitcode))
|
||||
|
||||
|
||||
def make_task(app):
|
||||
SQS_APPLY_ASYNC_DURATION_SECONDS = Histogram(
|
||||
'sqs_apply_async_duration_seconds',
|
||||
'Time taken to put task on queue',
|
||||
['task_name']
|
||||
)
|
||||
|
||||
def make_task(app, statsd_client):
|
||||
class NotifyTask(Task):
|
||||
abstract = True
|
||||
start = None
|
||||
|
||||
def on_success(self, retval, task_id, args, kwargs):
|
||||
elapsed_time = time.time() - self.start
|
||||
elapsed_time = time.monotonic() - self.start
|
||||
delivery_info = self.request.delivery_info or {}
|
||||
queue_name = delivery_info.get('routing_key', 'none')
|
||||
|
||||
app.logger.info(
|
||||
"{task_name} took {time}".format(
|
||||
task_name=self.name, time="{0:.4f}".format(elapsed_time)
|
||||
"Celery task {task_name} (queue: {queue_name}) took {time}".format(
|
||||
task_name=self.name,
|
||||
queue_name=queue_name,
|
||||
time="{0:.4f}".format(elapsed_time)
|
||||
)
|
||||
)
|
||||
|
||||
statsd_client.timing(
|
||||
"celery.{queue_name}.{task_name}.success".format(
|
||||
task_name=self.name,
|
||||
queue_name=queue_name
|
||||
), elapsed_time
|
||||
)
|
||||
|
||||
def on_failure(self, exc, task_id, args, kwargs, einfo):
|
||||
# ensure task will log exceptions to correct handlers
|
||||
app.logger.exception('Celery task: {} failed'.format(self.name))
|
||||
delivery_info = self.request.delivery_info or {}
|
||||
queue_name = delivery_info.get('routing_key', 'none')
|
||||
|
||||
app.logger.exception(
|
||||
"Celery task {task_name} (queue: {queue_name}) failed".format(
|
||||
task_name=self.name,
|
||||
queue_name=queue_name,
|
||||
)
|
||||
)
|
||||
|
||||
statsd_client.incr(
|
||||
"celery.{queue_name}.{task_name}.failure".format(
|
||||
task_name=self.name,
|
||||
queue_name=queue_name
|
||||
)
|
||||
)
|
||||
|
||||
super().on_failure(exc, task_id, args, kwargs, einfo)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
# ensure task has flask context to access config, logger, etc
|
||||
with app.app_context():
|
||||
self.start = time.time()
|
||||
# Remove 'request_id' from the kwargs (so the task doesn't get an unexpected kwarg), then add it to g
|
||||
# so that it gets logged
|
||||
self.start = time.monotonic()
|
||||
# Remove piggyback values from kwargs
|
||||
# Add 'request_id' to 'g' so that it gets logged
|
||||
g.request_id = kwargs.pop('request_id', None)
|
||||
|
||||
return super().__call__(*args, **kwargs)
|
||||
|
||||
def apply_async(self, args=None, kwargs=None, task_id=None, producer=None,
|
||||
link=None, link_error=None, **options):
|
||||
kwargs = kwargs or {}
|
||||
|
||||
if has_request_context() and hasattr(request, 'request_id'):
|
||||
kwargs['request_id'] = request.request_id
|
||||
elif has_app_context() and 'request_id' in g:
|
||||
kwargs['request_id'] = g.request_id
|
||||
|
||||
with SQS_APPLY_ASYNC_DURATION_SECONDS.labels(self.name).time():
|
||||
return super().apply_async(args, kwargs, task_id, producer, link, link_error, **options)
|
||||
return super().apply_async(args, kwargs, task_id, producer, link, link_error, **options)
|
||||
|
||||
return NotifyTask
|
||||
|
||||
|
||||
class NotifyCelery(Celery):
|
||||
|
||||
def init_app(self, app):
|
||||
def init_app(self, app, statsd_client):
|
||||
super().__init__(
|
||||
app.import_name,
|
||||
broker=app.config['BROKER_URL'],
|
||||
task_cls=make_task(app),
|
||||
task_cls=make_task(app, statsd_client),
|
||||
)
|
||||
|
||||
self.conf.update(app.config)
|
||||
|
||||
Reference in New Issue
Block a user