celery自定义日志格式,自动为输出日志增加任务名(task name)和任务ID(task id)

本文介绍如何在Celery中优化日志记录,通过在日志中加入任务ID和名称,便于日志追踪和问题排查。文章详细描述了在Debian环境下搭建Celery和Redis服务的过程,以及如何自定义日志格式,实现更高效的日志管理和任务执行监控。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于celery是并发执行任务,当打印日志在同一个文件时,不同进程之间的日志就交错堆叠在了一起,想要查询日志回溯某个问题时,总是非常困难。
如果每条log都能带上当前task的ID,就会方便很多。

核心代码:

from celery._state import get_current_task
task = get_current_task()
if task and task.request:
	task_id = task.request.id
	task_name = task.name

一、准备环境

建议使用root账户操作,以免安装、配置、运行权限不够。

  • 操作系统:推荐Debian系列(或其它Linux,celery4.0开始不再支持windows)
    • redis版本:4.0.8 (sudo apt-get install redis-server, sudo service redis-server start)
    • python版本:3.6.5

二、编写程序

  1. celeryconfig.py
import logging

from celery._state import get_current_task

class Formatter(logging.Formatter):
    """Formatter for tasks, adding the task name and id."""

    def format(self, record):
        task = get_current_task()
        if task and task.request:
            record.__dict__.update(task_id='%s ' % task.request.id,
                                   task_name='%s ' % task.name)
        else:
            record.__dict__.setdefault('task_name', '')
            record.__dict__.setdefault('task_id', '')
        return logging.Formatter.format(self, record)


root_logger = logging.getLogger() # 返回logging.root
root_logger.setLevel(logging.DEBUG)

