Python 当从cron作业运行时,脚本的行为不同;当使用cron监控器时,使用django manage.py从命令行运行时,脚本的行为不同
我知道cron运行在不同于命令行的环境中,但我在任何地方都使用绝对路径,我不明白为什么我的脚本行为不同。我相信这与我的cron_主管有关,他在一个子流程中运行django“manage.py” 克朗: 这将调用cron_supervisor,并调用脚本,但脚本不会像我运行时那样执行:Python 当从cron作业运行时,脚本的行为不同;当使用cron监控器时,使用django manage.py从命令行运行时,脚本的行为不同,python,django,cron,Python,Django,Cron,我知道cron运行在不同于命令行的环境中,但我在任何地方都使用绝对路径,我不明白为什么我的脚本行为不同。我相信这与我的cron_主管有关,他在一个子流程中运行django“manage.py” 克朗: 这将调用cron_supervisor,并调用脚本,但脚本不会像我运行时那样执行: /home/p1/.virtualenvs/prod/bin/python /home/p1/p1/manage.py envoyer_argent 在通过另一个脚本运行脚本时,是否需要执行一些特殊的操作才能正确
/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_主管调用脚本时,脚本未正确执行的示例:
# -*- 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中,但它们没有存储在那里,我不知道为什么。(但这与我的主要问题无关,只是无助于调试)您的cron
作业不能在virtualenv中运行Python解释器;这是完全不够的。您需要像在交互式环境中一样激活
环境
0 * * * * . /home/p1/.virtualenvs/prod/bin/activate; python /home/p1/p1/manage.py cron_supervisor --command="python /home/p1/p1/manage.py envoyer_argent"
这已经足够复杂,您可能需要创建一个包含这些命令的单独包装器脚本
如果不正确诊断当前脚本的工作方式,完全有可能仅此修复是不够的。Cron作业不仅(或特别)需要绝对路径;与交互式shell相比,主要区别在于cron作业在不同且更空闲的环境中运行,例如shell的路径
、各种库路径、环境变量等可能不同或完全缺失;当然,没有互动设施
系统变量有望由您的virtualenv处理;如果操作正确,激活它将设置脚本所需的所有变量(PATH
,PYTHONPATH
,等等)。仍然可能有类似于区域设置的东西,只有当您以交互方式登录时,才由shell设置;但是再一次,没有细节,让我们希望这不是你的问题
有些人推荐绝对路径的原因是,无论您的工作目录如何,绝对路径都可以工作。但是,一个正确编写的脚本在任何目录中都可以正常工作;如果重要的话,cron作业将在所有者的主目录中启动。如果您想从那里指向一个相对路径,那么在cron作业内部和外部都可以正常工作
另外,如果子流程
模块中的一个更高级别的包装器执行您想要的操作,您可能不应该使用子流程.Popen()。除非与旧式Python版本的兼容性很重要,否则您可能应该使用subprocess.run()
。。。虽然将Python作为Python的子进程运行通常也是一种无用的oomplication。另请参见我对的回答,具体取决于.virtualenvs/prod/bin/python
的创建方式,它可能只是系统python的一个符号链接。您需要做更多的工作才能真正激活virtualenv
。此外,在cron
和交互使用之间还有许多其他不同的因素;请参阅,或者也可以参阅此常见问题解答:此外,您似乎正在重新发明子流程.call()
。如果您有一个非常最新的Python,您可能应该查看subprocess.run()
。另请参阅,特别是我的答案。我已经使用process=subprocess.Popen(bash_命令,stdout=subprocess.PIPE,shell=True)
来运行实际的脚本。我查看了你的链接,但找不到任何与我的问题相关的内容。.virtualenvs
文件夹是使用virtualenv\u包装器创建的。感谢您的深入解释,非常有用。我试图激活
,但是权限被拒绝
,所以我在生产中使用了工作代码>,它做同样的事情,但没有解决它。我确实相信问题不在于环境本身,因为cron确实调用了我期望的文件,它只是对它没有任何作用。我猜它不知何故不理解它正在处理命令
类实例,并且没有实例化它。但确实很难排除故障。你会推荐什么来监督正确的cron执行,并在出现问题时发出警告?我刚刚添加了一个额外的示例,其中通过cron_监管器调用了一个脚本,该脚本基本上不会记录在其handle
方法中发生的任何事情。我刚才详细介绍了其他答案。我使用Python2.7,这就是为什么我也使用subprocess.Popen()
,因为subprocess.run()
在2.7中不可用,如果我理解正确的话。我还阅读了您关于从python运行python的建议,如果我理解正确,我宁愿调用我的cron_supervisor.py,它不会启动子进程,而是调用其他python模块来完成需要完成的工作。这实际上是有道理的。。。我们这样做的原因是,这些脚本最初是cron编写的,然后在上面创建了主管。我要试试看!基本上,每个进程只能调用一次logging.basicConfig
,这可能是坚持使用子进程的原因;或者切换到更详细的日志控制<代码>子流程。check_call()
在Python 2.7中肯定是可用的;您当前的代码基本上与之重复,只是您错误地将单个字符串作为列表传递给了Popen
。使用shell=True
时,第一个参数应该是一个字符串
0 * * * * . /home/p1/.virtualenvs/prod/bin/activate; python /home/p1/p1/manage.py cron_supervisor --command="python /home/p1/p1/manage.py envoyer_argent"