Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/361.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 当从cron作业运行时,脚本的行为不同;当使用cron监控器时,使用django manage.py从命令行运行时,脚本的行为不同_Python_Django_Cron - Fatal编程技术网

Python 当从cron作业运行时,脚本的行为不同;当使用cron监控器时,使用django manage.py从命令行运行时,脚本的行为不同

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 在通过另一个脚本运行脚本时,是否需要执行一些特殊的操作才能正确

我知道cron运行在不同于命令行的环境中,但我在任何地方都使用绝对路径,我不明白为什么我的脚本行为不同。我相信这与我的cron_主管有关,他在一个子流程中运行django“manage.py”

克朗:

这将调用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_主管调用脚本时,脚本未正确执行的示例:

# -*- 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"