如何让同一Logger在Django和Celery中分别输出到控制台与日志文件
我需要一个可同时在Django和Celery环境中使用的Logger:在Django中运行时输出至控制台,在Celery任务中运行时则通过Celery默认日志处理器输出到--logfile命令行参数指定的日志文件,这对被Django服务器和Celery任务共用的辅助函数尤为重要。
当前我的settings.py文件中的LOGGING配置如下:
LOGLEVEL = os.environ.get("LOGLEVEL", "info").upper() LOGGING = { "version": 1, # the dictConfig format version "disable_existing_loggers": False, # retain the default loggers "handlers": { # console logs to stderr "console": { "class": "logging.StreamHandler", "formatter": "verbose", }, "django.server": DEFAULT_LOGGING["handlers"]["django.server"], }, "formatters": { "verbose": { "format": "[{asctime}]|{levelname}| {message}", "style": "{", }, "django.server": { # "()": "django.utils.log.ServerFormatter", "format": "[{asctime}]|{levelname}| {message}", "style": "{", }, }, "loggers": { # default for all undefined Python modules "": { "level": "WARNING", "handlers": ["console"], }, # Our application code "app": { "level": LOGLEVEL, "handlers": ["console"], # Avoid double logging because of root logger "propagate": False, }, # Default runserver request logging "django.server": DEFAULT_LOGGING["loggers"]["django.server"], "django.channels.server": DEFAULT_LOGGING["loggers"]["django.server"], }, }
目前我的"app" logger仅配置了console处理器。当我定义Logger为logger = get_task_logger("app").getChild(__name__)时,日志输出到控制台但不写入日志文件;若定义为logger = get_task_logger(__name__),则日志写入日志文件但不输出到控制台。我希望将Celery默认日志处理器添加到我的"app" logger中,是否可行?
完全可行,你可以通过以下方式实现共用Logger的需求:
1. 统一Logger获取方式
不要混用get_task_logger和普通Django logger,统一使用标准的logging.getLogger,让Celery自动适配环境添加专属处理器。在共用的辅助函数中这样定义logger:
import logging logger = logging.getLogger(__name__) # 或使用 logging.getLogger("app." + __name__) 明确归属app日志组
2. 调整Django LOGGING配置
保留app logger的控制台处理器,同时开启日志向上传播,让Celery的root级处理器能捕获日志:
"app": { "level": LOGLEVEL, "handlers": ["console"], "propagate": True, # 允许日志传播到root logger,Celery会为root添加文件处理器 },
3. 配置Celery继承Django日志体系
在Celery配置文件(如celery.py)中,关闭root logger劫持,保留Django的日志配置:
from celery import Celery import os from django.conf import settings os.environ.setdefault("DJANGO_SETTINGS_MODULE", "你的项目名.settings") app = Celery("你的项目名") app.config_from_object("django.conf:settings", namespace="CELERY") # 关键配置:不覆盖Django的root logger,保留原有配置同时加载Celery的日志处理器 app.conf.update( worker_hijack_root_logger=False, )
4. 验证效果
- Django环境下:
applogger的日志通过自身配置的console处理器输出到控制台。 - Celery任务中:
applogger的日志会传播到root logger,被Celery自动添加的文件处理器捕获,写入--logfile指定的文件;若Celery开启控制台输出,日志也会同步打印到终端。
替代方案:动态添加Celery处理器
如果不想开启日志传播,可以在Celery启动时手动为app logger添加Celery的处理器:
import logging from celery.utils.log import get_logger # 获取Celery默认日志处理器 celery_root_logger = get_logger("celery") celery_handlers = celery_root_logger.handlers # 获取我们的app logger app_logger = logging.getLogger("app") # 避免重复添加处理器 for handler in celery_handlers: if handler not in app_logger.handlers: app_logger.addHandler(handler)
这样app logger在Celery环境中会同时拥有控制台和文件处理器,Django环境下仅保留控制台处理器,完全匹配你的需求。
内容的提问来源于stack exchange,提问作者hornobster

