Skip to content

Commit a98da50

Browse files
committed
feat: add Sentry error tracking + performance tracing to backend and auth
Integrate @sentry/node for server-side error capture and performance monitoring on both Express apps. Add X-Request-Id middleware for cross-service correlation with dj-site PostHog errors. - Sentry.init() in instrument.ts (imported first to patch modules) - setupExpressErrorHandler() before existing error handlers - X-Request-Id middleware: reads from request or generates UUID, tags Sentry scope - CORS updated to allow/expose X-Request-Id header - tsup configs externalize @sentry/node (monkey-patching requires unbundled) - Deploy action passes SENTRY_RELEASE to container - Auth service gets a fallback error handler (had none previously)
1 parent 9ea9846 commit a98da50

12 files changed

Lines changed: 980 additions & 19 deletions

File tree

.github/actions/deploy-service/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ runs:
7171
docker ps -a --filter "publish=$PUBLISH_PORT" --format "{{.ID}}" | xargs -r docker rm
7272
7373
echo "Starting new Docker container..."
74-
docker run -d --name $TARGET_APP -p $PUBLISH_PORT:8080 --restart unless-stopped --env-file .env ${{ inputs.aws_ecr_uri }}/$TARGET_APP:$DEPLOY_TAG
74+
docker run -d --name $TARGET_APP -p $PUBLISH_PORT:8080 --restart unless-stopped --env-file .env -e SENTRY_RELEASE=$TARGET_APP@$DEPLOY_TAG ${{ inputs.aws_ecr_uri }}/$TARGET_APP:$DEPLOY_TAG
7575
7676
echo "Cleaning up old Docker images..."
7777
docker images --format '{{.Repository}} {{.Tag}}' | \

apps/auth/app.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
// Auth service using Express
2+
import './instrument.js';
3+
import * as Sentry from '@sentry/node';
24
import { config } from 'dotenv';
35
const dotenvResult = config();
46

57
import { auth } from '@wxyc/authentication';
68
import { toNodeHandler } from 'better-auth/node';
79
import cors from 'cors';
810
import express from 'express';
11+
import type { Request, Response, NextFunction } from 'express';
912

1013
const port = process.env.AUTH_PORT || '8080';
1114

@@ -128,6 +131,14 @@ app.get('/healthcheck', async (req, res) => {
128131
}
129132
});
130133

134+
Sentry.setupExpressErrorHandler(app);
135+
136+
// Fallback error handler
137+
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
138+
const message = err instanceof Error ? err.message : String(err);
139+
res.status(500).json({ error: message });
140+
});
141+
131142
// Create default user if needed
132143
const createDefaultUser = async () => {
133144
if (process.env.CREATE_DEFAULT_USER !== 'TRUE') return;

apps/auth/instrument.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Sentry from '@sentry/node';
2+
3+
Sentry.init({
4+
dsn: process.env.SENTRY_DSN,
5+
release: process.env.SENTRY_RELEASE,
6+
environment: process.env.NODE_ENV || 'development',
7+
tracesSampleRate: 1.0,
8+
});

apps/auth/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
"author": "Jackson Meade",
1717
"license": "MIT",
1818
"dependencies": {
19-
"@wxyc/database": "^1.0.0",
19+
"@sentry/node": "^10.40.0",
2020
"@wxyc/authentication": "^1.0.0",
21+
"@wxyc/database": "^1.0.0",
2122
"better-auth": "^1.3.23",
2223
"cors": "^2.8.5",
2324
"dotenv": "^17.2.1",

apps/auth/tsup.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default defineConfig((options) => ({
88
target: 'node20',
99
clean: true,
1010
sourcemap: true,
11-
external: ['@wxyc/database', 'better-auth', 'drizzle-orm', 'express', 'cors', 'postgres'],
11+
external: ['@wxyc/database', 'better-auth', 'drizzle-orm', 'express', 'cors', 'postgres', '@sentry/node'],
1212
env: {
1313
NODE_ENV: process.env.NODE_ENV || 'development',
1414
},

apps/backend/app.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import './instrument.js';
2+
import * as Sentry from '@sentry/node';
13
import express from 'express';
24
import cors from 'cors';
35
import swaggerUi from 'swagger-ui-express';
@@ -13,6 +15,7 @@ import { request_line_route } from './routes/requestLine.route.js';
1315
import { showMemberMiddleware } from './middleware/checkShowMember.js';
1416
import { activeShow } from './middleware/checkActiveShow.js';
1517
import errorHandler from './middleware/errorHandler.js';
18+
import { requestIdMiddleware } from './middleware/requestId.js';
1619
import { requirePermissions } from '@wxyc/authentication';
1720

1821
const port = process.env.PORT || 8080;
@@ -23,12 +26,16 @@ app.set('trust proxy', true);
2326
//Interpret parse json into js objects
2427
app.use(express.json());
2528

29+
// Cross-service request correlation
30+
app.use(requestIdMiddleware);
31+
2632
//CORS
2733
app.use(
2834
cors({
2935
origin: process.env.FRONTEND_SOURCE || '*',
3036
methods: ['GET', 'POST', 'DELETE', 'PATCH'],
31-
allowedHeaders: ['Content-Type', 'Authorization'],
37+
allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-Id'],
38+
exposedHeaders: ['X-Request-Id'],
3239
credentials: true,
3340
})
3441
);
@@ -69,6 +76,7 @@ app.get('/healthcheck', async (req, res) => {
6976
res.json({ message: 'Healthy!' });
7077
});
7178

79+
Sentry.setupExpressErrorHandler(app);
7280
app.use(errorHandler);
7381

7482
const server = app.listen(port, () => {

apps/backend/instrument.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Sentry from '@sentry/node';
2+
3+
Sentry.init({
4+
dsn: process.env.SENTRY_DSN,
5+
release: process.env.SENTRY_RELEASE,
6+
environment: process.env.NODE_ENV || 'development',
7+
tracesSampleRate: 1.0,
8+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as Sentry from '@sentry/node';
2+
import type { Request, Response, NextFunction } from 'express';
3+
4+
export function requestIdMiddleware(req: Request, res: Response, next: NextFunction) {
5+
const requestId = req.get('X-Request-Id') || crypto.randomUUID();
6+
7+
res.setHeader('X-Request-Id', requestId);
8+
Sentry.getCurrentScope().setTag('request_id', requestId);
9+
10+
next();
11+
}

apps/backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"author": "AyBruno",
1717
"license": "MIT",
1818
"dependencies": {
19+
"@sentry/node": "^10.40.0",
1920
"@wxyc/authentication": "*",
2021
"@wxyc/database": "*",
2122
"async-mutex": "^0.5.0",

apps/backend/tsup.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export default defineConfig((options) => ({
1111
format: ['esm'],
1212
outDir: 'dist',
1313
clean: true,
14+
sourcemap: true,
15+
external: ['@sentry/node'],
1416
onSuccess: options.watch ? 'node ./dist/app.js' : undefined,
1517
minify: !options.watch,
1618

0 commit comments

Comments
 (0)