Skip to content

Commit da7ccfa

Browse files
authored
Merge pull request #934 from supabase/chore/add-sentry-monitoring
fix(monitoring): add sentry monitoring
2 parents c353582 + 223b83b commit da7ccfa

File tree

8 files changed

+1107
-198
lines changed

8 files changed

+1107
-198
lines changed

package-lock.json

+852-17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
"@fastify/cors": "^9.0.1",
4242
"@fastify/swagger": "^8.2.1",
4343
"@fastify/type-provider-typebox": "^3.5.0",
44+
"@sentry/node": "^9.12.0",
45+
"@sentry/profiling-node": "^9.12.0",
4446
"@sinclair/typebox": "^0.31.25",
4547
"close-with-grace": "^2.1.0",
4648
"crypto-js": "^4.0.0",

src/lib/PostgresMeta.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { init } from './db.js'
2222
import { PostgresMetaResult, PoolConfig } from './types.js'
2323

2424
export default class PostgresMeta {
25-
query: (sql: string) => Promise<PostgresMetaResult<any>>
25+
query: (sql: string, trackQueryInSentry?: boolean) => Promise<PostgresMetaResult<any>>
2626
end: () => Promise<void>
2727
columnPrivileges: PostgresMetaColumnPrivileges
2828
columns: PostgresMetaColumns

src/lib/db.ts

+195-179
Large diffs are not rendered by default.

src/server/admin-app.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import './sentry.js'
2+
import * as Sentry from '@sentry/node'
13
import { fastify, FastifyInstance, FastifyServerOptions } from 'fastify'
24
import fastifyMetrics from 'fastify-metrics'
35

46
export function build(opts: FastifyServerOptions = {}): FastifyInstance {
57
const app = fastify(opts)
8+
Sentry.setupFastifyErrorHandler(app)
69
app.register(fastifyMetrics.default, {
710
endpoint: '/metrics',
811
routeMetrics: { enabled: false },

src/server/app.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import './sentry.js'
2+
import * as Sentry from '@sentry/node'
13
import cors from '@fastify/cors'
24
import swagger from '@fastify/swagger'
35
import { fastify, FastifyInstance, FastifyServerOptions } from 'fastify'
@@ -9,6 +11,7 @@ import pkg from '#package.json' with { type: 'json' }
911

1012
export const build = (opts: FastifyServerOptions = {}): FastifyInstance => {
1113
const app = fastify({ disableRequestLogging: true, requestIdHeader: PG_META_REQ_HEADER, ...opts })
14+
Sentry.setupFastifyErrorHandler(app)
1215

1316
app.setErrorHandler((error, request, reply) => {
1417
app.log.error({ error: error.toString(), request: extractRequestForLogging(request) })

src/server/routes/query.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default async (fastify: FastifyInstance) => {
2121
const connectionString = request.headers.pg
2222

2323
const pgMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString })
24-
const { data, error } = await pgMeta.query(request.body.query)
24+
const { data, error } = await pgMeta.query(request.body.query, false)
2525
await pgMeta.end()
2626
if (error) {
2727
request.log.error({ error, request: extractRequestForLogging(request) })

src/server/sentry.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import * as Sentry from '@sentry/node'
2+
import { nodeProfilingIntegration } from '@sentry/profiling-node'
3+
4+
const sentryEnvironment = process.env.ENVIRONMENT ?? 'local'
5+
const dsn = process.env.SENTRY_DSN ?? ''
6+
7+
const captureOptions: Sentry.NodeOptions =
8+
sentryEnvironment === 'prod'
9+
? {
10+
// Tracing
11+
tracesSampleRate: 0.00001, // trace 1/10k events
12+
// Set sampling rate for profiling - this is evaluated only once per SDK.init call
13+
profilesSampleRate: 0.00001, // profile 1/10k events
14+
}
15+
: {
16+
tracesSampleRate: 0.01, // trace 1% of the events
17+
profilesSampleRate: 0.01,
18+
}
19+
20+
const sensitiveKeys = ['pg', 'x-connection-encrypted']
21+
22+
function redactSensitiveData(data: any) {
23+
if (data && typeof data === 'object') {
24+
for (const key of sensitiveKeys) {
25+
if (key in data) {
26+
data[key] = '[REDACTED]'
27+
}
28+
}
29+
}
30+
}
31+
32+
export default Sentry.init({
33+
enabled: Boolean(dsn),
34+
dsn: dsn,
35+
environment: sentryEnvironment,
36+
integrations: [nodeProfilingIntegration()],
37+
beforeSendTransaction(transaction) {
38+
if (transaction.contexts?.trace?.data) {
39+
redactSensitiveData(transaction.contexts.trace.data)
40+
}
41+
return transaction
42+
},
43+
beforeSendSpan(span) {
44+
if (span.data) {
45+
redactSensitiveData(span.data)
46+
}
47+
return span
48+
},
49+
...captureOptions,
50+
})

0 commit comments

Comments
 (0)