# 将日志输出到文件
fh = logging.FileHandler('celery_worker.log') # 这里注意不要使用TimedRotatingFileHandler,celery的每个进程都会切分,导致日志丢失
formatter = Formatter('[%(task_name)s%(task_id)s%(process)s %(thread)s %(asctime)s %(pathname)s:%(lineno)s] %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
fh.setFormatter(formatter)
fh.setLevel(logging.DEBUG)
root_logger.addHandler(fh)

# 将日志输出到控制台
sh = logging.StreamHandler()
formatter = Formatter('[%(task_name)s%(task_id)s%(process)s %(thread)s %(asctime)s %(pathname)s:%(lineno)s] %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
sh.setFormatter(formatter)
sh.setLevel(logging.INFO)
root_logger.addHandler(sh)

class CeleryConfig(object):
    BROKER_URL = 'redis://localhost:6379/0'
    CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
    CELERY_TASK_SERIALIZER = 'pickle' # " json从4.0版本开始默认json,早期默认为pickle(可以传二进制对象)
    CELERY_RESULT_SERIALIZER = 'pickle'
    CELERY_ACCEPT_CONTENT = ['json', 'pickle']
    CELERY_ENABLE_UTC = True # 启用UTC时区
    CELERY_TIMEZONE = 'Asia/Shanghai' # 上海时区
    CELERYD_HIJACK_ROOT_LOGGER = False # 拦截根日志配置
    CELERYD_MAX_TASKS_PER_CHILD = 1 # 每个进程最多执行1个任务后释放进程(再有任务,新建进程执行,解决内存泄漏)
  1. tasks.py
import logging

from celery import Celery, platforms

import celeryconfig
import detail

platforms.C_FORCE_ROOT = True # 配置里设置了序列化类型为pickle,操作系统开启允许
app = Celery(__name__)
app.config_from_object(celeryconfig.CeleryConfig)


@app.task(bind=True)
def heavy_task(self, seconds=1):
    logging.info("I'm heavy_task") # 默认使用logging.root
    return detail.process_heavy_task(seconds)
  1. detail.py
import logging
import time

def process_heavy_task(seconds=1):
    logging.info("I'm process_heavy_task") # 默认使用logging.root
    time.sleep(seconds)
    return True

三、启动并测试

  1. 新建shell窗口,启动celery服务
# ls
celeyconfig.py	detail.py	tasks.py
# celery worker -A tasks -l info
  1. 新建shell窗口,监控日志文件
# ls
celeyconfig.py	celery_worker.log	detail.py	tasks.py
# tail -f celery_worker.log
  1. 新建shell窗口,调用celery任务
# ls
celeyconfig.py	celery_worker.log	detail.py	tasks.py
# python3
>>> import tasks
>>> t = tasks.heavy_task.delay(3)
>>> t.result
True

四、结果截图

启动worker
监控日志
调用任务

<think>好的,用户想了解如何在Celery中实现自定义日志功能,特别是配置示例。首先,我需要回忆一下Celery日志系统是如何工作的。Celery使用Python的标准logging模块,所以自定义日志应该配置Python日志类似,但可能需要结合Celery的配置方式。 用户提到了他们提供的引用中有关于Celery配置的内容,比如引用[1]中的app.conf.update,这可能是一个切入点。我记得在Celery中,可以通过配置项来设置日志相关的参数,比如日志格式、处理器等。 接下来,我需要考虑如何自定义日志格式。Python的logging模块允许设置格式字符串,比如时间、日志级别、模块等信息。Celery日志配置可能需要通过设置task_default_queue或者其他特定的配置项来指定日志格式。例如,在引用[2]中,用户配置了定时任务,这可能涉及到日志记录任务执行的时间,但直接相关的配置可能不在那里。 另外,引用[3]提到安装消息传输代理创建任务,可能日志的上下文有关,比如任务ID需要记录在日志中。而引用[4]中的worker命可能与日志中的worker标识有关,比如在日志中显示哪个worker处理了任务。 用户的需求是自定义日志,可能需要修改日志格式、存储位置(比如文件)、日志级别等。常见的做法是创建一个自定义日志配置,覆盖Celery默认的日志设置。例如,在创建Celery应用时,通过配置logging模块的handlers、formattersloggers来实现。 可能的步骤包括: 1. 配置Celery日志处理器,比如添加文件处理器。 2. 设置日志格式,包含时间、任务ID、worker称等信息。 3. 调整日志级别,比如设置为DEBUG或INFO。 4. 确保配置在Celery应用初始化时生效。 需要验证示例是否正确。例如,在代码示例中,用户可能需要使用logging.config.dictConfig来应用配置,然后在Celery应用初始化时加载这个配置。同时,要注意worker进程是否能够正确读取这些配置,可能需要确保配置在所有worker节点上一致。 另外,用户可能需要同时配置Celery Beat的日志,如果有定时任务的话,这时候需要确保Beat进程也应用相同的日志配置。引用[2]中提到启动Beat进程,可能需要单独的日志配置,或者在同一个配置中处理。 还需要注意,Celery可能会使用自己的日志称,比如'celery',所以在配置logging时应该针对这个logger进行设置。例如,在dictConfig中的loggers部分添加'celery'的配置,指定handlerslevel。 最后,用户可能需要一个完整的示例代码,展示如何从零开始配置Celery日志,包括文件输出格式设置等。需要确保示例中的路径、格式等是可调整的,并且语法正确,比如正确使用$符号在日志格式字符串中,但根据用户之前的系统级指令,行内数学表达式需要用$...$,但这里日志格式可能不需要,所以可能不需要改动。 检查是否有遗漏的部分,比如日志文件的轮转(RotatingFileHandler)、日志级别过滤等。如果用户需要更高级的功能,可能需要提到这些选项,但当前问题主要是自定义配置示例,所以基础示例即可。</think>### 如何在Celery中实现自定义日志功能 #### 一、基础配置原理 Celery使用Python标准库`logging`模块实现日志系统,支持通过配置文件或代码动态修改日志格式、处理器级别。自定义日志的核心是覆盖默认的日志配置,并通过`celery.signals`增强日志上下文(例如任务ID)[^3]。 #### 二、实现步骤与示例 1. **修改Celery配置** 在初始化Celery应用时,通过`logging.config.dictConfig`定义日志格式处理器: ```python from celery import Celery import logging.config app = Celery('myproject', broker='pyamqp://guest@localhost//') # 自定义日志配置 logging.config.dictConfig({ 'version': 1, 'formatters': { 'custom': { 'format': '[%(asctime)s] [%(levelname)s] [Worker:%(processName)s] [Task:%(task_id)s] %(message)s', 'datefmt': '%Y-%m-%d %H:%M:%S' } }, 'handlers': { 'file': { 'class': 'logging.FileHandler', 'filename': 'celery.log', 'formatter': 'custom', }, 'console': { 'class': 'logging.StreamHandler', 'formatter': 'custom', } }, 'loggers': { 'celery': { 'handlers': ['file', 'console'], 'level': 'INFO', 'propagate': False } } }) app.conf.update( worker_log_format='[%(asctime)s] [%(levelname)s] [Worker:%(processName)s] [Task:%(task_id)s] %(message)s', worker_task_log_format='[%(asctime)s] [%(levelname)s] [Task:%(task_id)s] %(message)s' ) ``` 2. **注入任务ID日志** 使用`after_setup_task_logger`信号添加任务上下文: ```python from celery.signals import after_setup_task_logger @after_setup_task_logger.connect def inject_task_id(logger, **kwargs): from celery._state import get_current_task task = get_current_task() if task and task.request: logger = logging.getLogger('celery.task') logger.addFilter(lambda record: setattr(record, 'task_id', task.request.id)) ``` 3. **验证日志输出** 执行任务后,日志文件`celery.log`将包含如下格式的记录: ``` [2023-10-01 14:30:00] [INFO] [Worker:Worker-1] [Task:5a8d3f1c] Task proj.tasks.add succeeded in 0.2s ``` #### 三、高级优化 - **日志轮转**:使用`RotatingFileHandler`替代`FileHandler`防止日志文件过大。 - **环境区分**:通过`CELERYD_HIJACK_ROOT_LOGGER=False`禁止Celery劫持根日志器,实现多模块统一管理[^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值