diff --git a/src/errors.ts b/src/errors.ts index 2a513170..d6f5f132 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -7,6 +7,7 @@ import { ResponseError as SchemaRegistryResponseError } from "./clients/schemaRe import { ResponseError as SidecarResponseError } from "./clients/sidecar"; import { observabilityContext } from "./context/observability"; import { Logger } from "./logging"; +import { SIDECAR_CONNECTION_ID_HEADER } from "./sidecar/constants"; const logger = new Logger("errors"); @@ -54,7 +55,7 @@ export async function logResponseError( logger.error(errorMessage, { ...errorContext, ...extra }); if (sendTelemetry) { - Sentry.captureException(e, { + captureException(e, { contexts: { response: { status_code: responseStatusCode } }, extra: { ...errorContext, ...extra }, }); @@ -62,25 +63,48 @@ export async function logResponseError( } /** - * Wrapper around the Sentry {@link Sentry.captureException} to include our extension instance's + * Wrapper around the {@link Sentry.captureException} to include our extension instance's * {@link observabilityContext} in the event context, along with any additionally provided context. * + * If a {@link ResponseError} is provided as the `error`, the response status code and any associated + * `x-connection-id` header will be included in the event context under `contexts.response`. + * * See {@link ExclusiveEventHintOrCaptureContext} for more information on the `context` parameter. */ export function captureException( - error: unknown, - context: ExclusiveEventHintOrCaptureContext | undefined, + e: unknown, + context: ExclusiveEventHintOrCaptureContext = {}, ): void { // TODO: check telemetry settings here? const obsContext: Record = observabilityContext.toRecord(); - const extraContext = context ? context : {}; - // ensure observability context keys always make it into the Sentry event under `extra`, even if - // the caller provided their own `extra` data in `context` - const { extra: extraFromContext = {}, ...restExtraContext } = extraContext as any; - const errorContext: Record = { - extra: { ...obsContext, ...extraFromContext }, - ...restExtraContext, - }; + let errorContext: Record = { extra: { ...obsContext } }; + + if (context && context.data.extra) { + // ensure observability context keys always make it into the Sentry event under `extra`, even if + // the caller provided their own `extra` data in `context` + const { extra: extraFromContext = {}, ...restExtraContext } = context as Record; + errorContext = { + extra: { ...obsContext, ...extraFromContext }, + ...restExtraContext, + }; + } + + if ((e as any).response) { + const errorResponse = (e as ResponseError).response; + errorContext = { + ...errorContext, + contexts: { + response: { + status_code: errorResponse.status, + headers: { + [SIDECAR_CONNECTION_ID_HEADER]: + errorResponse.headers.get(SIDECAR_CONNECTION_ID_HEADER) ?? "", + }, + }, + }, + }; + } + logger.debug("capturing exception before sending to Sentry", errorContext); - Sentry.captureException(error, errorContext); + Sentry.captureException(e, errorContext); }