自定义 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)
相关推荐
青 春 记 忆6 小时前
Dify Docker Compose 通用无损升级指南:从备份、双版本预演到切换与回滚
运维·人工智能·python·docker·容器
GlueNa2SiO36 小时前
03-Flask模板引擎Jinja2详解
笔记·python·flask
临沂GEO8 小时前
GEO搜索优化科普|正规地理位置流量运营入门指南
大数据·人工智能·python·流量运营
大模型码小白9 小时前
Spring AI Tool 实现自然语言操作 MySQL 数据库详解
服务器·开发语言·数据库·人工智能·python·mysql·spring
荷蒲10 小时前
【小白量化Qbuddy】利用AI学习中文Python
人工智能·python·学习
数据狐(Datafox)11 小时前
京东商品列表API技术解析与落地应用(含标准 JSON 示例)
java·大数据·前端·人工智能·python·数据分析·json
量化吞吐机12 小时前
2026年下半年手工交易规则走向量化表达,产品该先接住哪一步
人工智能·python
Metaphor69212 小时前
使用 Python 读取和删除 Excel 文件属性
python·excel
迷迭香yy13 小时前
Python构建转融券多空识别系统从接口到因子的全流程 IG50免费开源股票数据API接口
开发语言·python·开源
月光船幽幽14 小时前
真实即粗糙,光滑是伪造
人工智能·python