From 55eb9e874affd09f61ac8e64ca60165eb86566d5 Mon Sep 17 00:00:00 2001 From: Blake Gentry Date: Fri, 21 Feb 2025 10:57:07 -0600 Subject: [PATCH 1/4] job factory: realistic sample trace for better storybook pages --- src/test/factories/job.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/test/factories/job.ts b/src/test/factories/job.ts index a917a980..9a7fcf38 100644 --- a/src/test/factories/job.ts +++ b/src/test/factories/job.ts @@ -7,13 +7,25 @@ import { add, sub } from "date-fns"; class AttemptErrorFactory extends Factory {} +const sampleTrace = ` +worker.go:184: panic: runtime error: invalid memory address or nil pointer dereference + panic({0x1035d3c40?, 0x103c0f530?}) + /opt/homebrew/Cellar/go/1.24.0/libexec/src/runtime/panic.go:787 +0xf0 + github.com/riverqueue/myapp/worker.(*SyntheticBaseWorker).syntheticSleepOrError(0x0, {0x1036c44c0, 0x14000056380}, {0x1036bb5e0, 0x140000b0320}) + /Users/username/River/myapp/worker/synthetic_base_job.go:22 +0x50 + github.com/riverqueue/myapp/worker.(*ChargeCreditCard).Work(0x1400000c090, {0x1036c44c0, 0x14000056380}, 0x1400004a310) + /Users/username/River/myapp/worker/charge_credit_card.go:33 +0x19c + github.com/riverqueue/river/rivertest.(*wrapperWorkUnit[...]).Work(0x1036c4320, {0x1036c44c0, 0x140000562a0}) + /Users/username/River/river/rivertest/worker.go:281 +0x60 +`.trim(); + export const attemptErrorFactory = AttemptErrorFactory.define(({ params }) => { const attempt = params.attempt || 1; return { at: params.at || sub(new Date(), { seconds: (21 - attempt) * 7.3 }), attempt, error: "Failed yet again with some Go message", - trace: "...", + trace: sampleTrace, }; }); From 1c2a905df22f5b590894c759c476ca2a0bcac797 Mon Sep 17 00:00:00 2001 From: Blake Gentry Date: Fri, 21 Feb 2025 11:22:47 -0600 Subject: [PATCH 2/4] JobDetails: improve styling of attempt error section + traces --- src/components/JobAttemptErrors.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/JobAttemptErrors.tsx b/src/components/JobAttemptErrors.tsx index 528a3d3a..675eb2d1 100644 --- a/src/components/JobAttemptErrors.tsx +++ b/src/components/JobAttemptErrors.tsx @@ -15,12 +15,12 @@ export default function JobAttemptErrors({ job }: JobAttemptErrorsProps) { : job.errors.slice(-1 * defaultErrorDisplayCount).reverse(); return ( -
-
+
+
Errors
-
+
{job.errors.length === 0 ? ( <>No errors ) : ( @@ -30,17 +30,17 @@ export default function JobAttemptErrors({ job }: JobAttemptErrorsProps) { className="divide-y divide-slate-300 dark:divide-slate-700" > {errorsToDisplay.map((error) => ( -
  • +
  • {error.attempt.toString()}

    -
    +
    {error.error}
    {error.trace && ( -
    +                          
                                 {error.trace}
                               
    )} From bdd5297e568fecb3582ca506938cedd2a6173396 Mon Sep 17 00:00:00 2001 From: Blake Gentry Date: Fri, 21 Feb 2025 11:44:39 -0600 Subject: [PATCH 3/4] JobAttemptErrors: improve display of multiline errors, stack trace header --- src/components/JobAttemptErrors.tsx | 28 ++++++++++++++++++++++++---- src/test/factories/job.ts | 12 +++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/components/JobAttemptErrors.tsx b/src/components/JobAttemptErrors.tsx index 675eb2d1..3b89affc 100644 --- a/src/components/JobAttemptErrors.tsx +++ b/src/components/JobAttemptErrors.tsx @@ -1,6 +1,7 @@ import { Job } from "@services/jobs"; import RelativeTimeFormatter from "./RelativeTimeFormatter"; import { useState } from "react"; +import clsx from "clsx"; type JobAttemptErrorsProps = { job: Job; @@ -14,6 +15,10 @@ export default function JobAttemptErrors({ job }: JobAttemptErrorsProps) { ? job.errors.slice().reverse() : job.errors.slice(-1 * defaultErrorDisplayCount).reverse(); + const isMultilineError = (error: AttemptError) => { + return error.error.includes("\n"); + }; + return (
    @@ -36,13 +41,28 @@ export default function JobAttemptErrors({ job }: JobAttemptErrorsProps) { {error.attempt.toString()}

    -
    +
    {error.error}
    {error.trace && ( -
    -                            {error.trace}
    -                          
    + <> +
    + Stack Trace: +
    +
    +                              {error.trace}
    +                            
    + )}

    diff --git a/src/test/factories/job.ts b/src/test/factories/job.ts index 9a7fcf38..7f4ed828 100644 --- a/src/test/factories/job.ts +++ b/src/test/factories/job.ts @@ -106,6 +106,12 @@ class JobFactory extends Factory { const erroredAt = add(attemptedAt, { seconds: faker.number.float({ min: 0.01, max: 95 }), }); + const multilineError = ` + This is a long error message that spans multiple lines. + It is used to test the ability of the JobAttemptErrors component to display long error messages. + + It should not show as a single paragraph with no linebreaks. + `.trim(); return this.params({ attempt: 9, attemptedAt, @@ -123,13 +129,13 @@ class JobFactory extends Factory { errors: [ attemptErrorFactory.build({ attempt: 1 }), attemptErrorFactory.build({ attempt: 2 }), - attemptErrorFactory.build({ attempt: 3 }), + attemptErrorFactory.build({ attempt: 3, error: multilineError }), attemptErrorFactory.build({ attempt: 4 }), attemptErrorFactory.build({ attempt: 5 }), attemptErrorFactory.build({ attempt: 6 }), attemptErrorFactory.build({ attempt: 7 }), - attemptErrorFactory.build({ attempt: 8 }), - attemptErrorFactory.build({ attempt: 9 }), + attemptErrorFactory.build({ attempt: 8, error: multilineError }), + attemptErrorFactory.build({ attempt: 9, trace: undefined }), attemptErrorFactory.build({ at: erroredAt, attempt: 10, From 4570ebe864ff8db08d6e938cded0cb58206da820 Mon Sep 17 00:00:00 2001 From: Blake Gentry Date: Fri, 21 Feb 2025 13:12:41 -0600 Subject: [PATCH 4/4] JobAttemptError: handle multiline error messages w/ scrolling Fixes #290. --- CHANGELOG.md | 1 + src/components/JobAttemptErrors.tsx | 8 +- src/components/JobDetail.tsx | 122 ++++++++++++++-------------- 3 files changed, 65 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ee229ef..2cac762c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix negative wait times in job timeline. [PR #288](https://github.com/riverqueue/riverui/pull/288). - Fix occasionally incorrect job durations for errored jobs. [PR #288](https://github.com/riverqueue/riverui/pull/288). +- Improve display of job attempt errors. [PR #291](https://github.com/riverqueue/riverui/pull/291). ## [v0.8.0] - 2025-02-10 diff --git a/src/components/JobAttemptErrors.tsx b/src/components/JobAttemptErrors.tsx index 3b89affc..fd91af1b 100644 --- a/src/components/JobAttemptErrors.tsx +++ b/src/components/JobAttemptErrors.tsx @@ -1,4 +1,4 @@ -import { Job } from "@services/jobs"; +import { type AttemptError, Job } from "@services/jobs"; import RelativeTimeFormatter from "./RelativeTimeFormatter"; import { useState } from "react"; import clsx from "clsx"; @@ -40,12 +40,12 @@ export default function JobAttemptErrors({ job }: JobAttemptErrorsProps) {

    {error.attempt.toString()}

    -
    +
    @@ -57,7 +57,7 @@ export default function JobAttemptErrors({ job }: JobAttemptErrorsProps) { Stack Trace:
                                   {error.trace}
    diff --git a/src/components/JobDetail.tsx b/src/components/JobDetail.tsx
    index e7fcff6f..35449597 100644
    --- a/src/components/JobDetail.tsx
    +++ b/src/components/JobDetail.tsx
    @@ -130,7 +130,7 @@ export default function JobDetail({
               
    -
    +
    {/* Description list */}
    @@ -221,69 +221,67 @@ export default function JobDetail({ -
    -
    -
    -
    -
    - Args -
    -
    -
    -                      {JSON.stringify(job.args, null, 2)}
    -                    
    -
    -
    -
    -
    - Metadata -
    -
    -
    -                      {JSON.stringify(job.metadata, null, 2)}
    -                    
    -
    -
    -
    -
    - Attempted By -
    -
    -
      - {attemptsToDisplay.map((attemptedBy, i) => ( -
    • - {attemptedBy} -
    • - ))} -
    - {!showAllAttempts && job.attemptedBy.length > 5 && ( - - )} - {showAllAttempts && ( - - )} -
    -
    + {attemptedBy} +
  • + ))} + + {!showAllAttempts && job.attemptedBy.length > 5 && ( + + )} + {showAllAttempts && ( + + )} +
    +
    - - -
    + +