Skip to content

Commit

Permalink
who catches the error catcher?
Browse files Browse the repository at this point in the history
  • Loading branch information
patroza committed Feb 19, 2025
1 parent 6cce8dd commit a661857
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 76 deletions.
6 changes: 6 additions & 0 deletions .changeset/rich-dolphins-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@effect-app/infra": minor
"@effect-app/vue": minor
---

who catches the error catcher?
99 changes: 59 additions & 40 deletions packages/infra/src/errorReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,51 @@ import { getRC } from "./api/setupRequest.js"
import { CauseException, ErrorReported, tryToJson, tryToReport } from "./errors.js"
import { InfraLogger } from "./logger.js"

const tryCauseException = <E>(cause: Cause<E>, name: string): CauseException<E> => {
try {
return new CauseException(cause, name)
} catch {
return new CauseException(Cause.die(new Error("Failed to create CauseException")), name)
}
}

export function reportError(
name: string
) {
return (cause: Cause<unknown>, extras?: Record<string, unknown>) =>
Effect.gen(function*() {
if (Cause.isInterruptedOnly(cause)) {
yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
return
}
const error = new CauseException(cause, name)
return (
cause: Cause<unknown>,
extras?: Record<string, unknown>
) =>
Effect
.gen(function*() {
if (Cause.isInterruptedOnly(cause)) {
yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
return
}
const error = tryCauseException(cause, name)

yield* reportSentry(error, extras)
yield* InfraLogger
.logError("Reporting error", cause)
.pipe(
Effect.annotateLogs(dropUndefined({
extras,
error: tryToReport(error),
cause: tryToJson(cause),
__error_name__: name
})),
Effect.catchAllCause((cause) => InfraLogger.logError("Failed to log error", cause)),
Effect.catchAllCause(() => InfraLogger.logError("Failed to log error cause"))
)
yield* reportSentry(error, extras)
yield* InfraLogger
.logError("Reporting error", cause)
.pipe(
Effect.annotateLogs(dropUndefined({
extras,
error: tryToReport(error),
cause: tryToJson(cause),
__error_name__: name
}))
)

error[ErrorReported] = true
return error
})
error[ErrorReported] = true
return error
})
.pipe(
Effect.tapErrorCause((cause) =>
InfraLogger.logError("Failed to report error", cause).pipe(
Effect.tapErrorCause(() => InfraLogger.logFatal("Failed to log error cause"))
)
)
)
}

