Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | 6x 25x 25x 21x 15x 6x 4x 6x 9x 9x 9x 5x 5x 1x 1x 4x 4x 9x 11x 25x 19x 11x 6x 6x 5x 5x 4x 1x | import { makeNodeTransport } from "@sentry/node";
import { logger } from "server/utils/logger";
import {
flattenErrorMessageChain,
retryWithDelays,
sentryRetryDelaysMs,
} from "server/utils/sentryRetry";
type NodeTransportOptions = Parameters<typeof makeNodeTransport>[0];
type NodeTransport = ReturnType<typeof makeNodeTransport>;
type TransportResponse = Awaited<ReturnType<NodeTransport["send"]>>;
type SendAttempt =
| { delivered: true; response: TransportResponse }
| { delivered: false; reason: string; response: TransportResponse }
| { delivered: false; reason: string; rejection: unknown };
// 4xx responses count as delivered: they are final verdicts from Sentry, and
// 429 backoff stays with the SDK's rate limiter
const attemptSend = async (
transport: NodeTransport,
envelope: Parameters<NodeTransport["send"]>[0],
): Promise<SendAttempt> => {
try {
const response = await transport.send(envelope);
if (response.statusCode !== undefined && response.statusCode >= 500) {
return {
delivered: false,
reason: `upstream responded with ${response.statusCode}`,
response,
};
}
return { delivered: true, response };
} catch (error) {
return {
delivered: false,
reason: flattenErrorMessageChain(error),
rejection: error,
};
}
};
// The default transport silently drops an envelope when the send fails or
// Sentry responds with 5xx, so retry each envelope with increasing delays
export const makeRetryingNodeTransport = (
options: NodeTransportOptions,
): NodeTransport => {
const transport = makeNodeTransport(options);
// Tracks the failing state so each outage logs one Sentry-bound error when
// it starts; repeats during the episode only warn to the console. Without
// this a failed drop report would feed itself back into the failing
// transport forever
let sendFailing = false;
const reportDrop = (reason: string): void => {
const message = `Sentry event dropped: ${reason}`;
if (sendFailing) {
logger.warn(message);
return;
}
sendFailing = true;
logger.error(new Error(message));
};
return {
...transport,
send: async (envelope) => {
const attempt = await retryWithDelays(
sentryRetryDelaysMs,
async () => attemptSend(transport, envelope),
(result) => !result.delivered,
);
if (attempt.delivered) {
sendFailing = false;
return attempt.response;
}
reportDrop(attempt.reason);
if ("response" in attempt) {
return attempt.response;
}
// eslint-disable-next-line no-restricted-syntax -- Transport contract propagates send failures to the SDK as rejections
throw attempt.rejection;
},
};
};
|