自定义 Celery的logging模块

为什么需要自定义 Celery 日志

默认的 Celery 日志配置虽然满足基本需求,但在以下情况下可能需要进行自定义:

  • 支持日志滚动:原生celery不支持日志滚动。
  • 更详细的日志信息:需要包含更多上下文信息,以便更好地理解任务的执行过程。
  • 日志格式调整:适应现有的日志管理系统,如 ELK(Elasticsearch, Logstash, Kibana)堆栈。
  • 多日志目标:将日志同时输出到多个地方,例如控制台、文件和远程日志服务器。
  • 日志级别控制:根据不同的模块或任务设定不同的日志级别。

Celery 日志为什么复杂

Celery的日志记录看起来不符合Python的风格,并且显得复杂。这不是Celery的问题,而是因为Python的日志系统默认不支持Celery所需的各种并发环境,如eventlets、greenlets、prefork和线程等。因此,Celery需要自定义日志记录。

Celery提供了一个特殊的函数get_task_logger,它在celery.utils.log中用于获取日志记录器。这个记录器会自动包含任务名称和唯一ID。Celery的文档建议使用get_task_logger。

在worker中应用日志,我更喜欢使用标准的logging.getLogger方法,因为这样可以保持代码库的清晰,并与Celery应用的上下文分离。

策略1:增强Celery日志记录器

Celery提供了一个after_setup_logger信号,该信号在Celery设置日志记录器后触发

(http://docs.celeryproject.org/en/latest/userguide/signals.html#after-setup-logger)。该信号传递了日志记录器对象(以及其他一些参数),你可以方便地向其添加自定义日志处理程序。

Celery 使用 Python 的标准 logging 模块进行日志记录。首先,需要获取 Celery 默认的日志记录器。

python 复制代码
import os
import logging
from celery import Celery
from celery.signals import after_setup_logger

@after_setup_logger.connect
def setup_loggers(logger, *args, **kwargs):
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    # FileHandler
    fh = logging.FileHandler('logs.log')
    fh.setFormatter(formatter)
    logger.addHandler(fh)

    # SysLogHandler
    slh = logging.handlers.SysLogHandler(address=('logsN.papertrailapp.com', '...'))
    slh.setFormatter(formatter)
    logger.addHandler(slh)

策略2:重写Celery根记录器

您可以通过连接setup_logging信号来阻止Celery配置任何记录器。这使您可以完全用自己的配置重写日志配置。

python 复制代码
import logging
import logging.config
from celery.signals import setup_logging

@setup_logging.connect
def setup_logging(**kwargs):
    logfile = kwargs.get("logfile")
    loglevel = kwargs.get("loglevel")
    loglevel_str = logging.getLevelName(loglevel)

    config = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'default': {
                'format': ' %(asctime)s - %(name)s - %(levelname)s - %(message)s',
                'datefmt': "%Y/%m/%d %H:%M:%S"
            }
        },
        'handlers': {
            'rotatingHandler': {
                'class': 'logging.handlers.RotatingFileHandler',
                'filename': logfile,
                'maxBytes': 2*1024*1024,
                'backupCount': 3,
                'formatter': 'default',
                'encoding': 'utf-8'
            },
            'timedHandler': {
                'class': 'logging.handlers.TimedRotatingFileHandler',
                'filename': logfile,
                'when': 'midnight',
                'interval': 1,
                'backupCount': 7,
                'formatter': 'default',
                'encoding': 'utf-8'
            },
        },
        'loggers': {
            'celery': {
                'handlers': ['timedHandler'],
                'level': loglevel_str,
                'propagate': False
            },
        },
        'root': {
            'handlers': ['rotatingHandler'],
            'level': loglevel_str,
            'propagate': False
        },
    }

    logging.config.dictConfig(config)

策略3:禁用Celery logger配置

另一种解决方案是让Celery设置其日志记录器但不使用它,并防止其劫持根日志记录器。

默认情况下,Celery会移除根日志记录器上任何先前配置的处理程序。如果你想自定义自己的日志处理程序而不被Celery干扰,可以通过设置worker_hijack_root_logger=False来禁用此行为。

这允许你重新控制根日志记录器并回退到标准的Python日志设置。

使用这种方法时要小心,因为你需要完全负责确保你的Python日志记录与Celery设置(eventlets、greenlets、prefork(子进程)、线程等)兼容。

python 复制代码
import os
import logging
from celery import Celery
from celery.signals import setup_logging

app = Celery('app')
app.conf.update({
    'worker_hijack_root_logger': False})
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger()

# StreamHandler
sh = logging.StreamHandler()
sh.setFormatter(formatter)
logger.addHandler(sh)

# FileHandler
fh = logging.FileHandler('logs.log')
fh.setFormatter(formatter)
logger.addHandler(fh)

# SysLogHandler
slh = logging.handlers.SysLogHandler(address=('logsN.papertrailapp.com', '...'))
slh.setFormatter(formatter)
logger.addHandler(slh)
相关推荐
橡晟4 小时前
深度学习入门:让神经网络变得“深不可测“⚡(二)
人工智能·python·深度学习·机器学习·计算机视觉
墨尘游子4 小时前
神经网络的层与块
人工智能·python·深度学习·机器学习
倔强青铜34 小时前
苦练Python第18天:Python异常处理锦囊
开发语言·python
企鹅与蟒蛇5 小时前
Ubuntu-25.04 Wayland桌面环境安装Anaconda3之后无法启动anaconda-navigator问题解决
linux·运维·python·ubuntu·anaconda
autobaba5 小时前
编写bat文件自动打开chrome浏览器,并通过selenium抓取浏览器操作chrome
chrome·python·selenium·rpa
Rvelamen6 小时前
LLM-SECURITY-PROMPTS大模型提示词攻击测评基准
人工智能·python·安全
【本人】6 小时前
Django基础(一)———创建与启动
后端·python·django
SHIPKING3938 小时前
【python】基于pygame实现动态粒子爱心
开发语言·python·pygame
kk_stoper9 小时前
如何通过API查询实时能源期货价格
java·开发语言·javascript·数据结构·python·能源
java1234_小锋9 小时前
【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 架构搭建
python·自然语言处理·flask