Skip to content

Commit e5c5fec

Browse files
authored
Merge pull request #34 from getsentry/sig/caches
feat(redis-cache): Add redis cache example app
2 parents 96e6364 + ea47b40 commit e5c5fec

File tree

5 files changed

+259
-0
lines changed

5 files changed

+259
-0
lines changed

apps/express_redis-cache/package.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "express_redis-cache-test-application",
3+
"version": "1.0.0",
4+
"main": "dist/main.js",
5+
"directories": {
6+
"lib": "lib"
7+
},
8+
"scripts": {
9+
"build": "tsc",
10+
"start": "yarn build && node dist/app.js",
11+
"clean": "npx rimraf node_modules,pnpm-lock.yaml"
12+
},
13+
"license": "MIT",
14+
"volta": {
15+
"extends": "../../package.json"
16+
},
17+
"dependencies": {
18+
"@sentry/node": "8.9.2",
19+
"dotenv": "^16.4.5",
20+
"express": "^4.19.2",
21+
"ioredis": "^4.28.5",
22+
"redis": "^4.6.14"
23+
},
24+
"devDependencies": {
25+
"@types/express": "^4",
26+
"@types/ioredis": "4.28.10",
27+
"@types/redis": "^4.0.11"
28+
}
29+
}

apps/express_redis-cache/src/app.ts

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import './instrument';
2+
3+
import * as Sentry from '@sentry/node';
4+
import express from 'express';
5+
import Redis from 'ioredis';
6+
import {
7+
createClient,
8+
type RedisClientType,
9+
type RedisDefaultModules,
10+
type RedisModules,
11+
type RedisFunctions,
12+
type RedisScripts,
13+
} from 'redis';
14+
import dotenv from 'dotenv';
15+
16+
dotenv.config({ path: './../../.env' });
17+
18+
declare global {
19+
namespace globalThis {
20+
var transactionIds: string[];
21+
}
22+
}
23+
24+
const app = express();
25+
const port = 3030;
26+
27+
Sentry.setupExpressErrorHandler(app);
28+
29+
// IOREDIS -----------------------------------------------------
30+
const redis = new Redis({
31+
port: 6379,
32+
host: 'localhost',
33+
});
34+
35+
app.get('/ioredis-get', function (req, res) {
36+
redis.get('cache:1').then(value => {
37+
res.send({ 'cache:1': value });
38+
});
39+
});
40+
41+
app.get('/ioredis-mget', function (req, res) {
42+
redis.mget(['cache:1', 'cache:2', 'cache:3']).then(value => {
43+
res.send({ 'cache:': value });
44+
});
45+
});
46+
47+
app.get('/ioredis-set', async function (req, res) {
48+
await redis.set('cache:io1.2', 'example-value-1');
49+
await redis.set('cache:io2.2', 'example-value-2');
50+
await redis.set('cache:io3.2', 'example-value-3');
51+
52+
res.send('Redis SET');
53+
});
54+
55+
app.get('/ioredis-setex', async function (req, res) {
56+
await redis.set('cache:test-key-set-EX', 'test-value34', 'EX', 10);
57+
await redis.setex('cache:test-key-setex', 10, 'test-value434');
58+
59+
res.send('Redis SET');
60+
});
61+
62+
// REDIS -----------------------------------------------------
63+
async function initializeClient() {
64+
return createClient().connect();
65+
}
66+
67+
let redisClient: RedisClientType<RedisDefaultModules & RedisModules, RedisFunctions, RedisScripts>;
68+
69+
(async function () {
70+
redisClient = await initializeClient();
71+
})();
72+
73+
app.get('/redis-get', function (req, res) {
74+
redisClient.get('cache:1').then((value: any) => {
75+
res.send({ 'cache:1': value });
76+
});
77+
});
78+
79+
app.get('/redis-mget', function (req, res) {
80+
redisClient
81+
.mGet(['redis-test-key', 'redis-cache:test-key', 'redis-cache:unavailable-data'])
82+
.then((value: any) => {
83+
res.send({ 'cache:': value });
84+
});
85+
});
86+
87+
app.get('/redis-set', async function (req, res) {
88+
await redisClient.set('cache:1.2', 'example-value-1');
89+
await redisClient.set('cache:2.2', 'example-value-2');
90+
await redisClient.set('cache:3.2', 'example-value-3');
91+
92+
res.send('Redis SET');
93+
});
94+
95+
app.get('/redis-setex', async function (req, res) {
96+
await redisClient.set('cache:test-key-set-EX', 'test-value34', { EX: 10 });
97+
await redisClient.setEx('cache:test-key-setex', 10, 'test-value434');
98+
99+
res.send('Redis SET');
100+
});
101+
102+
// ----------------------------------------------------------
103+
104+
app.get('/test-success', function (req, res) {
105+
res.send({ version: 'v1' });
106+
});
107+
108+
app.get('/test-error', async function (req, res) {
109+
const exceptionId = Sentry.captureException(new Error('This is an error'));
110+
111+
await Sentry.flush(2000);
112+
113+
res.send({ exceptionId });
114+
});
115+
116+
app.get('/test-param-success/:param', function (req, res) {
117+
res.send({ paramWas: req.params.param });
118+
});
119+
120+
app.get('/test-param-error/:param', async function (req, res) {
121+
const exceptionId = Sentry.captureException(new Error('This is an error'));
122+
123+
await Sentry.flush(2000);
124+
125+
res.send({ exceptionId, paramWas: req.params.param });
126+
});
127+
128+
app.get('/test-success-manual', async function (req, res) {
129+
Sentry.startSpan({ name: 'test-transaction', op: 'e2e-test' }, () => {
130+
Sentry.startSpan({ name: 'test-span' }, () => undefined);
131+
});
132+
133+
await Sentry.flush();
134+
135+
res.send({
136+
transactionIds: global.transactionIds || [],
137+
});
138+
});
139+
140+
app.get('/test-error-manual', async function (req, res) {
141+
Sentry.startSpan({ name: 'test-transaction', op: 'e2e-test' }, () => {
142+
Sentry.startSpan({ name: 'test-span' }, () => {
143+
Sentry.captureException(new Error('This is an error'));
144+
});
145+
});
146+
147+
await Sentry.flush(2000);
148+
149+
res.send({
150+
transactionIds: global.transactionIds || [],
151+
});
152+
});
153+
154+
app.get('/test-local-variables-uncaught', function (req, res) {
155+
const randomVariableToRecord = 'LOCAL VARIABLE';
156+
throw new Error(`Uncaught Local Variable Error - ${JSON.stringify({ randomVariableToRecord })}`);
157+
});
158+
159+
app.get('/test-local-variables-caught', function (req, res) {
160+
const randomVariableToRecord = 'LOCAL VARIABLE';
161+
162+
let exceptionId: string;
163+
try {
164+
throw new Error('Local Variable Error');
165+
} catch (e) {
166+
exceptionId = Sentry.captureException(e);
167+
}
168+
169+
res.send({ exceptionId, randomVariableToRecord });
170+
});
171+
172+
// @ts-ignore
173+
app.use(function onError(err, req, res, next) {
174+
// The error id is attached to `res.sentry` to be returned
175+
// and optionally displayed to the user for support.
176+
res.statusCode = 500;
177+
res.end(res.sentry + '\n');
178+
});
179+
180+
app.listen(port, () => {
181+
console.log(`Example app listening on port ${port}`);
182+
});
183+
184+
Sentry.addEventProcessor(event => {
185+
global.transactionIds = global.transactionIds || [];
186+
187+
if (event.type === 'transaction') {
188+
const eventId = event.event_id;
189+
190+
if (eventId) {
191+
global.transactionIds.push(eventId);
192+
}
193+
}
194+
195+
return event;
196+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as Sentry from '@sentry/node';
2+
import dotenv from 'dotenv';
3+
4+
dotenv.config({ path: './../../.env' });
5+
6+
Sentry.init({
7+
environment: 'qa', // dynamic sampling bias to keep transactions
8+
dsn: process.env.SENTRY_DSN,
9+
includeLocalVariables: true,
10+
tunnel: `http://localhost:3031/`, // proxy server
11+
tracesSampleRate: 1,
12+
integrations: [Sentry.redisIntegration({ cachePrefixes: ['cache:'] })],
13+
beforeSendTransaction: transaction => {
14+
console.log(
15+
'Trace',
16+
// @ts-ignore
17+
transaction.spans.map(span => span),
18+
);
19+
return transaction;
20+
},
21+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"include": ["src/**/*.ts"],
4+
"compilerOptions": {
5+
"outDir": "dist",
6+
"module": "commonJS",
7+
"target": "es2015"
8+
}
9+
}

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"scripts": {
1111
"start:proxy-server": "yarn workspace event-proxy-server run start",
1212
"start:proxy-server:express_profiling-crons": "APP_NAME=express_profiling-crons yarn workspace event-proxy-server run start",
13+
"start:proxy-server:express_redis-cache": "APP_NAME=express_redis-cache yarn workspace event-proxy-server run start",
1314
"start:proxy-server:express": "APP_NAME=express yarn workspace event-proxy-server run start",
1415
"start:proxy-server:fastify": "APP_NAME=fastify yarn workspace event-proxy-server run start",
1516
"start:proxy-server:connect": "APP_NAME=connect yarn workspace event-proxy-server run start",
@@ -22,6 +23,7 @@
2223
"start:proxy-server:sveltekit-2": "APP_NAME=sveltekit-2 yarn workspace event-proxy-server run start",
2324
"start:proxy-server:remix": "APP_NAME=remix yarn workspace event-proxy-server run start",
2425
"start:app:express_profiling-crons": "yarn workspace express_profiling-crons-test-application run start",
26+
"start:app:express_redis-cache": "yarn workspace express_redis-cache-test-application run start",
2527
"start:app:express": "yarn workspace express-test-application run start",
2628
"start:app:fastify": "yarn workspace fastify-test-application run start",
2729
"start:app:connect": "APP_NAME=connect yarn workspace connect-test-application run start",
@@ -34,6 +36,7 @@
3436
"start:app:sveltekit-2": "yarn workspace sveltekit-2-test-application run dev",
3537
"start:app:remix": "yarn workspace remix-test-application run dev",
3638
"start:express_profiling-crons": "run-p start:proxy-server:express_profiling-crons start:app:express_profiling-crons",
39+
"start:express_redis-cache": "run-p start:proxy-server:express_redis-cache start:app:express_redis-cache",
3740
"start:express": "run-p start:proxy-server:express start:app:express",
3841
"start:fastify": "run-p start:proxy-server:fastify start:app:fastify",
3942
"start:connect": "run-p start:proxy-server:connect start:app:connect",
@@ -52,6 +55,7 @@
5255
"workspaces": [
5356
"utils/event-proxy-server",
5457
"apps/express_profiling-crons",
58+
"apps/express_redis-cache",
5559
"apps/express",
5660
"apps/fastify",
5761
"apps/connect",

0 commit comments

Comments
 (0)