Celery + Python logging: problems and solutions
by Amit
In my current project, I had a few logging requirements for which I had to look around a bit.
First off, Celery, by default doesn’t respect your application’s logging and redirects all the application’s existing logging to its own logger. I wanted to preserve my application’s logging and this is how I went about doing it based on the answer by Ask Sol on the celery-users list [1]. It worked great.
Second, I wanted the log file to be different everytime a new task was executed. With just the above modification, that won’t happen. Since the after_setup_task_logger signal is invoked only when Celery is starting up. For this, every time a new task request was received, I modified the existing logger handler to a newly created FileHandler.
Finally, I also wanted a way to retrieve the name of the log file which the current logger was using. From the logging documentation, I found that this was not possible. So, I took a hint from this StackOverflow question’s answer [2] and extended the FileHandler class to implement a new method to return the name of the log file.
Here is the complete tasks.py file:
from __future__ import absolute_import import json import os import logging import time import tempfile from celery import Celery from celery.signals import after_setup_task_logger class myFileHandler(logging.FileHandler): def __init__(self, logfile, mode): self.logfile = logfile super(myFileHandler,self).__init__(self.logfile,mode) def getlogfile(self): return self.logfile celery = Celery() celery.config_from_object('celeryconfig') # Return a filename of the form imagebuild_.log def getfilename(): time_now = str(time.time()).split('.') logfile = tempfile.gettempdir() + '/imagebuild_{0:s}.log'.format(time_now[0]+time_now[1]) return logfile @after_setup_task_logger.connect def augment_celery_log(**kwargs): logger = logging.getLogger('imagebuilder') logfile = getfilename() handler = myFileHandler(logfile,'w') formatter = logging.Formatter('%(asctime)s - %(message)s') if not logger.handlers: formatter = logging.Formatter(logging.BASIC_FORMAT) handler.setFormatter(formatter) logger.addHandler(handler) logger.propagate = 0 logger.setLevel(logging.DEBUG) @celery.task def build(buildconfig, kickstart): logger = logging.getLogger('imagebuilder') logfile = getfilename() handler = myFileHandler(logfile,'w') formatter = logging.Formatter('%(asctime)s - %(message)s') handler.setFormatter(formatter) # replace the handler logger.handlers[0] = handler # Your custom code # ..
There may be some unused imports remaining. This solution seems to work for me as of now. Note that this is for Celery 3.0.
Links:
[1] https://groups.google.com/d/topic/celery-users/xNPYTobJ5Rg/discussion
I was in same trouble. So It’s very very useful for me. Thanks.
Though I encountered a trouble, I fixed it.
I try to write the problem and my solution.
I couldn’t execute following line in “myFileHandler” class.
> super(myFileHandler,self).__init__(self.logfile,mode)
error message:
“super() argument 1 must be type, not classobj: super() argument 1 must be type, not classobj”
“myFileHandler” class inherits “logging.FileHandler” class.
“logging.FileHandler” class is not written in ‘new-style class’ in my environment(CentOS 6.0, python 2.6.6).
To be exact, “Filterer” class which is inherited “FileHandler” class is not written in ‘new-style class’.
(“Filterer” class is defined in “/usr/lib64/python2.6/logging/__init__.py” at my environment(CentOS 6.0, python 2.6.6).)
“super()” is needed to use in ‘new-style class’.
So I thought that “Filterer” class(“logging.FileHandler” class) is needed to write in ‘new-style class’.
I fixed this problem like followings.
———
# diff -u /usr/lib64/python2.6/logging/__init__.py.orig /usr/lib64/python2.6/logging/__init__.py
— /usr/lib64/python2.6/logging/__init__.py.orig 2012-09-03 17:13:08.374520006 +0900
+++ /usr/lib64/python2.6/logging/__init__.py 2012-09-03 17:13:33.957516494 +0900
@@ -538,7 +538,8 @@
return 0
return (record.name[self.nlen] == “.”)
-class Filterer:
+#class Filterer:
+class Filterer(object):
“””
A base class for loggers and handlers which allows them to share
common code.
———-
Sorry my broken English.
Thanks.
First off, Celery, by default doesn’t respect your application’s logging and redirects all the application’s existing logging to its own logger.The existence of harm does not mean the relevant action is illegitimate. accounting software singapore
In my current project, I had a few logging requirements for which I had to look around a bit.Joel Fitzgibbon HCG drops
Second, I wanted the log file to be different everytime a new task was executed.safe and untraceable receptacle for leaked information which was then published in its primary form. optometrist portland