我知道crons在与命令行不同的环境中运行,但是我到处都使用绝对路径,而且我不明白为什么我的脚本的行为会有所不同。我相信这与我的cron_supervisor有某种关系,该cron_supervisor在子进程中运行django“ manage.py”。
Cron:
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
中,但它们没有存储在那里,我不知道为什么。 (但这与我的主要问题无关,只是对调试没有帮助)
答案 0 :(得分:1)
您的cron
工作不能只在virtualenv中运行Python解释器;这是完全不够的。您需要像在交互式环境中一样activate
进行环境操作。
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作业在不同的备用环境下运行,例如外壳程序的PATH
,各种库路径,环境变量等可能不同或完全丢失;当然,没有交互式功能可用。
希望您的virtualenv可以处理系统变量;如果正确完成,则激活它会设置脚本所需的所有变量(PATH
,PYTHONPATH
等)。仍然可能有诸如语言环境设置之类的东西,只有当您以交互方式登录时,shell才会设置它们。但同样,没有细节,我们只是希望这对您来说不是问题。
有人建议使用绝对路径的原因是,不管您的工作目录是什么,它都将起作用。但是正确编写的脚本应该可以在任何目录下正常工作。如果重要,则cron作业将在所有者的主目录中启动。如果您要指向从那里开始的相对路径,那么在cron作业内部和外部都可以正常工作。
顺便说一句,如果subprocess.Popen()
模块中的更高层包装器之一满足您的要求,则可能不应该使用subprocess
。除非与旧版Python的兼容性很重要,否则您应该使用subprocess.run()
...,尽管将Python作为Python的子进程运行也常常是无用的omplication。另请参阅我对this related question.