自定义 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)
相关推荐
Zik----几秒前
Anaconda搭建Python虚拟环境并在Pycharm中配置(小白也能懂)
开发语言·人工智能·python·机器学习·pycharm
小周不摆烂1 分钟前
【论文投稿】Python 网络爬虫:探秘网页数据抓取的奇妙世界
python
Srlua17 分钟前
初入图像处理:水稻剑叶夹角测量
python·图形图像
凡人的AI工具箱17 分钟前
每天40分玩转Django:Django缓存
数据库·人工智能·后端·python·缓存·django
问道飞鱼31 分钟前
【Python知识】Python面向对象编程知识
开发语言·python·面向对象·
福如意如我心意1 小时前
conda常用维护命令
python·conda
不听话的好孩子1 小时前
基于深度学习(HyperLPR3框架)的中文车牌识别系统-python程序开发测试
开发语言·python·深度学习
葡萄架子1 小时前
用Python写炸金花游戏
windows·python·游戏
Dovir多多7 小时前
Python数据处理——re库与pydantic的使用总结与实战,处理采集到的思科ASA防火墙设备信息
网络·python·计算机网络·安全·网络安全·数据分析
沐霜枫叶9 小时前
解决pycharm无法识别miniconda
ide·python·pycharm