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.