-
Notifications
You must be signed in to change notification settings - Fork 3
🥅 server: fingerprint service errors by name and message #834
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@exactly/server": patch | ||
| --- | ||
|
|
||
| 🥅 fingerprint service errors by name and message |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,6 +19,7 @@ import persona from "./hooks/persona"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import androidFingerprints from "./utils/android/fingerprints"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import appOrigin from "./utils/appOrigin"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { closeAndFlush as closeSegment } from "./utils/segment"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ServiceError from "./utils/ServiceError"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { UnofficialStatusCode } from "hono/utils/http-status"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -273,36 +274,11 @@ frontend.use( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app.route("/", frontend); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app.onError((error, c) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let fingerprint: string[] | undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (error instanceof Error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const message = error.message | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .split("Error:") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .reduce((result, part) => (result ? `${result}Error:${part}` : part.trimStart()), ""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const status = message.slice(0, 3); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hasStatus = /^\d{3}$/.test(status); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hasBodyFormat = message.length === 3 || message[3] === " "; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const body = hasBodyFormat && message.length > 3 ? message.slice(4).trim() : undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (hasStatus && hasBodyFormat) fingerprint = ["{{ default }}", status]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (hasStatus && hasBodyFormat && body) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const json = JSON.parse(body) as { code?: unknown; error?: unknown; message?: unknown }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fingerprint = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "{{ default }}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...("code" in json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? [String(json.code)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : typeof json.message === "string" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? [json.message] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : typeof json.error === "string" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? [json.error] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : [body]), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fingerprint = ["{{ default }}", status, body]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| captureException(error, { level: "error", tags: { unhandled: true }, fingerprint }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| captureException(error, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| level: "error", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tags: { unhandled: true }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fingerprint: error instanceof ServiceError ? ["{{ default }}", error.name, error.message] : undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+277
to
+281
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Comment on lines
276
to
+281
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add trailing comma in fingerprint array. Line 280’s array should include a trailing comma per repo style. 🔧 Proposed fix- fingerprint: error instanceof ServiceError ? ["{{ default }}", error.name, error.message] : undefined,
+ fingerprint: error instanceof ServiceError ? ["{{ default }}", error.name, error.message,] : undefined,📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return c.json({ code: "unexpected error", legacy: "unexpected error" }, 555 as UnofficialStatusCode); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
276
to
282
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚩 Loss of fingerprinting for non-ServiceError status-formatted errors reaching app.onError The old Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,20 +28,28 @@ init({ | |
| ], | ||
| beforeSend: (event, hint) => { | ||
| const exception = event.exception?.values?.[0]; | ||
| if (!exception) return event; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| const error = hint.originalException; | ||
| if ( | ||
| error instanceof Error && | ||
| typeof (/** @type {{ status?: unknown }} */ (error).status) === "number" && | ||
|
Comment on lines
+32
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: The new fingerprinting logic in Suggested FixUpdate the check in Prompt for AI AgentDid we get this right? 👍 / 👎 to inform future reviews. |
||
| !(event.fingerprint && event.fingerprint.length > 1) | ||
| ) { | ||
| event.fingerprint = ["{{ default }}", error.name, error.message]; | ||
| } | ||
|
Comment on lines
+32
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This new block correctly identifies errors that have a
Comment on lines
+33
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚩 beforeSend fingerprinting applies to all errors with numeric The new Was this helpful? React with 👍 or 👎 to provide feedback.
Comment on lines
+31
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove JSDoc cast and add trailing comma. JSDoc casts are disallowed, and the fingerprint array needs a trailing comma. 🔧 Proposed fix- if (
- error instanceof Error &&
- typeof (/** `@type` {{ status?: unknown }} */ (error).status) === "number" &&
- !(event.fingerprint && event.fingerprint.length > 1)
- ) {
- event.fingerprint = ["{{ default }}", error.name, error.message];
- }
+ if (
+ error instanceof Error &&
+ typeof error.status === "number" &&
+ !(event.fingerprint && event.fingerprint.length > 1)
+ ) {
+ event.fingerprint = ["{{ default }}", error.name, error.message,];
+ } |
||
| if ( | ||
| exception && | ||
| event.fingerprint?.[0] === "{{ default }}" && | ||
| (exception.type === "ContractFunctionExecutionError" || | ||
| exception.type === "ContractFunctionRevertedError" || | ||
| exception.type === "BaseError") | ||
| ) { | ||
| /** @typedef {{ cause?: unknown; data?: { errorName?: string }; reason?: string; signature?: string }} RevertError */ | ||
| for ( | ||
| let error = /** @type {RevertError | undefined} */ (hint.originalException); | ||
| error; | ||
| error = /** @type {RevertError | undefined} */ (error.cause) | ||
| let revert = /** @type {RevertError | undefined} */ (hint.originalException); | ||
| revert; | ||
| revert = /** @type {RevertError | undefined} */ (revert.cause) | ||
| ) { | ||
| const reason = error.data?.errorName ?? error.reason ?? error.signature; | ||
| const reason = revert.data?.errorName ?? revert.reason ?? revert.signature; | ||
| if (reason) { | ||
| exception.type = reason; | ||
| break; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removal of the complex error message parsing logic is a significant improvement. Relying on
ServiceErrorfor structured error data simplifies theonErrorhandler and makes error fingerprinting more robust and maintainable. This change directly addresses the goal of fingerprinting service errors by name and message, making the Sentry events more actionable.