简体   繁体   English

当使用 cron 管理器时,脚本在从 cron 作业和使用 django manage.py 的命令行运行时表现不同

[英]Script behaves differently when run from cron job and from command line using django manage.py when using a cron supervisor

I know crons run in a different environment than command lines, but I'm using absolute paths everywhere and I don't understand why my script behaves differently.我知道 cron 在与命令行不同的环境中运行,但我在任何地方都使用绝对路径,我不明白为什么我的脚本表现不同。 I believe it is somehow related to my cron_supervisor which runs the django "manage.py" within a sub process.我相信它与我的 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"

This will call the cron_supervisor, and it's call the script, but the script won't be executed as it would if I would run:这将调用 cron_supervisor,它调用脚本,但脚本不会像我运行时那样执行:

/home/p1/.virtualenvs/prod/bin/python /home/p1/p1/manage.py envoyer_argent

Is there something particular to be done for the script to be called properly when running it through another script?在通过另一个脚本运行脚本时,是否需要做一些特别的事情才能正确调用脚本?

Here is the supervisor, which basically is for error handling and making sure we get warned if something goes wrong within the cron scripts themselves.这是主管,它基本上用于错误处理,并确保我们在 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)

Example of script not executed properly when being called by the cron, through the cron_supervisor:通过 cron_supervisor 被 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")

Additionally, I have another issue with the logging which I don't quite understand either, I specify to store logs within cron-supervisor.log but they don't get stored there, I couldn't figure out why.此外,我还有另一个我不太明白的日志问题,我指定将日志存储在cron-supervisor.log但它们没有存储在那里,我不知道为什么。 (but that's not related to my main issue, just doesn't help with debug) (但这与我的主要问题无关,只是对调试没有帮助)

Your cron job can't just run the Python interpreter in the virtualenv;您的cron作业不能只在 virtualenv 中运行 Python 解释器; this is completely insufficient.这是完全不够的。 You need to activate the env just like in an interactive environment.您需要像在交互式环境中一样activate env。

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; Cron 作业不仅(或特别)需要绝对路径; the main differences compared to interactive shells is that cron jobs run with a different and more spare environment, where eg the shell's PATH , various library paths, environment variables etc can be different or missing altogether;与交互式 shell 相比,主要区别在于 cron 作业在不同且更空闲的环境中运行,例如 shell 的PATH 、各种库路径、环境变量等可能不同或完全缺失; and of course, no interactive facilities are available.当然,没有互动设施可用。

The system variables will hopefully be taken care of by your virtualenv;系统变量有望由您的 virtualenv 处理; if it's correctly done, activating it will set up all the variables ( PATH , PYTHONPATH , etc) your script needs.如果正确完成,激活它将设置脚本所需的所有变量( PATHPYTHONPATH等)。 There could still be things like locale settings which are set up by your shell only when you log in interactively;只有当您以交互方式登录时,您的 shell 才会设置诸如区域设置之类的东西; 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.如果重要,cron 作业将在所有者的主目录中启动。 If you wanted to point to a relative path from there, this will work fine inside a cron job just as it does outside.如果您想从那里指向相对路径,这将在 cron 作业中正常工作,就像在外部一样。

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. subprocess.Popen()如果来自subprocess模块的更高级别包装器之一执行您想要的操作,则您可能不应该使用subprocess.Popen() 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.除非与旧版 Python 版本的兼容性很重要,否则您可能应该使用subprocess.run() ……尽管将 Python 作为 Python 的子进程运行通常也是无用的 oomplication。 See also my answer to this related question.另请参阅我对这个相关问题的回答。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在docker容器中为django运行cron作业时manage.py脚本出错 - Error in manage.py script when running cron job for django inside docker container 从Shell脚本运行时Django manage.py错误 - Django manage.py error when run from a shell script 产品服务器上的cron作业django-找不到manage.py - cron job django on prod server - manage.py not found 从IDLE或命令行运行时,Web.py的行为有所不同 - Web.py behaves differently when run from IDLE or command line 从命令行运行Python脚本时,MySQLdb模块可以工作,但不能作为cron作业 - MySQLdb module works when running Python script from the command line, but not as a cron job 为什么在__name__ ==“ __main__”下使用manage.py执行脚本时会运行两次 - Why does manage.py execution script run twice when using it under if __name__ == “__main__” 为什么 python 脚本在 pycharm 中运行和在命令提示符下运行时的行为不同? - Why does a python script behaves differently when it is run in pycharm and when it is run from a command prompt? 当我在models.py中的python django中运行此命令python manage.py makemigrations博客时出错 - Error when I run this command python manage.py makemigrations blog in python django in models.py Python-运行cron作业时出现“ IOError:[Errno 13]权限被拒绝”,但从命令行运行时却没有 - Python - “IOError: [Errno 13] Permission denied” when running cron job but not when running from command line 从Mac Cron作业而不是从Mac Terminal运行时,Python脚本错误出 - Python script errors out when run from mac cron job but not from mac terminal
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM