Skip to content

Commit

Permalink
docs(express): Add v7 docs (#11519)
Browse files Browse the repository at this point in the history
  • Loading branch information
chargome authored Oct 10, 2024
1 parent 647fb6a commit e56cb59
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 7 deletions.
14 changes: 14 additions & 0 deletions docs/platforms/javascript/guides/express/index__v7.x.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: Express
description: "Learn how to instrument the Sentry Express SDK."
sdk: sentry.javascript.node
fallbackGuide: javascript.node
categories:
- server
---

<PlatformContent includePath="getting-started-primer" />

This guide explains how to set up Sentry in your Express application.

<PlatformContent includePath="getting-started-node" />
201 changes: 201 additions & 0 deletions platform-includes/getting-started-node/javascript.express__v7.x.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@

<OnboardingOptionButtons
options={["error-monitoring", "performance", "profiling"]}
/>
<PlatformContent includePath="getting-started-install" />

## Configure

Sentry should be initialized in your application as early as possible:

```javascript {tabTitle:CommonJS} {filename: instrument.js} {"onboardingOptions": {"performance": "10-14", "profiling": "3, 15-18, 22"}}
const express = require('express');
const Sentry = require('@sentry/node');
const { nodeProfilingIntegration } = require("@sentry/profiling-node");

const app = express();

// Sentry must be initialized as early as possible
Sentry.init({
dsn: "___DSN___",

// Add Performance Monitoring by setting tracesSampleRate and adding integration
// Set tracesSampleRate to 1.0 to capture 100% of transactions
// We recommend adjusting this value in production
tracesSampleRate: 1.0,

// Set sampling rate for profiling
// This is relative to tracesSampleRate
profilesSampleRate: 1.0,

integrations: [
expressIntegration({ app }),
nodeProfilingIntegration(),
],

// It is possible to specify the routes that should be traced by passing a router instance to the `router` option:
// expressIntegration({ router: someRouterInstance })
});

// The Sentry request handler middleware must be added before any other handlers
app.use(Sentry.Handlers.requestHandler());
// Performance Monitoring: Create spans and traces for every incoming request
app.use(Sentry.Handlers.tracingHandler());

// All controllers should live here
app.get("/", function rootHandler(req, res) {
res.end("Hello world!");
});

// The Sentry error handler middleware must be registered before any other error middleware and after all controllers
app.use(Sentry.Handlers.errorHandler());

// Optional fallthrough error handler
app.use(function onError(err, req, res, next) {
// The ID of error events captured by the Sentry error middleware is attached to `res.sentry`.
// You can use this ID for debugging, or to correlate requests with errors in Sentry.
res.statusCode = 500;
res.end(res.sentry + "\n");
});

app.listen(3000);
```

```javascript {tabTitle:ESM} {filename: instrument.mjs} {"onboardingOptions": {"performance": "10-14", "profiling": "3, 15-18, 22"}}
import express from "express";
import * as Sentry from "@sentry/node";
import { nodeProfilingIntegration } from '@sentry/profiling-node';

const app = express();

// Sentry must be initialized as early as possible
Sentry.init({
dsn: "___DSN___",

// Add Performance Monitoring by setting tracesSampleRate and adding integration
// Set tracesSampleRate to 1.0 to capture 100% of transactions
// We recommend adjusting this value in production
tracesSampleRate: 1.0,

// Set sampling rate for profiling
// This is relative to tracesSampleRate
profilesSampleRate: 1.0,

integrations: [
expressIntegration({ app }),
nodeProfilingIntegration(),
],

// It is possible to specify the routes that should be traced by passing a router instance to the `router` option:
// expressIntegration({ router: someRouterInstance })
});

// The Sentry request handler middleware must be added before any other handlers
app.use(Sentry.Handlers.requestHandler());
// Performance Monitoring: Create spans and traces for every incoming request
app.use(Sentry.Handlers.tracingHandler());

// All controllers should live here
app.get("/", function rootHandler(req, res) {
res.end("Hello world!");
});

// The Sentry error handler middleware must be registered before any other error middleware and after all controllers
app.use(Sentry.Handlers.errorHandler());

// Optional fallthrough error handler
app.use(function onError(err, req, res, next) {
// The ID of error events captured by the Sentry error middleware is attached to `res.sentry`.
// You can use this ID for debugging, or to correlate requests with errors in Sentry.
res.statusCode = 500;
res.end(res.sentry + "\n");
});

app.listen(3000);
```

## Verify

You can verify the Sentry integration by creating a route that will throw an error:

```javascript
app.get("/debug-sentry", function mainHandler(req, res) {
throw new Error("My first Sentry error!");
});
```

## `requestHandler`

`requestHandler` accepts some options that let you decide which data should be included in the events that are sent to Sentry.

Possible options are:

```javascript
// keys to be extracted from req
request?: boolean | string[]; // default: true = ['cookies', 'data', 'headers', 'method', 'query_string', 'url']

// server name
serverName?: boolean; // default: true

// generate transaction name
// path == request.path (eg. "/foo")
// methodPath == request.method + request.path (eg. "GET|/foo")
// handler == function name (eg. "fooHandler")
transaction?: boolean | 'path' | 'methodPath' | 'handler'; // default: true = 'methodPath'

// keys to be extracted from req.user
user?: boolean | string[]; // default: true = ['id', 'username', 'email']

// client ip address
ip?: boolean; // default: false

// node version
version?: boolean; // default: true

// timeout for fatal route errors to be delivered
flushTimeout?: number; // default: undefined
```

If you want to skip the server name and just add the user, you would use `requestHandler` like this:

```javascript
app.use(
Sentry.Handlers.requestHandler({
serverName: false,
user: ["email"],
})
);
```

By default, `errorHandler` will only capture errors with a status code of `500` or higher. If you want to change it, provide it with the `shouldHandleError` callback, which accepts middleware errors as its argument and decides whether an error should be sent or not by returning an appropriate boolean value.

```javascript
app.use(
Sentry.Handlers.errorHandler({
shouldHandleError(error) {
// Capture all 404 and 500 errors
if (error.status === 404 || error.status === 500) {
return true;
}
return false;
},
})
);
```

## Monitor Performance

You can monitor the performance of your Express application by setting a `tracesSampleRate`, adding the `expressIntegration`, and registering the `tracingHandler` middleware.

Spans will then be created for the following operations:

- HTTP requests made with `request`
- `get` calls using native `http` and `https` modules
- Middleware (Express.js only)

### Troubleshooting
When capturing errors locally, ensure that your project's [data filter](https://sentry.io/orgredirect/organizations/:orgslug/settings/projects/action-release/filters/) for filtering localhost events is toggled off:

![Issue alert](./img/express-data-filters.png)

This ensures that errors produced by your browser (such as HTTP-method errors) are properly captured.
10 changes: 8 additions & 2 deletions src/components/platformContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import {getCurrentGuide, getDocsRootNode, getPlatform} from 'sentry-docs/docTree
import {getFileBySlug} from 'sentry-docs/mdx';
import {mdxComponents} from 'sentry-docs/mdxComponents';
import {serverContext} from 'sentry-docs/serverContext';
import {isVersioned, stripVersion} from 'sentry-docs/versioning';
import {
getVersion,
isVersioned,
stripVersion,
VERSION_INDICATOR,
} from 'sentry-docs/versioning';

import {Include} from './include';

Expand Down Expand Up @@ -55,6 +60,7 @@ export async function PlatformContent({includePath, platform, noGuides}: Props)
const guidePath = udpatePathIfVersionedFileDoesNotExist(
`platform-includes/${includePath}/${guide}`
);

try {
doc = await getFileBySlug(guidePath);
} catch (e) {
Expand All @@ -67,7 +73,7 @@ export async function PlatformContent({includePath, platform, noGuides}: Props)
const guideObject = getCurrentGuide(rootNode, path);

const fallbackGuidePath = udpatePathIfVersionedFileDoesNotExist(
`platform-includes/${includePath}/${guideObject?.fallbackGuide}`
`platform-includes/${includePath}/${guideObject?.fallbackGuide}${VERSION_INDICATOR}${getVersion(guide || '')}`
);

if (guideObject?.fallbackGuide) {
Expand Down
5 changes: 1 addition & 4 deletions src/components/versionSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@ import {ChevronDownIcon} from '@radix-ui/react-icons';
import * as RadixSelect from '@radix-ui/react-select';
import {usePathname, useRouter} from 'next/navigation';

import {stripTrailingSlash} from 'sentry-docs/utils';
import {getLocalStorageVersionKey, VERSION_INDICATOR} from 'sentry-docs/versioning';

import styles from './style.module.scss';

import {VersionBanner} from '../versionBanner';

const stripTrailingSlash = (url: string) => {
return url.replace(/\/$/, '');
};

export function VersionSelector({versions, sdk}: {sdk: string; versions: string[]}) {
const availableVersions = ['latest', ...versions];
const router = useRouter();
Expand Down
4 changes: 4 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,7 @@ export function isTruthy<T>(value: T | undefined | null): value is T {
}

export const isLocalStorageAvailable = () => typeof localStorage !== 'undefined';

export const stripTrailingSlash = (url: string) => {
return url.replace(/\/$/, '');
};
8 changes: 7 additions & 1 deletion src/versioning.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {describe, expect, test} from 'vitest';

import {getUnversionedPath} from './versioning';
import {getUnversionedPath, getVersion} from './versioning';

describe('versioning', () => {
test('should return unversioned paths', () => {
Expand All @@ -11,4 +11,10 @@ describe('versioning', () => {
expect(getUnversionedPath(['some', 'path__v2'])).toBe('some/path/');
expect(getUnversionedPath(['some', 'path__v2'], false)).toBe('some/path');
});

test('should return version from slug', () => {
expect(getVersion('/')).toBe('');
expect(getVersion('/some/path__v7.x')).toBe('7.x');
expect(getVersion('/some/path__v7.x/')).toBe('7.x');
});
});
10 changes: 10 additions & 0 deletions src/versioning.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {stripTrailingSlash} from './utils';

export const VERSION_INDICATOR = '__v';

export const getLocalStorageVersionKey = (platform: string) => `version:${platform}`;
Expand All @@ -14,3 +16,11 @@ export const getUnversionedPath = (path: string | string[], trailingSlash = true
export const isVersioned = (path: string) => path.includes(VERSION_INDICATOR);

export const stripVersion = (path: string) => path.split(VERSION_INDICATOR)[0];

export const getVersion = (path: string) => {
const version = path.split(VERSION_INDICATOR)[1];
if (!version) {
return '';
}
return stripTrailingSlash(version);
};

0 comments on commit e56cb59

Please sign in to comment.