Reputation: 23
I have a blocking scheduler launching 3 jobs, nothing fancy. The problem is that when I try to exit from any of the jobs earlier, using exit(3)
for instance, it throws an exception and the whole stack. I looked for a way to catch this exception and not display the whole error/stack, but without luck. I tried adding a listener like below, but it still prints the whole stack. Which is the proper way to exit from a blockingscheduler's job ahead of it's normal termination?
def make_db_snapshot(dataset, args):
databases = ''
print_header()
snapshots_no = count_snapshots(dataset)
....
def scheduler_listener(event):
log.debug('event code: {}'.format(event.__dict__))
if event.code == 8192:
log.error('The job crashed')
log.debug('event code: {}'.format(event.code))
scheduler = BlockingScheduler()
scheduler.configure(executors=executors)
scheduler.add_listener(scheduler_listener, EVENT_JOB_EXECUTED |
EVENT_JOB_ERROR)
# snapshot
scheduler.add_job(make_db_snapshot, trigger='cron',
name='DB snapshot', minute=SNAPSHOT['minute'],
hour=SNAPSHOT['hour'], args=[ZFS['dataset'], ARGS])
try:
scheduler.start()
except (KeyboardInterrupt, SystemExit):
exit(0)
This is where I need to exit:
def print_header():
if not proc_is_running(DB['process_name']):
log.error('DB process <{0}> is NOT running, exiting job'.
format(DB['process_name']))
exit(5)
log.info('{:=^70}'.format('='))
....
And this is what I get:
INFO: Running job "DB snapshot (trigger: cron[hour='*', minute='*/1'], next
run at: 2017-10-20 13:53:00 CEST)" (scheduled at 2017-10-20 13:52:00+02:00)
DEBUG: checking if process mysqld is running
ERROR: DB process <mysqld> is NOT running, exiting job
ERROR: Job "DB snapshot (trigger: cron[hour='*', minute='*/1'], next run at:
2017-10-20 13:53:00 CEST)" raised an exception
Traceback (most recent call last):
File "/usr/local/lib/python3.5/dist-
packages/apscheduler/executors/base.py", line 125, in run_job
retval = job.func(*job.args, **job.kwargs)
File "./db-backup", line 198, in make_db_snapshot
print_header()
File "./db-backup", line 289, in print_header
exit(5)
File "/usr/lib/python3.5/_sitebuiltins.py", line 26, in __call__
raise SystemExit(code)
SystemExit: 5
DEBUG: event code: {'traceback': ' File "/usr/local/lib/python3.5/dist-
packages/apscheduler/executors/base.py", line 125, in run_job\n retval =
job.func(*job.args, **job.kwargs)\n File "./db-backup", line 198, in
make_db_snapshot\n print_header()\n File "./db-backup", line 289, in
print_header\n exit(5)\n File "/usr/lib/python3.5/_sitebuiltins.py",
line 26, in __call__\n raise SystemExit(code)\n', 'exception':
SystemExit(5,), 'retval': None, 'job_id':
'd0ba1527e68a460da44b4b4dde618552', 'code': 8192, 'scheduled_run_time':
datetime.datetime(2017, 10, 20, 13, 52, tzinfo=<DstTzInfo 'Europe/Oslo'
CEST+2:00:00 DST>), 'jobstore': 'default', 'alias': None}
ERROR: The job crashed
DEBUG: event code: 8192
Upvotes: 2
Views: 4102
Reputation: 2053
Don't ever, ever use sys.exit
within a job. Raise exceptions instead. If you don't want APScheduler to handle exceptions, handle them yourself before APScheduler.
I assume you are using a ThreadPool executor (which is the default). Within a thread, sys.exit
raises a SystemExit
error. This is what APScheduler caught according to your logs, this is 100% normal. APScheduler will always try to catch and log any exception coming from your job (to avoid loosing the underlying thread/process). Even a sys.exit(0)
is abnormal in a job: APScheduler expects you to end the job by letting the job's function return without throwing any exception.
In your case, you may find handy to catch exceptions by yourself, before APScheduler can:
class StopJob(BaseException):
pass
def print_header():
if not proc_is_running(DB['process_name']):
raise StopJob('DB process <{0}> is NOT running.'.format(DB['process_name']))
log.info('{:=^70}'.format('='))
def make_db_snapshot(dataset, args):
try:
do_stuff(dataset, args)
print_headers()
do_other_stuff()
except StopJob:
logger.exception('Stopping job.')
# APScheduler won't notice the exception because it is handled, but it will be logged anyways.
However, I don't think there's anything wrong letting APScheduler handling exceptions for you. Just raise a DBConnectionError
instead of sys.exit
and let APScheduler display the error. What's wrong with this after all?
Upvotes: 1