diff --git a/tests/etl/taskcluster_pulse/test_handler.py b/tests/etl/taskcluster_pulse/test_handler.py index ecfe4e0ce17..018dc75ea2d 100644 --- a/tests/etl/taskcluster_pulse/test_handler.py +++ b/tests/etl/taskcluster_pulse/test_handler.py @@ -1,6 +1,48 @@ import pytest -from treeherder.etl.taskcluster_pulse.handler import handle_message, handle_task_defined +from treeherder.etl.taskcluster_pulse.handler import ( + create_log_reference, + handle_message, + handle_task_defined, +) + +ROOT_URL = "https://firefox-ci-tc.services.mozilla.com" +TASK_ID = "AJBb7wqZT6K9kz4niYAatg" + + +def test_create_log_reference_emits_live_backing_log_by_default(): + logs = create_log_reference(ROOT_URL, TASK_ID, 0) + assert len(logs) == 1 + assert logs[0]["name"] == "live_backing_log" + assert logs[0]["url"].endswith(f"task/{TASK_ID}/runs/0/artifacts/public/logs/live_backing.log") + + +def test_create_log_reference_only_live_backing_log_when_no_raw_log(): + artifacts = [ + {"name": "public/logs/live_backing.log"}, + {"name": "public/test_info/something.txt"}, + ] + logs = create_log_reference(ROOT_URL, TASK_ID, 0, artifacts=artifacts) + assert len(logs) == 1 + assert logs[0]["url"].endswith("artifacts/public/logs/live_backing.log") + + +def test_create_log_reference_appends_raw_log_when_present(): + artifacts = [ + {"name": "public/logs/live_backing.log"}, + {"name": "public/test_info/xpcshell_raw.log"}, + {"name": "public/test_info/mochitest_raw.log"}, + ] + logs = create_log_reference(ROOT_URL, TASK_ID, 0, artifacts=artifacts) + assert [log["name"] for log in logs] == [ + "live_backing_log", + "structured_log", + "structured_log", + ] + urls = [log["url"] for log in logs] + assert urls[0].endswith("artifacts/public/logs/live_backing.log") + assert urls[1].endswith("artifacts/public/test_info/xpcshell_raw.log") + assert urls[2].endswith("artifacts/public/test_info/mochitest_raw.log") @pytest.mark.asyncio diff --git a/tests/ui/mock/structured_log.json b/tests/ui/mock/structured_log.json new file mode 100644 index 00000000000..72758a786eb --- /dev/null +++ b/tests/ui/mock/structured_log.json @@ -0,0 +1,782 @@ +[ + { + "action": "log", + "time": 1776950625757, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "Using harness timeout of 30s (base=30s, factor=1.0)", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "These variables are available in the mozinfo environment and can be used to skip tests conditionally:", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " appname: firefox", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " artifact: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " asan: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " automation: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " bin_suffix: ", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " bits: 64", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " buildapp: browser", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " buildtype: opt", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " buildtype_guess: opt", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " cc_type: None", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " ccov: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " condprof: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625779, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " crashreporter: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " datareporting: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " dbus_enabled: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " debug: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " devedition: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " e10s: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " early_beta_or_earlier: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " fission: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " healthreport: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " inc_origin_init: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " is_ubuntu: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " isolated_process: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " mingw: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " mozconfig: /Users/benoitgoupilleau/Files/mozilla/firefox/mozconfig", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " msix: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " nightly_build: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " normandy: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " official: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " opt: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " os: mac", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " pgo: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625780, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " platform_guess: macosx64", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " processor: aarch64", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " release_or_beta: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " require_signing: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " sessionHistoryInParent: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " socketprocess_networking: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " stylo: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " sync: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " telemetry: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " tests_enabled: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " toolkit: cocoa", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " topobjdir: /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " topsrcdir: /Users/benoitgoupilleau/Files/mozilla/firefox", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " tsan: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " ubsan: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " updater: True", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625781, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": " verify: False", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625782, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "Found node at /Users/benoitgoupilleau/.mozbuild/node/bin/node", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625782, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "mozserve | Found moz-http2 server path: /Users/benoitgoupilleau/Files/mozilla/firefox/testing/xpcshell/moz-http2/moz-http2.js", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950625782, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "Found node at /Users/benoitgoupilleau/.mozbuild/node/bin/node", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626677, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "mozserve | moz-http2 server msg: HTTP2 server listening on ports 62584,62585\n", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626680, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "moz-http2 server started on ports 62584, 62585", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626681, + "thread": "Thread-7 (read_streams)", + "pid": 12742, + "source": "mach-test", + "message": "node moz-http2 [stderr] (node:12779) [DEP0060] DeprecationWarning: The `util._extend` API is deprecated. Please use Object.assign() instead.\n", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626681, + "thread": "Thread-7 (read_streams)", + "pid": 12742, + "source": "mach-test", + "message": "node moz-http2 [stderr] (Use `node --trace-deprecation ...` to show where the warning was created)\n", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626681, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "mozserve | Found Http3Server path: /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/bin/http3server", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626682, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "mozserve | cert db path: /Users/benoitgoupilleau/Files/mozilla/firefox/netwerk/test/http3serverDB", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626850, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "mozserve | http3 server msg: HTTP3 server listening on ports 55492, 53492, 49293, 60978, 53983 and 61764. EchConfig is @AE3+DQBJBwAgACBFkQGZpKTJgJaxYmDd1Q8RDOSkk4eAIe/iGDbfx581NwAQAAEAAQABAAMAAgABAAIAA0AOcHVibGljLmV4YW1wbGUAAA==@\n", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626852, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "Running tests sequentially.", + "level": "INFO" + }, + { + "action": "suite_start", + "time": 1776950626853, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "name": "xpcshell", + "tests": { + "xpcom/tests/unit/xpcshell.toml": [ + "xpcom/tests/unit/test_bug476919.js" + ] + } + }, + { + "action": "group_start", + "time": 1776950626853, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "name": "sequential" + }, + { + "action": "test_start", + "time": 1776950626853, + "thread": "Thread-10", + "pid": 12742, + "source": "mach-test", + "group": "xpcom/tests/unit/xpcshell.toml", + "test": "xpcom/tests/unit/test_bug476919.js" + }, + { + "action": "log", + "time": 1776950626855, + "thread": "Thread-10", + "pid": 12742, + "source": "mach-test", + "message": "profile dir is /var/folders/_q/_459jbz15zq9qf7v979px5v00000gn/T/firefox/xpcshellprofile", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626856, + "thread": "Thread-10", + "pid": 12742, + "source": "mach-test", + "message": "xpcom/tests/unit/test_bug476919.js | full command: ['/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/MacOS/xpcshell', '-g', '/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/Resources', '-a', '/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/Resources', '-m', '-e', 'const _HEAD_JS_PATH = \"/Users/benoitgoupilleau/Files/mozilla/firefox/testing/xpcshell/head.js\";', '-e', 'const _MOZINFO_JS_PATH = \"/var/folders/_q/_459jbz15zq9qf7v979px5v00000gn/T/firefox/xpcshellprofile/mozinfo.json\";', '-e', 'const _PREFS_FILE = \"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/temp/user.js\";', '-e', 'const _TESTING_MODULES_DIR = \"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/modules/\";', '-f', '/Users/benoitgoupilleau/Files/mozilla/firefox/testing/xpcshell/head.js', '-e', 'const _HEAD_FILES = [\"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit/head_xpcom.js\"];', '-e', 'const _JSDEBUGGER_PORT = 0;', '-e', 'const _TEST_FILE = [\"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit/test_bug476919.js\"];', '-e', 'const _TEST_NAME = \"xpcom/tests/unit/test_bug476919.js\";', '-e', 'const _EXPECTED = \"pass\";', '-e', '_execute_test(); quit(0);']", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626856, + "thread": "Thread-10", + "pid": 12742, + "source": "mach-test", + "message": "xpcom/tests/unit/test_bug476919.js | current directory: '/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit'", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626856, + "thread": "Thread-10", + "pid": 12742, + "source": "mach-test", + "message": "xpcom/tests/unit/test_bug476919.js | environment: ['MOZ_HTTP3_CERT_DB_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/netwerk/test/http3serverDB', 'MOZ_DISABLE_CONTENT_SANDBOX=1', 'MOZ_DISABLE_SOCKET_PROCESS=1', 'MOZ_DEVELOPER_REPO_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox', 'MOZHTTP3_PORT_MASQUE=61764', 'MOZ_DISABLE_NONLOCAL_CONNECTIONS=1', 'MOZHTTP3_PORT_FAILED=53492', 'MOZHTTP2_PORT=62584', 'MOZ_CRASHREPORTER_NO_REPORT=1', 'XPCSHELL_TEST_PROFILE_DIR=/var/folders/_q/_459jbz15zq9qf7v979px5v00000gn/T/firefox/xpcshellprofile', 'MOZ_HTTP3_SERVER_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/bin/http3server', 'XPCSHELL_TEST_TEMP_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/temp/xpc-other-no90eapi', 'MOZNODE_EXEC_PORT=62585', 'DYLD_LIBRARY_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/MacOS', 'MOZHTTP3_PORT_ECH=49293', 'MOZHTTP3_PORT_PROXY=60978', 'MOZ_CRASHREPORTER=1', 'MOZ_DEVELOPER_OBJ_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0', 'MOZHTTP3_PORT_NO_RESPONSE=53983', 'MOZHTTP3_PORT=55492', 'MOZ_TEST_TIMEOUT_INTERVAL=30', 'XPCOM_DEBUG_BREAK=stack-and-abort', 'MOZHTTP3_ECH=AE3+DQBJBwAgACBFkQGZpKTJgJaxYmDd1Q8RDOSkk4eAIe/iGDbfx581NwAQAAEAAQABAAMAAgABAAIAA0AOcHVibGljLmV4YW1wbGUAAA==']", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950626857, + "thread": "Thread-10", + "pid": 12742, + "source": "mach-test", + "message": "xpcom/tests/unit/test_bug476919.js | as shell command: (cd /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit; MOZ_HTTP3_CERT_DB_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/netwerk/test/http3serverDB MOZ_DISABLE_CONTENT_SANDBOX=1 MOZ_DISABLE_SOCKET_PROCESS=1 MOZ_DEVELOPER_REPO_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox MOZHTTP3_PORT_MASQUE=61764 MOZ_DISABLE_NONLOCAL_CONNECTIONS=1 MOZHTTP3_PORT_FAILED=53492 MOZHTTP2_PORT=62584 MOZ_CRASHREPORTER_NO_REPORT=1 XPCSHELL_TEST_PROFILE_DIR=/var/folders/_q/_459jbz15zq9qf7v979px5v00000gn/T/firefox/xpcshellprofile MOZ_HTTP3_SERVER_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/bin/http3server XPCSHELL_TEST_TEMP_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/temp/xpc-other-no90eapi MOZNODE_EXEC_PORT=62585 DYLD_LIBRARY_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/MacOS MOZHTTP3_PORT_ECH=49293 MOZHTTP3_PORT_PROXY=60978 MOZ_CRASHREPORTER=1 MOZ_DEVELOPER_OBJ_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0 MOZHTTP3_PORT_NO_RESPONSE=53983 MOZHTTP3_PORT=55492 MOZ_TEST_TIMEOUT_INTERVAL=30 XPCOM_DEBUG_BREAK=stack-and-abort MOZHTTP3_ECH=AE3+DQBJBwAgACBFkQGZpKTJgJaxYmDd1Q8RDOSkk4eAIe/iGDbfx581NwAQAAEAAQABAAMAAgABAAIAA0AOcHVibGljLmV4YW1wbGUAAA== /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/MacOS/xpcshell -g /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/Resources -a /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/Resources -m -e 'const _HEAD_JS_PATH = \"/Users/benoitgoupilleau/Files/mozilla/firefox/testing/xpcshell/head.js\";' -e 'const _MOZINFO_JS_PATH = \"/var/folders/_q/_459jbz15zq9qf7v979px5v00000gn/T/firefox/xpcshellprofile/mozinfo.json\";' -e 'const _PREFS_FILE = \"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/temp/user.js\";' -e 'const _TESTING_MODULES_DIR = \"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/modules/\";' -f /Users/benoitgoupilleau/Files/mozilla/firefox/testing/xpcshell/head.js -e 'const _HEAD_FILES = [\"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit/head_xpcom.js\"];' -e 'const _JSDEBUGGER_PORT = 0;' -e 'const _TEST_FILE = [\"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit/test_bug476919.js\"];' -e 'const _TEST_NAME = \"xpcom/tests/unit/test_bug476919.js\";' -e 'const _EXPECTED = \"pass\";' -e '_execute_test(); quit(0);')", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950630163, + "thread": "Thread-10", + "pid": null, + "source": "xpcshell/head.js", + "message": "\"CONSOLE_MESSAGE: (info) No chrome package registered for chrome://branding/locale/brand.properties\"", + "exc_info": false, + "level": "INFO" + }, + { + "action": "log", + "time": 1776950630179, + "thread": "Thread-10", + "pid": null, + "source": "xpcshell/head.js", + "message": "(xpcshell/head.js) | test MAIN run_test pending (1)", + "exc_info": false, + "level": "INFO" + }, + { + "action": "test_status", + "time": 1776950630182, + "thread": "Thread-10", + "pid": null, + "source": "xpcshell/head.js", + "test": "xpcom/tests/unit/test_bug476919.js", + "subtest": "run_test", + "status": "PASS", + "message": "[run_test : 24] true == true" + }, + { + "action": "log", + "time": 1776950630182, + "thread": "Thread-10", + "pid": null, + "source": "xpcshell/head.js", + "message": "(xpcshell/head.js) | test MAIN run_test finished (1)", + "exc_info": false, + "level": "INFO" + }, + { + "action": "log", + "time": 1776950630182, + "thread": "Thread-10", + "pid": null, + "source": "xpcshell/head.js", + "message": "exiting test", + "exc_info": false, + "level": "INFO" + }, + { + "action": "test_end", + "time": 1776950630187, + "thread": "Thread-10", + "pid": 12742, + "source": "mach-test", + "message": "xpcshell return code: 0", + "group": "xpcom/tests/unit/xpcshell.toml", + "test": "xpcom/tests/unit/test_bug476919.js", + "status": "PASS" + }, + { + "action": "group_end", + "time": 1776950630189, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "name": "sequential" + }, + { + "action": "log", + "time": 1776950630190, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "INFO | Result summary:", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950630190, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "INFO | Passed: 1", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950630190, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "INFO | Failed: 0", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950630190, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "INFO | Todo: 0", + "level": "INFO" + }, + { + "action": "log", + "time": 1776950630190, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "INFO | Retried: 0", + "level": "INFO" + }, + { + "action": "suite_end", + "time": 1776950630190, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test" + }, + { + "action": "log", + "time": 1776950630192, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test", + "message": "http3Server server shutting down ...", + "level": "INFO" + }, + { + "action": "shutdown", + "time": 1776950630298, + "thread": "MainThread", + "pid": 12742, + "source": "mach-test" + } +] \ No newline at end of file diff --git a/tests/ui/shared/tabs/failureSummary/StructuredErrorsList.test.jsx b/tests/ui/shared/tabs/failureSummary/StructuredErrorsList.test.jsx new file mode 100644 index 00000000000..3c546329841 --- /dev/null +++ b/tests/ui/shared/tabs/failureSummary/StructuredErrorsList.test.jsx @@ -0,0 +1,33 @@ +import fs from 'fs'; +import path from 'path'; + +import { render, screen } from '@testing-library/react'; + +import StructuredErrorsList from '../../../../../ui/shared/tabs/failureSummary/StructuredErrorsList'; + +import structuredLogs from '../mock/structured_log.json'; + +describe('StructuredErrorsList', () => { + test('renders one row per parsed JSONL entry', () => { + const { container } = render( + , + ); + const rows = container.querySelectorAll( + '[data-testid="structured-log-error"]', + ); + expect(rows).toHaveLength(structuredLogs.length); + }); + + test('renders the message of the first entry', () => { + render(); + const firstMessage = structuredLogs[0]?.message; + expect(screen.getByText(firstMessage)).toBeTruthy(); + }); + + test('renders nothing extra when given an empty list', () => { + const { container } = render(); + expect( + container.querySelectorAll('[data-testid="structured-log-error"]'), + ).toHaveLength(0); + }); +}); diff --git a/treeherder/etl/jobs.py b/treeherder/etl/jobs.py index 785d57cd856..caacdedcb03 100644 --- a/treeherder/etl/jobs.py +++ b/treeherder/etl/jobs.py @@ -344,7 +344,7 @@ def _schedule_log_parsing(job, job_logs, result, repository): # importing here to avoid an import loop from treeherder.log_parser.tasks import parse_logs - task_types = {"errorsummary_json", "live_backing_log"} + task_types = {"errorsummary_json", "live_backing_log", "structured_log"} sheriffed_repos = { "autoland", "mozilla-central", diff --git a/treeherder/etl/taskcluster_pulse/handler.py b/treeherder/etl/taskcluster_pulse/handler.py index 1bbc13da36c..f01321c41e3 100644 --- a/treeherder/etl/taskcluster_pulse/handler.py +++ b/treeherder/etl/taskcluster_pulse/handler.py @@ -56,16 +56,31 @@ def result_from_run(job_run): return "unknown" -# Creates a log entry for Treeherder to retrieve and parse. This log is -# displayed on the Treeherder Log Viewer once parsed. -def create_log_reference(root_url, task_id, run_id): - log_url = taskcluster_urls.api( - root_url, "queue", "v1", "task/{taskId}/runs/{runId}/artifacts/public/logs/live_backing.log" - ).format(taskId=task_id, runId=run_id) - return { - "name": "live_backing_log", - "url": log_url, - } +# Creates the log entries for Treeherder to retrieve and parse. These logs +# are displayed on the Treeherder Log Viewer once parsed. +# `public/logs/live_backing.log` is always included. Any artifact whose name +# ends with `_raw.log` is appended as an additional reference. All entries +# share the `live_backing_log` JobLog name so the existing parser dispatch +# (jobs.py:_schedule_log_parsing, log_parser/tasks.py:parser_tasks) handles +# them; JobLog's `(job, name, url)` unique constraint allows the duplicates. +def create_log_reference(root_url, task_id, run_id, artifacts=None): + def _ref(name, artifact_path): + return { + "name": name, + "url": taskcluster_urls.api( + root_url, + "queue", + "v1", + f"task/{{taskId}}/runs/{{runId}}/artifacts/{artifact_path}", + ).format(taskId=task_id, runId=run_id), + } + + logs = [_ref("live_backing_log", "public/logs/live_backing.log")] + for artifact in artifacts or []: + name = artifact.get("name", "") + if name.endswith("_raw.log"): + logs.append(_ref("structured_log", name)) + return logs # Filters the task routes for the treeherder specific route. Once found, @@ -371,11 +386,23 @@ async def handle_task_completed(push_info, task, message, session): job["timeStarted"] = job_run["started"] job["timeCompleted"] = job_run["resolved"] - job["logs"] = [ - create_log_reference(message["root_url"], payload["status"]["taskId"], job_run["runId"]), - ] + + task_id = payload["status"]["taskId"] + run_id = job_run["runId"] + try: + artifacts = await fetch_artifacts(message["root_url"], task_id, run_id, session) + except Exception: + logger.debug("Artifacts could not be found for task: %s run: %s", task_id, run_id) + artifacts = [] + + job["logs"] = create_log_reference(message["root_url"], task_id, run_id, artifacts=artifacts) job = await add_artifact_uploaded_links( - message["root_url"], payload["status"]["taskId"], payload["runId"], job, session + message["root_url"], + task_id, + payload["runId"], + job, + session, + artifacts=artifacts, ) return job @@ -434,13 +461,13 @@ async def fetch_artifacts(root_url, task_id, run_id, session): # fetch them in order to determine if there is an error_summary log; # TODO refactor this when there is a way to only retrieve the error_summary # artifact: https://bugzilla.mozilla.org/show_bug.cgi?id=1629716 -async def add_artifact_uploaded_links(root_url, task_id, run_id, job, session): - artifacts = [] - try: - artifacts = await fetch_artifacts(root_url, task_id, run_id, session) - except Exception: - logger.debug("Artifacts could not be found for task: %s run: %s", task_id, run_id) - return job +async def add_artifact_uploaded_links(root_url, task_id, run_id, job, session, artifacts=None): + if artifacts is None: + try: + artifacts = await fetch_artifacts(root_url, task_id, run_id, session) + except Exception: + logger.debug("Artifacts could not be found for task: %s run: %s", task_id, run_id) + return job seen = {} links = [] diff --git a/treeherder/log_parser/tasks.py b/treeherder/log_parser/tasks.py index 827a2bb2e50..bfeaf0c8408 100644 --- a/treeherder/log_parser/tasks.py +++ b/treeherder/log_parser/tasks.py @@ -6,11 +6,13 @@ from requests.exceptions import HTTPError from treeherder.etl.artifact import serialize_artifact_json_blobs, store_job_artifacts +from treeherder.etl.text import astral_filter from treeherder.log_parser.artifactbuildercollection import ( ArtifactBuilderCollection, LogSizeError, ) -from treeherder.model.models import Job, JobLog +from treeherder.model.models import Job, JobLog, StructuredLogError +from treeherder.utils.http import fetch_text from treeherder.workers.task import retryable_task from . import failureline, intermittents @@ -33,6 +35,7 @@ def parse_logs(job_id, job_log_ids, priority): parser_tasks = { "errorsummary_json": store_failure_lines, "live_backing_log": post_log_artifacts, + "structured_log": post_structured_log_artifacts, } # We don't want to stop parsing logs for most Exceptions however we still @@ -122,6 +125,79 @@ def post_log_artifacts(job_log): raise +def post_structured_log_artifacts(job_log): + """Download a structured (mozlog JSON-lines) log and store error entries.""" + logger.info("Downloading/parsing structured log for log %s", job_log.id) + + try: + log_text = fetch_text(job_log.url) + except HTTPError as e: + job_log.update_status(JobLog.FAILED) + if e.response is not None and e.response.status_code in (403, 404): + logger.warning("Unable to retrieve structured log for %s: %s", job_log.id, e) + return + logger.error("Failed to download structured log for %s: %s", job_log.id, e) + raise + except Exception as e: + job_log.update_status(JobLog.FAILED) + logger.error("Failed to download structured log for %s: %s", job_log.id, e) + raise + + if not log_text: + job_log.update_status(JobLog.PARSED) + return + + error_entries = [] + for raw_line in log_text.splitlines(): + try: + entry = json.loads(raw_line) + except (ValueError, TypeError): + continue + if not isinstance(entry, dict): + continue + level = (entry.get("level") or "").upper() + if level not in ("ERROR", "CRITICAL"): + continue + + time_value = entry.get("time") + if isinstance(time_value, float): + time_value = int(time_value) + elif not isinstance(time_value, int): + time_value = None + + pid_value = entry.get("pid") + if not isinstance(pid_value, int) or pid_value < 0: + pid_value = None + + error_entries.append( + StructuredLogError( + job_log=job_log, + action=str(entry.get("action") or "")[:32], + time=time_value, + thread=astral_filter(str(entry.get("thread") or ""))[:255], + pid=pid_value, + source=astral_filter(str(entry.get("source") or ""))[:255], + message=astral_filter(str(entry.get("message") or "")), + level=level[:16], + ) + ) + + try: + StructuredLogError.objects.filter(job_log=job_log).delete() + if error_entries: + StructuredLogError.objects.bulk_create(error_entries) + job_log.update_status(JobLog.PARSED) + logger.info( + "Stored structured log errors for %s %s %s", + job_log.job.repository.name, + job_log.job.id, + job_log.id, + ) + except Exception as e: + logger.error("Failed to store structured log errors for %s: %s", job_log.id, e) + raise + + def extract_text_log_artifacts(job_log): """Generate a set of artifacts by parsing from the raw text log.""" diff --git a/treeherder/model/migrations/0049_structuredlogerror.py b/treeherder/model/migrations/0049_structuredlogerror.py new file mode 100644 index 00000000000..dcb7c626930 --- /dev/null +++ b/treeherder/model/migrations/0049_structuredlogerror.py @@ -0,0 +1,37 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("model", "0048_alter_failureline_action"), + ] + + operations = [ + migrations.CreateModel( + name="StructuredLogError", + fields=[ + ("id", models.BigAutoField(primary_key=True, serialize=False)), + ("action", models.CharField(blank=True, max_length=32)), + ("time", models.BigIntegerField(blank=True, null=True)), + ("thread", models.CharField(blank=True, max_length=255)), + ("pid", models.PositiveIntegerField(blank=True, null=True)), + ("source", models.CharField(blank=True, max_length=255)), + ("message", models.TextField(blank=True)), + ("level", models.CharField(blank=True, max_length=16)), + ( + "job_log", + models.ForeignKey( + on_delete=models.deletion.CASCADE, + related_name="structured_log_error", + to="model.joblog", + ), + ), + ], + options={ + "db_table": "structured_log_error", + "indexes": [ + models.Index(fields=["job_log"], name="structured__job_log_idx"), + ], + }, + ), + ] diff --git a/treeherder/model/models.py b/treeherder/model/models.py index 3b2d04fc134..9eaa36c90e3 100644 --- a/treeherder/model/models.py +++ b/treeherder/model/models.py @@ -1324,6 +1324,29 @@ def get_failure_line(self): return None +class StructuredLogError(models.Model): + """A detected error entry from a structured (mozlog JSON-lines) log.""" + + id = models.BigAutoField(primary_key=True) + job_log = models.ForeignKey( + JobLog, on_delete=models.CASCADE, related_name="structured_log_error" + ) + action = models.CharField(max_length=32, blank=True) + time = models.BigIntegerField(null=True, blank=True) + thread = models.CharField(max_length=255, blank=True) + pid = models.PositiveIntegerField(null=True, blank=True) + source = models.CharField(max_length=255, blank=True) + message = models.TextField(blank=True) + level = models.CharField(max_length=16, blank=True) + + class Meta: + db_table = "structured_log_error" + indexes = [models.Index(fields=["job_log"])] + + def __str__(self): + return f"{self.id} {self.job_log_id}" + + class TextLogErrorMetadata(models.Model): """ Link matching TextLogError and FailureLine instances. diff --git a/treeherder/webapp/api/jobs.py b/treeherder/webapp/api/jobs.py index 73ad69d5790..77ea0a0aaf5 100644 --- a/treeherder/webapp/api/jobs.py +++ b/treeherder/webapp/api/jobs.py @@ -17,6 +17,7 @@ JobLog, OptionCollection, Repository, + StructuredLogError, TextLogError, ) from treeherder.webapp.api import pagination, serializers @@ -400,6 +401,21 @@ def bug_suggestions(self, request, project, pk=None): return Response(get_error_summary(job)) + @action(detail=True, methods=["get"]) + def structured_log_errors(self, request, project, pk=None): + """ + Gets parsed structured log errors for this job, if any have been ingested. + """ + try: + job = Job.objects.get(repository__name=project, id=pk) + except ObjectDoesNotExist: + return Response(f"No job with id: {pk}", status=HTTP_404_NOT_FOUND) + + errors = StructuredLogError.objects.filter(job_log__job=job).order_by("id") + return Response( + serializers.StructuredLogErrorSerializer(errors, many=True, read_only=True).data + ) + @action(detail=True, methods=["get"]) def similar_jobs(self, request, project, pk=None): """ diff --git a/treeherder/webapp/api/serializers.py b/treeherder/webapp/api/serializers.py index 1982362aed4..b2d721d6487 100644 --- a/treeherder/webapp/api/serializers.py +++ b/treeherder/webapp/api/serializers.py @@ -189,6 +189,12 @@ class Meta: fields = "__all__" +class StructuredLogErrorSerializer(serializers.ModelSerializer): + class Meta: + model = models.StructuredLogError + fields = ("id", "action", "time", "thread", "pid", "source", "message", "level") + + class BugJobMapSerializer(serializers.ModelSerializer): job_id = serializers.PrimaryKeyRelatedField(source="job", read_only=True) bug_id = serializers.IntegerField(source="bug.bugzilla_id", read_only=True) diff --git a/ui/models/structuredLogErrors.js b/ui/models/structuredLogErrors.js new file mode 100644 index 00000000000..650e00deff8 --- /dev/null +++ b/ui/models/structuredLogErrors.js @@ -0,0 +1,9 @@ +import { getProjectJobUrl } from '../helpers/location'; + +export default class StructuredLogErrorsModel { + static get(jobId) { + return fetch(getProjectJobUrl('/structured_log_errors/', jobId)).then( + (resp) => (resp.ok ? resp.json() : []), + ); + } +} diff --git a/ui/shared/tabs/failureSummary/FailureSummaryTab.jsx b/ui/shared/tabs/failureSummary/FailureSummaryTab.jsx index f6aeb2be243..11a4b572841 100644 --- a/ui/shared/tabs/failureSummary/FailureSummaryTab.jsx +++ b/ui/shared/tabs/failureSummary/FailureSummaryTab.jsx @@ -14,9 +14,11 @@ import { getReftestUrl } from '../../../helpers/url'; import BugFiler from '../../BugFiler'; import InternalIssueFiler from '../../InternalIssueFiler'; import BugSuggestionsModel from '../../../models/bugSuggestions'; +import StructuredLogErrorsModel from '../../../models/structuredLogErrors'; import ErrorsList from './ErrorsList'; import ListItem from './ListItem'; +import StructuredErrorsList from './StructuredErrorsList'; import SuggestionsListItem from './SuggestionsListItem'; class FailureSummaryTab extends React.Component { @@ -28,12 +30,813 @@ class FailureSummaryTab extends React.Component { isInternalIssueFilerOpen: false, suggestions: [], errors: [], + structuredErrors: [ + { + action: 'log', + time: 1776950625757, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: 'Using harness timeout of 30s (base=30s, factor=1.0)', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + 'These variables are available in the mozinfo environment and can be used to skip tests conditionally:', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' appname: firefox', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' artifact: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' asan: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' automation: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' bin_suffix: ', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' bits: 64', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' buildapp: browser', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' buildtype: opt', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' buildtype_guess: opt', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' cc_type: None', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' ccov: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' condprof: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625779, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' crashreporter: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' datareporting: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' dbus_enabled: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' debug: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' devedition: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' e10s: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' early_beta_or_earlier: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' fission: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' healthreport: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' inc_origin_init: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' is_ubuntu: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' isolated_process: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' mingw: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + ' mozconfig: /Users/benoitgoupilleau/Files/mozilla/firefox/mozconfig', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' msix: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' nightly_build: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' normandy: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' official: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' opt: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' os: mac', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' pgo: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625780, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' platform_guess: macosx64', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' processor: aarch64', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' release_or_beta: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' require_signing: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' sessionHistoryInParent: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' socketprocess_networking: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' stylo: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' sync: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' telemetry: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' tests_enabled: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' toolkit: cocoa', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + ' topobjdir: /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + ' topsrcdir: /Users/benoitgoupilleau/Files/mozilla/firefox', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' tsan: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' ubsan: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' updater: True', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625781, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: ' verify: False', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625782, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + 'Found node at /Users/benoitgoupilleau/.mozbuild/node/bin/node', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625782, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + 'mozserve | Found moz-http2 server path: /Users/benoitgoupilleau/Files/mozilla/firefox/testing/xpcshell/moz-http2/moz-http2.js', + level: 'INFO', + }, + { + action: 'log', + time: 1776950625782, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + 'Found node at /Users/benoitgoupilleau/.mozbuild/node/bin/node', + level: 'INFO', + }, + { + action: 'log', + time: 1776950626677, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + 'mozserve | moz-http2 server msg: HTTP2 server listening on ports 62584,62585\n', + level: 'INFO', + }, + { + action: 'log', + time: 1776950626680, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: 'moz-http2 server started on ports 62584, 62585', + level: 'INFO', + }, + { + action: 'log', + time: 1776950626681, + thread: 'Thread-7 (read_streams)', + pid: 12742, + source: 'mach-test', + message: + 'node moz-http2 [stderr] (node:12779) [DEP0060] DeprecationWarning: The `util._extend` API is deprecated. Please use Object.assign() instead.\n', + level: 'INFO', + }, + { + action: 'log', + time: 1776950626681, + thread: 'Thread-7 (read_streams)', + pid: 12742, + source: 'mach-test', + message: + 'node moz-http2 [stderr] (Use `node --trace-deprecation ...` to show where the warning was created)\n', + level: 'INFO', + }, + { + action: 'log', + time: 1776950626681, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + 'mozserve | Found Http3Server path: /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/bin/http3server', + level: 'INFO', + }, + { + action: 'log', + time: 1776950626682, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + 'mozserve | cert db path: /Users/benoitgoupilleau/Files/mozilla/firefox/netwerk/test/http3serverDB', + level: 'INFO', + }, + { + action: 'log', + time: 1776950626850, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: + 'mozserve | http3 server msg: HTTP3 server listening on ports 55492, 53492, 49293, 60978, 53983 and 61764. EchConfig is @AE3+DQBJBwAgACBFkQGZpKTJgJaxYmDd1Q8RDOSkk4eAIe/iGDbfx581NwAQAAEAAQABAAMAAgABAAIAA0AOcHVibGljLmV4YW1wbGUAAA==@\n', + level: 'INFO', + }, + { + action: 'log', + time: 1776950626852, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: 'Running tests sequentially.', + level: 'INFO', + }, + { + action: 'suite_start', + time: 1776950626853, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + name: 'xpcshell', + tests: { + 'xpcom/tests/unit/xpcshell.toml': [ + 'xpcom/tests/unit/test_bug476919.js', + ], + }, + }, + { + action: 'group_start', + time: 1776950626853, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + name: 'sequential', + }, + { + action: 'test_start', + time: 1776950626853, + thread: 'Thread-10', + pid: 12742, + source: 'mach-test', + group: 'xpcom/tests/unit/xpcshell.toml', + test: 'xpcom/tests/unit/test_bug476919.js', + }, + { + action: 'log', + time: 1776950626855, + thread: 'Thread-10', + pid: 12742, + source: 'mach-test', + message: + 'profile dir is /var/folders/_q/_459jbz15zq9qf7v979px5v00000gn/T/firefox/xpcshellprofile', + level: 'INFO', + }, + { + action: 'log', + time: 1776950626856, + thread: 'Thread-10', + pid: 12742, + source: 'mach-test', + message: + "xpcom/tests/unit/test_bug476919.js | full command: ['/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/MacOS/xpcshell', '-g', '/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/Resources', '-a', '/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/Resources', '-m', '-e', 'const _HEAD_JS_PATH = \"/Users/benoitgoupilleau/Files/mozilla/firefox/testing/xpcshell/head.js\";', '-e', 'const _MOZINFO_JS_PATH = \"/var/folders/_q/_459jbz15zq9qf7v979px5v00000gn/T/firefox/xpcshellprofile/mozinfo.json\";', '-e', 'const _PREFS_FILE = \"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/temp/user.js\";', '-e', 'const _TESTING_MODULES_DIR = \"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/modules/\";', '-f', '/Users/benoitgoupilleau/Files/mozilla/firefox/testing/xpcshell/head.js', '-e', 'const _HEAD_FILES = [\"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit/head_xpcom.js\"];', '-e', 'const _JSDEBUGGER_PORT = 0;', '-e', 'const _TEST_FILE = [\"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit/test_bug476919.js\"];', '-e', 'const _TEST_NAME = \"xpcom/tests/unit/test_bug476919.js\";', '-e', 'const _EXPECTED = \"pass\";', '-e', '_execute_test(); quit(0);']", + level: 'INFO', + }, + { + action: 'log', + time: 1776950626856, + thread: 'Thread-10', + pid: 12742, + source: 'mach-test', + message: + "xpcom/tests/unit/test_bug476919.js | current directory: '/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit'", + level: 'INFO', + }, + { + action: 'log', + time: 1776950626856, + thread: 'Thread-10', + pid: 12742, + source: 'mach-test', + message: + "xpcom/tests/unit/test_bug476919.js | environment: ['MOZ_HTTP3_CERT_DB_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/netwerk/test/http3serverDB', 'MOZ_DISABLE_CONTENT_SANDBOX=1', 'MOZ_DISABLE_SOCKET_PROCESS=1', 'MOZ_DEVELOPER_REPO_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox', 'MOZHTTP3_PORT_MASQUE=61764', 'MOZ_DISABLE_NONLOCAL_CONNECTIONS=1', 'MOZHTTP3_PORT_FAILED=53492', 'MOZHTTP2_PORT=62584', 'MOZ_CRASHREPORTER_NO_REPORT=1', 'XPCSHELL_TEST_PROFILE_DIR=/var/folders/_q/_459jbz15zq9qf7v979px5v00000gn/T/firefox/xpcshellprofile', 'MOZ_HTTP3_SERVER_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/bin/http3server', 'XPCSHELL_TEST_TEMP_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/temp/xpc-other-no90eapi', 'MOZNODE_EXEC_PORT=62585', 'DYLD_LIBRARY_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/MacOS', 'MOZHTTP3_PORT_ECH=49293', 'MOZHTTP3_PORT_PROXY=60978', 'MOZ_CRASHREPORTER=1', 'MOZ_DEVELOPER_OBJ_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0', 'MOZHTTP3_PORT_NO_RESPONSE=53983', 'MOZHTTP3_PORT=55492', 'MOZ_TEST_TIMEOUT_INTERVAL=30', 'XPCOM_DEBUG_BREAK=stack-and-abort', 'MOZHTTP3_ECH=AE3+DQBJBwAgACBFkQGZpKTJgJaxYmDd1Q8RDOSkk4eAIe/iGDbfx581NwAQAAEAAQABAAMAAgABAAIAA0AOcHVibGljLmV4YW1wbGUAAA==']", + level: 'INFO', + }, + { + action: 'log', + time: 1776950626857, + thread: 'Thread-10', + pid: 12742, + source: 'mach-test', + message: + "xpcom/tests/unit/test_bug476919.js | as shell command: (cd /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit; MOZ_HTTP3_CERT_DB_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/netwerk/test/http3serverDB MOZ_DISABLE_CONTENT_SANDBOX=1 MOZ_DISABLE_SOCKET_PROCESS=1 MOZ_DEVELOPER_REPO_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox MOZHTTP3_PORT_MASQUE=61764 MOZ_DISABLE_NONLOCAL_CONNECTIONS=1 MOZHTTP3_PORT_FAILED=53492 MOZHTTP2_PORT=62584 MOZ_CRASHREPORTER_NO_REPORT=1 XPCSHELL_TEST_PROFILE_DIR=/var/folders/_q/_459jbz15zq9qf7v979px5v00000gn/T/firefox/xpcshellprofile MOZ_HTTP3_SERVER_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/bin/http3server XPCSHELL_TEST_TEMP_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/temp/xpc-other-no90eapi MOZNODE_EXEC_PORT=62585 DYLD_LIBRARY_PATH=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/MacOS MOZHTTP3_PORT_ECH=49293 MOZHTTP3_PORT_PROXY=60978 MOZ_CRASHREPORTER=1 MOZ_DEVELOPER_OBJ_DIR=/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0 MOZHTTP3_PORT_NO_RESPONSE=53983 MOZHTTP3_PORT=55492 MOZ_TEST_TIMEOUT_INTERVAL=30 XPCOM_DEBUG_BREAK=stack-and-abort MOZHTTP3_ECH=AE3+DQBJBwAgACBFkQGZpKTJgJaxYmDd1Q8RDOSkk4eAIe/iGDbfx581NwAQAAEAAQABAAMAAgABAAIAA0AOcHVibGljLmV4YW1wbGUAAA== /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/MacOS/xpcshell -g /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/Resources -a /Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/dist/Nightly.app/Contents/Resources -m -e 'const _HEAD_JS_PATH = \"/Users/benoitgoupilleau/Files/mozilla/firefox/testing/xpcshell/head.js\";' -e 'const _MOZINFO_JS_PATH = \"/var/folders/_q/_459jbz15zq9qf7v979px5v00000gn/T/firefox/xpcshellprofile/mozinfo.json\";' -e 'const _PREFS_FILE = \"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/temp/user.js\";' -e 'const _TESTING_MODULES_DIR = \"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/modules/\";' -f /Users/benoitgoupilleau/Files/mozilla/firefox/testing/xpcshell/head.js -e 'const _HEAD_FILES = [\"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit/head_xpcom.js\"];' -e 'const _JSDEBUGGER_PORT = 0;' -e 'const _TEST_FILE = [\"/Users/benoitgoupilleau/Files/mozilla/firefox/obj-aarch64-apple-darwin25.3.0/_tests/xpcshell/xpcom/tests/unit/test_bug476919.js\"];' -e 'const _TEST_NAME = \"xpcom/tests/unit/test_bug476919.js\";' -e 'const _EXPECTED = \"pass\";' -e '_execute_test(); quit(0);')", + level: 'INFO', + }, + { + action: 'log', + time: 1776950630163, + thread: 'Thread-10', + pid: null, + source: 'xpcshell/head.js', + message: + '"CONSOLE_MESSAGE: (info) No chrome package registered for chrome://branding/locale/brand.properties"', + exc_info: false, + level: 'INFO', + }, + { + action: 'log', + time: 1776950630179, + thread: 'Thread-10', + pid: null, + source: 'xpcshell/head.js', + message: '(xpcshell/head.js) | test MAIN run_test pending (1)', + exc_info: false, + level: 'INFO', + }, + { + action: 'test_status', + time: 1776950630182, + thread: 'Thread-10', + pid: null, + source: 'xpcshell/head.js', + test: 'xpcom/tests/unit/test_bug476919.js', + subtest: 'run_test', + status: 'PASS', + message: '[run_test : 24] true == true', + }, + { + action: 'log', + time: 1776950630182, + thread: 'Thread-10', + pid: null, + source: 'xpcshell/head.js', + message: '(xpcshell/head.js) | test MAIN run_test finished (1)', + exc_info: false, + level: 'INFO', + }, + { + action: 'log', + time: 1776950630182, + thread: 'Thread-10', + pid: null, + source: 'xpcshell/head.js', + message: 'exiting test', + exc_info: false, + level: 'INFO', + }, + { + action: 'test_end', + time: 1776950630187, + thread: 'Thread-10', + pid: 12742, + source: 'mach-test', + message: 'xpcshell return code: 0', + group: 'xpcom/tests/unit/xpcshell.toml', + test: 'xpcom/tests/unit/test_bug476919.js', + status: 'PASS', + }, + { + action: 'group_end', + time: 1776950630189, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + name: 'sequential', + }, + { + action: 'log', + time: 1776950630190, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: 'INFO | Result summary:', + level: 'INFO', + }, + { + action: 'log', + time: 1776950630190, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: 'INFO | Passed: 1', + level: 'INFO', + }, + { + action: 'log', + time: 1776950630190, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: 'INFO | Failed: 0', + level: 'INFO', + }, + { + action: 'log', + time: 1776950630190, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: 'INFO | Todo: 0', + level: 'INFO', + }, + { + action: 'log', + time: 1776950630190, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: 'INFO | Retried: 0', + level: 'INFO', + }, + { + action: 'suite_end', + time: 1776950630190, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + }, + { + action: 'log', + time: 1776950630192, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + message: 'http3Server server shutting down ...', + level: 'INFO', + }, + { + action: 'shutdown', + time: 1776950630298, + thread: 'MainThread', + pid: 12742, + source: 'mach-test', + }, + ], bugSuggestionsLoading: false, }; } componentDidMount() { - this.loadBugSuggestions(); + //this.loadErrors(); window.addEventListener(thEvents.internalIssueClassification, (event) => this.checkInternalFailureOccurrences(event.detail.internalBugId), @@ -48,10 +851,35 @@ class FailureSummaryTab extends React.Component { !!prevProps.selectedJob && selectedJobId !== prevProps.selectedJobId ) { - this.loadBugSuggestions(); + this.loadErrors(); } } + loadErrors = async () => { + const { selectedJobId } = this.props; + + if (!selectedJobId) { + return; + } + this.setState({ bugSuggestionsLoading: true }); + let structuredErrors = []; + try { + structuredErrors = await StructuredLogErrorsModel.get(selectedJobId); + } catch { + structuredErrors = []; + } + if (structuredErrors.length > 0) { + this.setState({ + structuredErrors, + suggestions: [], + bugSuggestionsLoading: false, + }); + return; + } + this.setState({ structuredErrors: [] }); + this.loadBugSuggestions(); + }; + fileBug = (suggestion) => { const { selectedJob, pinJob } = this.props; @@ -243,7 +1071,9 @@ class FailureSummaryTab extends React.Component { bugSuggestionsLoading, suggestions, errors, + structuredErrors, } = this.state; + const hasStructuredErrors = structuredErrors.length > 0; const logs = jobLogUrls.filter( (jlu) => !jlu.name.includes('perfherder-data'), ); @@ -275,106 +1105,114 @@ class FailureSummaryTab extends React.Component { ref={this.fsMount} id="failure-summary-scroll-area" > - {selectedJob.newFailure > 0 && ( - - )} + {hasStructuredErrors ? ( + + ) : ( + <> + {selectedJob.newFailure > 0 && ( + + )} - {suggestions.map((suggestion, index) => ( - this.fileBug(suggestion)} - toggleInternalIssueFiler={() => - this.fileInternalIssue(suggestion) - } - selectedJob={selectedJob} - addBug={addBug} - currentRepo={currentRepo} - developerMode={developerMode} - jobDetails={jobDetails} - /> - ))} - - {!!errors.length && } - - {!jobLogsAllParsed && } - - {!bugSuggestionsLoading && - jobLogsAllParsed && - !logs.length && - !suggestions.length && - !errors.length && } - - {!bugSuggestionsLoading && - jobLogsAllParsed && - !!logs.length && - logParseStatus === 'success' && ( -
  • -

    - Log parsing complete. Generating bug suggestions. -
    - - The content of this panel will refresh in 5 seconds. - -

    -
  • - )} - - {!bugSuggestionsLoading && - !jobLogsAllParsed && - logs.map((jobLog) => ( -
  • -

    - Log parsing in progress. -
    - - The raw log - {' '} - is available. This panel will automatically recheck every 5 - seconds. -

    -
  • - ))} - - {!bugSuggestionsLoading && logParseStatus === 'failed' && ( - - )} + {suggestions.map((suggestion, index) => ( + this.fileBug(suggestion)} + toggleInternalIssueFiler={() => + this.fileInternalIssue(suggestion) + } + selectedJob={selectedJob} + addBug={addBug} + currentRepo={currentRepo} + developerMode={developerMode} + jobDetails={jobDetails} + /> + ))} - {!bugSuggestionsLoading && logParseStatus === 'skipped-size' && ( - - )} + {!!errors.length && } - {!bugSuggestionsLoading && !logs.length && ( - - )} + {!jobLogsAllParsed && ( + + )} - {bugSuggestionsLoading && ( -
    -
    - } + + {!bugSuggestionsLoading && + jobLogsAllParsed && + !!logs.length && + logParseStatus === 'success' && ( +
  • +

    + Log parsing complete. Generating bug suggestions. +
    + + The content of this panel will refresh in 5 seconds. + +

    +
  • + )} + + {!bugSuggestionsLoading && + !jobLogsAllParsed && + logs.map((jobLog) => ( +
  • +

    + Log parsing in progress. +
    + + The raw log + {' '} + is available. This panel will automatically recheck every + 5 seconds. +

    +
  • + ))} + + {!bugSuggestionsLoading && logParseStatus === 'failed' && ( + + )} + + {!bugSuggestionsLoading && logParseStatus === 'skipped-size' && ( + + )} + + {!bugSuggestionsLoading && !logs.length && ( + -
    -
    + )} + + {bugSuggestionsLoading && ( +
    +
    + +
    +
    + )} + )} {isBugFilerOpen && ( diff --git a/ui/shared/tabs/failureSummary/StructuredErrorsList.jsx b/ui/shared/tabs/failureSummary/StructuredErrorsList.jsx new file mode 100644 index 00000000000..e8c74274616 --- /dev/null +++ b/ui/shared/tabs/failureSummary/StructuredErrorsList.jsx @@ -0,0 +1,82 @@ +import PropTypes from 'prop-types'; + +const formatTime = (epochMs) => { + if (!epochMs) return ''; + const date = new Date(epochMs); + if (Number.isNaN(date.getTime())) return ''; + return date.toISOString().replace('T', ' ').replace('Z', ''); +}; + +const levelBadgeClass = (level) => { + const normalized = (level || '').toUpperCase(); + if (normalized === 'CRITICAL') return 'bg-danger'; + if (normalized === 'ERROR') return 'bg-warning text-dark'; + return 'bg-secondary'; +}; + +export default function StructuredErrorsList(props) { + const { errors } = props; + + return ( +
  • +
      + {errors.map((err) => ( +
    • +
      + + {(err.level || '').toUpperCase() || 'ERROR'} + + {err.action && ( + + {err.action} + + )} + {err.source && ( + + {err.source} + + )} + {err.time && ( + + {formatTime(err.time)} + + )} + {(err.pid || err.thread) && ( + + {err.pid ? `pid ${err.pid}` : ''} + {err.pid && err.thread ? ' ยท ' : ''} + {err.thread || ''} + + )} +
      +
      +              {err.message}
      +            
      +
    • + ))} +
    +
  • + ); +} + +StructuredErrorsList.propTypes = { + errors: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + action: PropTypes.string, + time: PropTypes.number, + thread: PropTypes.string, + pid: PropTypes.number, + source: PropTypes.string, + message: PropTypes.string, + level: PropTypes.string, + }), + ).isRequired, +};