Closes #20916: Record a stack trace in the job log for unhandled exceptions

This commit is contained in:
Jeremy Stretch
2026-03-04 13:39:08 -05:00
parent 6eafffb497
commit 3c6596de8f
3 changed files with 19 additions and 0 deletions
+8
View File
@@ -1,4 +1,6 @@
import django_tables2 as tables
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from core.constants import JOB_LOG_ENTRY_LEVELS
@@ -82,3 +84,9 @@ class JobLogEntryTable(BaseTable):
class Meta(BaseTable.Meta):
empty_text = _('No log entries')
fields = ('timestamp', 'level', 'message')
def render_message(self, record, value):
if record.get('level') == 'error' and '\n' in value:
value = conditional_escape(value)
return mark_safe(f'<pre class="p-0">{value}</pre>')
return value
+7
View File
@@ -1,4 +1,5 @@
import logging
import traceback
from abc import ABC, abstractmethod
from datetime import timedelta
@@ -107,6 +108,12 @@ class JobRunner(ABC):
job.terminate(status=JobStatusChoices.STATUS_FAILED)
except Exception as e:
tb_record = logging.makeLogRecord({
'levelno': logging.ERROR,
'levelname': 'ERROR',
'msg': traceback.format_exc(),
})
job.log(tb_record)
job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=repr(e))
if type(e) is JobTimeoutException:
logger.error(e)
+4
View File
@@ -83,6 +83,10 @@ class JobRunnerTest(JobRunnerTestCase):
self.assertEqual(job.status, JobStatusChoices.STATUS_ERRORED)
self.assertEqual(job.error, repr(ErroredJobRunner.EXP))
self.assertEqual(len(job.log_entries), 1)
self.assertEqual(job.log_entries[0]['level'], 'error')
self.assertIn('Traceback', job.log_entries[0]['message'])
self.assertIn('Test error', job.log_entries[0]['message'])
class EnqueueTest(JobRunnerTestCase):