function reportSentry(
Expand All @@ -53,23 +70,25 @@ export function logError<E>(
name: string
) {
return (cause: Cause<E>, extras?: Record<string, unknown>) =>
Effect.gen(function*() {
if (Cause.isInterruptedOnly(cause)) {
yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
return
}
yield* InfraLogger
.logWarning("Logging error", cause)
.pipe(
Effect.annotateLogs(dropUndefined({
extras,
cause: tryToJson(cause),
__error_name__: name
})),
Effect.catchAllCause((cause) => InfraLogger.logError("Failed to log error", cause)),
Effect.catchAllCause(() => InfraLogger.logError("Failed to log error cause"))
)
})
Effect
.gen(function*() {
if (Cause.isInterruptedOnly(cause)) {
yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
return
}
yield* InfraLogger
.logWarning("Logging error", cause)
.pipe(
Effect.annotateLogs(dropUndefined({
extras,
cause: tryToJson(cause),
__error_name__: name
}))
)
})
.pipe(
Effect.tapErrorCause(() => InfraLogger.logFatal("Failed to log error cause"))
)
}

export function captureException(error: unknown) {
Expand Down
90 changes: 57 additions & 33 deletions packages/vue/src/errorReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,46 @@ import { Cause, Effect } from "effect-app"
import { CauseException, ErrorReported, tryToJson, tryToReport } from "effect-app/client/errors"
import { dropUndefined } from "effect-app/utils"

export const tryCauseException = <E>(cause: Cause<E>, name: string): CauseException<E> => {
try {
return new CauseException(cause, name)
} catch {
return new CauseException(Cause.die(new Error("Failed to create CauseException")), name)
}
}

export function reportError(
name: string
) {
return (cause: Cause.Cause<unknown>, extras?: Record<string, unknown>) =>
Effect.gen(function*() {
if (Cause.isInterruptedOnly(cause)) {
yield* Effect.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
return Cause.squash(cause)
}
return (cause: Cause.Cause<unknown>, extras?: Record<string, unknown>): Effect.Effect<unknown, never, never> =>
Effect
.gen(function*() {
if (Cause.isInterruptedOnly(cause)) {
yield* Effect.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
return Cause.squash(cause)
}

const error = new CauseException(cause, name)
yield* reportSentry(error, extras)
yield* Effect
.logError("Reporting error", cause)
.pipe(Effect.annotateLogs(dropUndefined({
extras,
error: tryToReport(error),
cause: tryToJson(cause),
__error_name__: name
})))
const error = tryCauseException(cause, name)
yield* reportSentry(error, extras)
yield* Effect
.logError("Reporting error", cause)
.pipe(Effect.annotateLogs(dropUndefined({
extras,
error: tryToReport(error),
cause: tryToJson(cause),
__error_name__: name
})))

error[ErrorReported] = true
return error
})
error[ErrorReported] = true
return error
})
.pipe(
Effect.tapErrorCause((cause) =>
Effect.logError("Failed to report error", cause).pipe(
Effect.tapErrorCause(() => Effect.logFatal("Failed to log error cause"))
)
)
)
}

function reportSentry(
Expand All @@ -48,21 +64,29 @@ export function logError<E>(
name: string
) {
return (cause: Cause.Cause<E>, extras?: Record<string, unknown>) =>
Effect.gen(function*() {
if (Cause.isInterruptedOnly(cause)) {
yield* Effect.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
return
}
yield* Effect
.logWarning("Logging error", cause)
.pipe(
Effect.annotateLogs(dropUndefined({
extras,
cause: tryToJson(cause),
__error_name__: name
}))
Effect
.gen(function*() {
if (Cause.isInterruptedOnly(cause)) {
yield* Effect.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
return
}
yield* Effect
.logWarning("Logging error", cause)
.pipe(
Effect.annotateLogs(dropUndefined({
extras,
cause: tryToJson(cause),
__error_name__: name
}))
)
})
.pipe(
Effect.tapErrorCause((cause) =>
Effect.logError("Failed to log error", cause).pipe(
Effect.tapErrorCause(() => Effect.logFatal("Failed to log error cause"))
)
)
})
)
}

export function captureException(error: unknown) {
Expand Down
7 changes: 4 additions & 3 deletions packages/vue/src/makeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import type { Result } from "@effect-rx/rx/Result"
import * as Sentry from "@sentry/browser"
import { Cause, Effect, Exit, Match, Option, Runtime, S, Struct } from "effect-app"
import type { RequestHandler, RequestHandlerWithInput, TaggedRequestClassAny } from "effect-app/client/clientFor"
import { CauseException, type SupportedErrors } from "effect-app/client/errors"
import { type SupportedErrors } from "effect-app/client/errors"
import { constant, pipe, tuple } from "effect-app/Function"
import type { OperationFailure } from "effect-app/Operations"
import { OperationSuccess } from "effect-app/Operations"
import type { Schema } from "effect-app/Schema"
import { dropUndefinedT } from "effect-app/utils"
import type { ComputedRef, Ref, ShallowRef } from "vue"
import { computed, ref, watch } from "vue"
import { tryCauseException } from "./errorReporter.js"
import { buildFieldInfoFromFieldsRoot } from "./form.js"
import { getRuntime } from "./lib.js"
import type { MakeIntlReturn } from "./makeIntl.js"
Expand Down Expand Up @@ -156,7 +157,7 @@ export function handleRequest<
message: `Unexpected Error trying to ${action}`
}
console.error(extra.message, cause)
Sentry.captureException(new CauseException(cause, "defect"), { extra })
Sentry.captureException(tryCauseException(cause, "defect"), { extra })

yield* options.onDefect(cause, i)
})
Expand All @@ -168,7 +169,7 @@ export function handleRequest<
action,
message: `Unexpected Error trying to handle errors for ${action}`
}
Sentry.captureException(new CauseException(cause, "unhandled"), { extra })
Sentry.captureException(tryCauseException(cause, "unhandled"), { extra })
console.error(Cause.pretty(cause), extra)
})
)
Expand Down

0 comments on commit a661857

Please sign in to comment.