You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过日志识别Python脚本的运行发起者:cron定时任务还是人工?

判断Python脚本运行发起者并记录到日志

当然可以实现!要区分脚本是由cron定时任务发起还是人工手动执行,我们可以通过几种方法判断发起者,再把这个信息整合到logging库的日志记录中。下面分步骤给你详细说明:

一、判断运行发起者的几种方法

1. 最可靠:通过环境变量标记

如果你有权限修改crontab配置,这是最准确的方法。在cron任务里手动添加一个环境变量,明确标记任务来源:

# 编辑crontab
crontab -e
# 添加任务时带上环境变量
* * * * * INITIATOR=cron /usr/bin/python3 /path/to/your_script.py

然后在Python脚本里读取这个环境变量:

import os

def get_run_initiator():
    # 存在INITIATOR变量则用它,否则默认是人工执行
    return os.environ.get('INITIATOR', 'manual')

2. 无需修改cron:通过父进程判断

使用psutil库可以获取当前进程的父进程信息,cron发起的脚本父进程通常是cron或crond。首先安装依赖:

pip install psutil

然后编写判断函数:

import psutil
import sys

def get_run_initiator():
    try:
        current_process = psutil.Process()
        parent_process = current_process.parent()
        # 检查父进程名称是否是cron相关进程
        if parent_process and parent_process.name() in ['cron', 'crond']:
            return 'cron'
    except (psutil.NoSuchProcess, psutil.AccessDenied):
        # 无法获取父进程信息时,后续会用其他方法判断
        pass
    # 如果不是cron,再检查是否关联终端(人工执行通常有终端)
    return 'manual' if sys.stdin.isatty() else 'unknown'

3. 轻量无依赖:通过终端判断

人工执行脚本时通常会关联一个终端(比如/dev/pts/0),而cron运行的脚本没有终端关联。可以用sys.stdin.isatty()判断:

import sys

def get_run_initiator():
    return 'manual' if sys.stdin.isatty() else 'cron'

⚠️ 注意:如果人工执行时使用了输入重定向(比如python script.py < input.txt),isatty()会返回False,会被误判为cron发起,所以这个方法适合简单场景。

二、将发起者信息整合到日志

logging库的LogRecord默认没有发起者字段,我们可以通过自定义过滤器给日志记录添加这个属性,这样所有日志都会自动带上发起者信息:

完整代码示例

import os
import sys
import psutil
import logging
from logging.handlers import RotatingFileHandler

def get_run_initiator():
    # 优先用环境变量(最准确)
    initiator = os.environ.get('INITIATOR')
    if initiator:
        return initiator
    
    # 其次检查父进程
    try:
        current_process = psutil.Process()
        parent_process = current_process.parent()
        if parent_process and parent_process.name() in ['cron', 'crond']:
            return 'cron'
    except (psutil.NoSuchProcess, psutil.AccessDenied):
        pass
    
    # 最后用终端判断兜底
    return 'manual' if sys.stdin.isatty() else 'unknown'

# 自定义日志过滤器,给LogRecord添加initiator属性
class InitiatorFilter(logging.Filter):
    def filter(self, record):
        record.initiator = get_run_initiator()
        return True

# 配置logging
def setup_logger():
    logger = logging.getLogger('script_logger')
    logger.setLevel(logging.INFO)
    
    # 添加自定义过滤器
    initiator_filter = InitiatorFilter()
    logger.addFilter(initiator_filter)
    
    # 定义日志格式,新增%(initiator)s字段
    log_format = '%(asctime)s - %(name)s - %(levelname)s - %(initiator)s - %(message)s'
    formatter = logging.Formatter(log_format)
    
    # 设置文件日志处理器(这里用滚动日志,避免文件过大)
    file_handler = RotatingFileHandler(
        'script_run.log',
        maxBytes=1024*1024,  # 1MB
        backupCount=5
    )
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)
    
    return logger

# 使用示例
if __name__ == '__main__':
    logger = setup_logger()
    logger.info('脚本开始执行')
    
    # 你的脚本逻辑...
    logger.info('脚本执行完成')

三、效果说明

配置完成后,日志文件里会出现类似这样的记录:

2024-05-20 14:30:00,123 - script_logger - INFO - cron - 脚本开始执行
2024-05-20 15:45:22,456 - script_logger - INFO - manual - 脚本开始执行

这样就能清晰区分每次运行是cron发起还是人工执行了。

内容的提问来源于stack exchange,提问作者Rahoul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 15:12:36