python - 使用 cron 管理员从 cron 作业和使用 django manage.py 的命令行运行时,脚本的行为不同
问题描述
我知道 crons 在与命令行不同的环境中运行,但我在任何地方都使用绝对路径,我不明白为什么我的脚本表现不同。我相信它与我的 cron_supervisor 有关,它在子进程中运行 django“manage.py”。
克朗:
0 * * * * /home/p1/.virtualenvs/prod/bin/python /home/p1/p1/manage.py cron_supervisor --command="/home/p1/.virtualenvs/prod/bin/python /home/p1/p1/manage.py envoyer_argent"
这将调用 cron_supervisor,它调用了脚本,但脚本不会像我运行时那样执行:
/home/p1/.virtualenvs/prod/bin/python /home/p1/p1/manage.py envoyer_argent
当通过另一个脚本运行脚本时,是否需要做一些特别的事情才能正确调用脚本?
这是主管,它基本上是用于错误处理并确保在 cron 脚本本身出现问题时得到警告。
import logging
import os
from subprocess import PIPE, Popen
from django.core.management.base import BaseCommand
from command_utils import email_admin_error, isomorphic_logging
from utils.send_slack_message import send_slack_message
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIR = CURRENT_DIR + '/../../../'
logging.basicConfig(
level=logging.INFO,
filename=PROJECT_DIR + 'cron-supervisor.log',
format='%(asctime)s %(levelname)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
class Command(BaseCommand):
help = "Control a subprocess"
def add_arguments(self, parser):
parser.add_argument(
'--command',
dest='command',
help="Command to execute",
)
parser.add_argument(
'--mute_on_success',
dest='mute_on_success',
action='store_true',
help="Don't post any massage on success",
)
def handle(self, *args, **options):
try:
isomorphic_logging(logging, "Starting cron supervisor with command \"" + options['command'] + "\"")
if options['command']:
self.command = options['command']
else:
error_message = "Empty required parameter --command"
# log error
isomorphic_logging(logging, error_message, "error")
# send slack message
send_slack_message("Cron Supervisor Error: " + error_message)
# send email to admin
email_admin_error("Cron Supervisor Error", error_message)
raise ValueError(error_message)
if options['mute_on_success']:
self.mute_on_success = True
else:
self.mute_on_success = False
# running process
process = Popen([self.command], stdout=PIPE, stderr=PIPE, shell=True)
output, error = process.communicate()
if output:
isomorphic_logging(logging, "Output from cron:" + output)
# check for any subprocess error
if process.returncode != 0:
error_message = 'Command \"{command}\" - Error \nReturn code: {code}\n```{error}```'.format(
code=process.returncode,
error=error,
command=self.command,
)
self.handle_error(error_message)
else:
message = "Command \"{command}\" ended without error".format(command=self.command)
isomorphic_logging(logging, message)
# post message on slack if process isn't muted_on_success
if not self.mute_on_success:
send_slack_message(message)
except Exception as e:
error_message = 'Command \"{command}\" - Error \n```{error}```'.format(
error=e,
command=self.command,
)
self.handle_error(error_message)
def handle_error(self, error_message):
# log the error in local file
isomorphic_logging(logging, error_message)
# post message in slack
send_slack_message(error_message)
# email admin
email_admin_error("Cron Supervisor Error", error_message)
由 cron 通过 cron_supervisor 调用时脚本未正确执行的示例:
# -*- coding: utf-8 -*-
import json
import logging
import os
from django.conf import settings
from django.core.management.base import BaseCommand
from utils.lock import handle_lock
logging.basicConfig(
level=logging.INFO,
filename=os.path.join(settings.BASE_DIR, 'crons.log'),
format='%(asctime)s %(levelname)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
class Command(BaseCommand):
help = "Envoi de l'argent en attente"
@handle_lock
def handle(self, *args, **options):
logging.info("some logs that won't be log (not called)")
logging.info("Those logs will be correcly logged")
此外,我还有另一个我也不太了解的日志记录问题,我指定将日志存储在cron-supervisor.log
其中,但它们没有存储在那里,我不知道为什么。(但这与我的主要问题无关,只是对调试没有帮助)
解决方案
Your cron
job can't just run the Python interpreter in the virtualenv; this is completely insufficient. You need to activate
the env just like in an interactive environment.
0 * * * * . /home/p1/.virtualenvs/prod/bin/activate; python /home/p1/p1/manage.py cron_supervisor --command="python /home/p1/p1/manage.py envoyer_argent"
This is already complex enough that you might want to create a separate wrapper script containing these commands.
Without proper diagnostics of how your current script doesn't work, it's entirely possible that this fix alone is insufficient. Cron jobs do not only (or particularly) need absoute paths; the main differences compared to interactive shells is that cron jobs run with a different and more spare environment, where e.g. the shell's PATH
, various library paths, environment variables etc can be different or missing altogether; and of course, no interactive facilities are available.
The system variables will hopefully be taken care of by your virtualenv; if it's correctly done, activating it will set up all the variables (PATH
, PYTHONPATH
, etc) your script needs. There could still be things like locale settings which are set up by your shell only when you log in interactively; but again, without details, let's just hope this isn't an issue for you.
The reason some people recommend absolute paths is that this will work regardless of your working directory. But a correctly written script should work fine in any directory; if it matters, the cron job will start in the owner's home directory. If you wanted to point to a relative path from there, this will work fine inside a cron job just as it does outside.
As an aside, you probably should not use subprocess.Popen()
if one of the higher-level wrappers from the subprocess
module do what you want. Unless compatibility with legacy Python versions is important, you should probably use subprocess.run()
... though running Python as a subprocess of Python is also often a useless oomplication. See also my answer to this related question.
推荐阅读
- websocket - 我们如何在jmeter webscoket负载测试中获得客户端请求和服务器接收到的请求之间的一一对应关系
- javascript - 使用 Axios 返回具有多个响应的数组
- javascript - 如何仅在使用猫鼬的数据库中没有文档时为数据库播种
- python - 使用新的 xgboost-distribution 时导入错误
- ms-access - 在 MS Access - 设计视图中生成超出限制的销售计数
- node.js - nodejs 路由上的日期错误,获取“NaN”或“无效日期”
- javascript - 使用 GTM 部署的 Javascript 函数:只有 click 事件,而不是 mouseover/mouseenter 事件,正在调用该函数
- python - 当另一个人使用命令时,如何对 discord.py 机器人进行编程以通知我?
- clojure - clojure 对任何关于命名约定(fns)的工作都是开放的,特别是关于类泛型 fns 作为部分
- arrays - 为什么我的指针(“char *item”)变量被视为常规变量(“char item”)?