Преглед изворни кода

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

Jeremy Stretch пре 2 дана
родитељ
комит
3c6596de8f
3 измењених фајлова са 19 додато и 0 уклоњено
  1. 8 0
      netbox/core/tables/jobs.py
  2. 7 0
      netbox/netbox/jobs.py
  3. 4 0
      netbox/netbox/tests/test_jobs.py

+ 8 - 0
netbox/core/tables/jobs.py

@@ -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 - 0
netbox/netbox/jobs.py

@@ -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 - 0
netbox/netbox/tests/test_jobs.py

@@ -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):