如何通过日志识别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
相关产品推荐
相关产品推荐

