@snapshot-labs/snapshot-sentry
v4.0.0
Published
`snapshot-sentry` is an npm package that contains the Sentry setup files and configurations for Snapshot backend projects. It simplifies the process of setting up Sentry for error tracking in any Snapshot service.
Readme
Snapshot-sentry
snapshot-sentry is an npm package that contains the Sentry setup files and configurations for Snapshot backend projects.
It simplifies the process of setting up Sentry for error tracking in any Snapshot service.
Install
yarn add @snapshot-labs/snapshot-sentryConfigure
Set the following env variables:
SENTRY_DSN(required)SENTRY_TRACE_SAMPLE_RATE(Optional, default to0)METRICS_PUSHGATEWAY_URL(Optional, shared with@snapshot-labs/snapshot-metrics)
Usage
Instantiate
Important: Since
@sentry/nodev8, Sentry uses OpenTelemetry-based auto-instrumentation.initLogger()must run before any instrumented module (http,express,pg, etc.) is imported. The recommended pattern is to put init in its own file and load it first.
For express
Create an instrument.ts (or .js) file that runs Sentry init at the top of your process:
// instrument.ts
import { initLogger } from '@snapshot-labs/snapshot-sentry';
initLogger();Import it as the very first import in your entry file, before express:
// app.ts
import './instrument';
import express from 'express';
import { fallbackLogger } from '@snapshot-labs/snapshot-sentry';
const app = express();
// ... your routes/controllers
// Set the fallthrough error handler after all controllers, and after any error middleware of your own
fallbackLogger(app);For ESM projects, use Node's --import flag instead of importing ./instrument:
node --import ./instrument.mjs ./app.mjsNote: fallbackLogger(app) must come after every route, a catch-all included. Express only looks for error handlers registered later in the stack than the layer that failed, so a route registered after the call has its errors answered by express instead, stack in the body and nothing reported.
All uncaught errors, with code >= 500, will be handled automatically by Sentry. See Capture exception for how to handle caught exceptions.
Every express service should call fallbackLogger(app), whether or not its routes currently let an error escape. It is the only thing that turns an express route error into a Sentry event, and on express 5 a rejected async handler reaches it rather than becoming an unhandled rejection. A service whose routes all catch their own errors gains nothing from the call today and loses nothing by making it, which is what keeps its error reporting working across an express upgrade.
fallbackLogger reports the error and then answers the request. An error carrying a status / statusCode in the 4xx or 5xx range answers with it, otherwise a status already set on the response stands, and 500 is the fallback. Headers the error carries, such as WWW-Authenticate or Retry-After, are applied when the status came from the error.
What gets reported is decided separately, by Sentry, from the status on the error alone and never from the status answered. A route that does res.status(403) and then fails with a plain error answers 403 and is still reported, because the error itself carries no status. The event id, when there is one, is set on res.sentry.
The body is the reason phrase for the status, Bad Request or Internal Server Error, and never the error message, its stack, or an event id. That is what keeps an unredacted error message out of the response: the scrubbing described below governs what is sent to Sentry, and express would otherwise write the raw message and stack into the body of any process not running with NODE_ENV=production.
Two paths fall outside that and are answered by express, so set NODE_ENV=production as well: fallbackLogger installs nothing at all when SENTRY_DSN is unset, and it cannot see an error from a route registered after the call.
fallbackLogger answers the request, so error middleware of your own belongs before the call and should pass the error on with next(err); anything registered after it will not run.
For vanilla js
Import the library in your root file
import { initLogger } from '@snapshot-labs/snapshot-sentry'Init sentry, as soon as possible to catch all errors.
initLogger()See Capture exception for how to handle exceptions.
Capture exception
To capture an exception, use the capture function:
import { capture } from '@snapshot-labs/snapshot-sentry'
try {
throw new Error('Ooops, someting went wrong');
} catch (e: any) {
// Send the error to sentry
capture(e)
}You can also pass additional context data to the capture function:
import { capture } from '@snapshot-labs/snapshot-sentry'
let url = '';
try {
url = getUrlFromSomewhere('argOne')
throw new Error('Ooops, someting went wrong');
} catch (e: any) {
// Send the error to sentry
capture(e, { contexts: { input: { url: url } } })
}When sentry is disabled, the capture function will fallback to a simple console.log, so no code change will be required when toggling this library on/off.
Unhandled promise rejections
An unhandled promise rejection is reported to Sentry and then terminates the process with exit code 1, which is what Node itself does by default since v15. Any service calling initLogger() must therefore run under something that restarts it (Docker/Fly restart policy, systemd, pm2), otherwise a rejection leaves it stopped.
Two exceptions to that: a small set of reasons that Sentry ignores by default (AbortError among them) is neither reported nor fatal, and ignoreErrors only stops the event being sent, not the exit, so a rejection matching it kills the process with nothing in Sentry to explain the restart.
Data scrubbing
Before sending an event, initLogger runs a beforeSend hook that redacts common secrets from the error message (exception.value), replacing them with [Filtered]:
?apiKey=...query-string valuesset-cookie/cookie/authorizationheader values serialised as JSON (e.g."set-cookie":"...")- bare
Bearer <token>strings - the value of any
process.envvariable whose name ends in_KEY,_API,_SECRETor_TOKEN
Scrubbing only covers the error message string. Structured data you attach via capture(e, context) is sent as-is, so avoid putting secrets in contexts.
Pushgateway breadcrumbs
When METRICS_PUSHGATEWAY_URL is set, initLogger drops HTTP breadcrumbs for requests to that origin and URL path, including child paths. The same variable configures @snapshot-labs/snapshot-metrics, and this package reads it directly to remain dependency-free.
The filter leaves other URLs and breadcrumb categories unchanged. An invalid value logs a warning during initialization and disables the filter.
More info
License
Snapshot-sentry is open-sourced software licensed under the © MIT license.
