Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Celery results set task id to something human readable?

After many days I have a working celery and celery beat task list, and the results are stored using django_celery_results. However when I look at the table record, it hasn't got any useful information in there.

is it possible to set the task id to something human readable?

results

one example would be using the demo task, it returns the no, but an unreadable task id

tasks.py

@app.task
def test(a,b):
    return a + b

scheduler in app.settings

CELERYBEAT_SCHEDULE = {
    'test_task': {
        'task': 'home.tasks.test',
        'schedule': crontab(minute='*/1'),
    },
like image 609
AlexW Avatar asked Sep 28 '17 13:09

AlexW


2 Answers

The easy answer is No. The task_id attribute is generated automatically. If you follow the code backwards the core function that generates ID's is in kombu.utils.uuid.uuid(..), which interestingly is just a thin wrapper around the builtin uuid.uuid4(..).

However, if you look at the function signature:

def uuid(_uuid=uuid4):
    """Generate unique id in UUID4 format.
    See Also:
        For now this is provided by :func:`uuid.uuid4`.
    """
    return str(_uuid())

..it appears like it would be possible to supply your own function that generates the ID, and as long as they're unique they should work. Fundamentally you'd have to patch: celery.__init__.uuid, celery.utils.__init__.uuid, and celery.utils.__init__.gen_unique_id.

I don't think that you'd be able to apply worthwhile names since this function is called with no parameters to simply return something unique.

BUT

If you look at Task.apply and Task.apply_async there's an undocumented parameter task_id!

This would allow you to manually specify the task_id before the Task is called/created, and as long as they're globally unique you'll still get all of the reporting and metrics. Unfortunately I don't see any easy way to change the task_id from a Result, to make it more useful after the fact...kinda making your UI's textbox a little silly.


If your project is set up for it, you can use Task Inheritance and modify task behavior that way. In theory you should be able to overwrite apply and apply_async to inject a task's ID.

import time

from celery import Task

 class NamedTask(Task):
    id_gen = lambda: int(time.time() * 1000)


    def _gen_task_id(self):
        return {
            'task_id': '%s-%s' % (
                self.name,
                self.id_gen())}

    def apply(self, *args, **kwargs):
        kwargs.update(self._gen_task_id())
        return Task.apply(self, *args, **kwargs)

    def apply_async(self, *args, **kwargs):
        kwargs.update(self._gen_task_id())
        return Task.apply_async(self, *args, **kwargs)



@task(base=NamedTask)
def add(x, y):
    return x + y

Should make your Task ID look like: project.tasks.add-15073315189870

like image 68
blakev Avatar answered Oct 22 '22 13:10

blakev


Implement a custom result backend that overwrites _store_result to decide what is saved as result to the database.

Depending on which backend you're using find the related class in celery.backends.

This example extends the result for an amqp backend.

class UsefulInfoBackend(AMQPBackend):
    def store_result(self, task_id, result, state,
                     traceback=None, request=None, **kwargs):
        result = super(UsefulInfoBackend, self).store_result(task_id, result, state,
                     traceback=None, request=None, **kwargs)
        result['useful_info'] = 'Very Useful! :)' # determine the rules for extraneous information here contains. 
        return result

When initializing Celery pass your result backend class.

celery.Celery(backend=UsefulInfoBackend)
like image 38
Oluwafemi Sule Avatar answered Oct 22 '22 13:10

Oluwafemi Sule