Skip to content

Commit

Permalink
Error reporting in Cloudflare functions with Sentry (#3068)
Browse files Browse the repository at this point in the history
Add Sentry logging for /donation/* Cloudflare Pages functions routes.

At the time of writing, neither [bugsnag](bugsnag/bugsnag-js#637) nor [slack](slackapi/node-slack-sdk#1335) have official Cloudflare worker integrations.

Given the criticality of these routes, I favoured reliability and introduced a new logging app which has official Cloudflare support: https://developers.cloudflare.com/pages/functions/plugins/sentry/

We should switch to bugsnag whenever official support is available.

Testing:
- (ENV and SENTRY_DSN secrets are already defined for the preview environment in Cloudflare's "owid" project)
- temporarily disable the [Cloudflare Access rule](https://one.dash.cloudflare.com/078fcdfed9955087315dd86792e71a7e/access/apps/edit/d8c658c3-fd20-477e-ac20-e7ed7fd656de?tab=overview) by setting its  subdomain to `temp`
- send an empty JSON to https://donate.owid.pages.dev/donation/donate

-> an error is logged in sentry and an alert email is sent to [TBD]@ourworldindata.org (this will be configured when the use of Sentry is confirmed). Slack integration is only available on paid plans.
  • Loading branch information
mlbrgl authored Jan 11, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents efe4bd7 + 7b34197 commit d4723ca
Showing 6 changed files with 127 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .dev.vars.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# env vars for local development of /functions/donation/donate.ts Cloudflare pages function
# rename to .dev.vars and fill in the values from the homonymous "(dev)" keys in 1Password

ENV=development

# https://dashboard.stripe.com/test/apikeys
# required by /donation/donate
STRIPE_SECRET_KEY=
@@ -14,3 +16,6 @@ RECAPTCHA_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
MAILGUN_DOMAIN=
MAILGUN_SENDING_KEY=

# optional
SENTRY_DSN=
31 changes: 31 additions & 0 deletions functions/donation/_middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import sentryPlugin from "@cloudflare/pages-plugin-sentry"
import { CaptureConsole } from "@sentry/integrations"

interface SentryEnvVars {
SENTRY_DSN: string
ENV: "production" | "development"
}

const hasSentryEnvVars = (env: any): env is SentryEnvVars => {
return (
!!env.SENTRY_DSN &&
!!env.ENV &&
["production", "development"].includes(env.ENV)
)
}

export const onRequest: PagesFunction = (context) => {
if (!hasSentryEnvVars(context.env)) {
console.error(
"Missing Sentry environment variables. Continuing without error logging..."
)
// Gracefully continue if Sentry is not configured.
return context.next()
}

return sentryPlugin({
dsn: context.env.SENTRY_DSN,
integrations: [new CaptureConsole()],
environment: context.env.ENV,
})(context)
}
5 changes: 3 additions & 2 deletions functions/donation/donate.ts
Original file line number Diff line number Diff line change
@@ -11,13 +11,12 @@ import { Value } from "@sinclair/typebox/value"
import { DEFAULT_HEADERS, CORS_HEADERS } from "./_utils/constants.js"

interface DonateEnvVars {
ASSETS: Fetcher
STRIPE_SECRET_KEY: string
RECAPTCHA_SECRET_KEY: string
}

const hasDonateEnvVars = (env: any): env is DonateEnvVars => {
return !!env.ASSETS && !!env.STRIPE_SECRET_KEY && !!env.RECAPTCHA_SECRET_KEY
return !!env.STRIPE_SECRET_KEY && !!env.RECAPTCHA_SECRET_KEY
}

// This function is called when the request is a preflight request ("OPTIONS").
@@ -79,6 +78,8 @@ export const onRequestPost: PagesFunction = async ({
status: 200,
})
} catch (error) {
// Reporting to Sentry through the CaptureConsole integration in _middleware.ts
console.error("Error in donation/donate.ts", error)
return new Response(
JSON.stringify({ error: stringifyUnknownError(error) }),
{
6 changes: 2 additions & 4 deletions functions/donation/thank-you.ts
Original file line number Diff line number Diff line change
@@ -13,18 +13,15 @@ interface MessageData {
}

type ThankYouEnvVars = {
ASSETS: Fetcher
STRIPE_WEBHOOK_SECRET: string
STRIPE_SECRET_KEY: string
} & MailgunEnvVars

const hasThankYouEnvVars = (env: unknown): env is ThankYouEnvVars => {
return (
typeof env === "object" &&
"ASSETS" in env &&
"STRIPE_WEBHOOK_SECRET" in env &&
"STRIPE_SECRET_KEY" in env &&
!!env.ASSETS &&
!!env.STRIPE_WEBHOOK_SECRET &&
!!env.STRIPE_SECRET_KEY
)
@@ -132,7 +129,8 @@ export const onRequestPost: PagesFunction = async ({
status: 200,
})
} catch (error) {
console.error(error)
// Reporting to Sentry through the CaptureConsole integration in _middleware.ts
console.error("Error in donation/thank-you.ts", error)
return new Response(
JSON.stringify({ error: stringifyUnknownError(error) }),
{
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -55,6 +55,7 @@
"@bugsnag/js": "^7.20.0",
"@bugsnag/plugin-express": "^7.19.0",
"@bugsnag/plugin-react": "^7.19.0",
"@cloudflare/pages-plugin-sentry": "^1.1.1",
"@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-brands-svg-icons": "^6.4.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
85 changes: 85 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -1958,6 +1958,15 @@ __metadata:
languageName: node
linkType: hard

"@cloudflare/pages-plugin-sentry@npm:^1.1.1":
version: 1.1.1
resolution: "@cloudflare/pages-plugin-sentry@npm:1.1.1"
dependencies:
toucan-js: "npm:^3.0.0"
checksum: a9058ac31f662b6f6a2176f92e8befc07d82b28e72c73904600f173aa9ed782953cb0b3cdd82a23e84f6be7ea2c67ae81e377920206825083495c8b66075d582
languageName: node
linkType: hard

"@cloudflare/workerd-darwin-64@npm:1.20231218.0":
version: 1.20231218.0
resolution: "@cloudflare/workerd-darwin-64@npm:1.20231218.0"
@@ -3814,6 +3823,44 @@ __metadata:
languageName: node
linkType: hard

"@sentry/core@npm:7.76.0":
version: 7.76.0
resolution: "@sentry/core@npm:7.76.0"
dependencies:
"@sentry/types": "npm:7.76.0"
"@sentry/utils": "npm:7.76.0"
checksum: f6b524036a0bfe875868882d31a822ae71e598a33da1c6bb6ebf62da0520ac2b6dca5e28c70e8fb7570751ae1ebd590fc3c5b5fcc49f857753dcb739bfa7f06e
languageName: node
linkType: hard

"@sentry/integrations@npm:7.76.0":
version: 7.76.0
resolution: "@sentry/integrations@npm:7.76.0"
dependencies:
"@sentry/core": "npm:7.76.0"
"@sentry/types": "npm:7.76.0"
"@sentry/utils": "npm:7.76.0"
localforage: "npm:^1.8.1"
checksum: 8f307c81fd0eec8ee250061a311bc0b5d7f0839a069b6939d0576aee74f9d87cd332e6376d41b75dc4c789aaeedba11087f28e7950f1769884bbf05a3898a3d0
languageName: node
linkType: hard

"@sentry/types@npm:7.76.0":
version: 7.76.0
resolution: "@sentry/types@npm:7.76.0"
checksum: 67a4cde848be8be534b42608f930da91fcb3e6e9f38843d9fc670d5bd531f7d13f826fb24f0ef6f88f1e3e603e8377ea1412b2fd8097ba641746ad4243b90936
languageName: node
linkType: hard

"@sentry/utils@npm:7.76.0":
version: 7.76.0
resolution: "@sentry/utils@npm:7.76.0"
dependencies:
"@sentry/types": "npm:7.76.0"
checksum: 141e40b3ed4da50b2b6ceb11d58e557db34431d0ad8837af21636de5692275e1fddb0b920bb20c9ac2ead67c28a446fc76f451eefd10304a4aea85f9f2b32242
languageName: node
linkType: hard

"@sigstore/bundle@npm:^1.1.0":
version: 1.1.0
resolution: "@sigstore/bundle@npm:1.1.0"
@@ -10752,6 +10799,7 @@ __metadata:
"@bugsnag/js": "npm:^7.20.0"
"@bugsnag/plugin-express": "npm:^7.19.0"
"@bugsnag/plugin-react": "npm:^7.19.0"
"@cloudflare/pages-plugin-sentry": "npm:^1.1.1"
"@cloudflare/workers-types": "npm:^4.20230821.0"
"@fortawesome/fontawesome-svg-core": "npm:^6.4.2"
"@fortawesome/free-brands-svg-icons": "npm:^6.4.2"
@@ -11478,6 +11526,13 @@ __metadata:
languageName: node
linkType: hard

"immediate@npm:~3.0.5":
version: 3.0.6
resolution: "immediate@npm:3.0.6"
checksum: f9b3486477555997657f70318cc8d3416159f208bec4cca3ff3442fd266bc23f50f0c9bd8547e1371a6b5e82b821ec9a7044a4f7b944798b25aa3cc6d5e63e62
languageName: node
linkType: hard

"immutable@npm:^3.8.2":
version: 3.8.2
resolution: "immutable@npm:3.8.2"
@@ -13467,6 +13522,15 @@ __metadata:
languageName: node
linkType: hard

"lie@npm:3.1.1":
version: 3.1.1
resolution: "lie@npm:3.1.1"
dependencies:
immediate: "npm:~3.0.5"
checksum: c2c7d9dcc3a9aae641f41cde4e2e2cd571e4426b1f5915862781d77776672dcbca43461e16f4d382c9a300825c15e1a4923f1def3a5568d97577e077a3cecb44
languageName: node
linkType: hard

"liftoff@npm:3.1.0":
version: 3.1.0
resolution: "liftoff@npm:3.1.0"
@@ -13556,6 +13620,15 @@ __metadata:
languageName: node
linkType: hard

"localforage@npm:^1.8.1":
version: 1.10.0
resolution: "localforage@npm:1.10.0"
dependencies:
lie: "npm:3.1.1"
checksum: d5c44be3a09169b013a3ebe252e678aaeb6938ffe72e9e12c199fd4307c1ec9d1a057ac2dfdfbb1379dfeec467a34ad0fc3ecd27489a2c43a154fb72b2822542
languageName: node
linkType: hard

"locate-path@npm:^2.0.0":
version: 2.0.0
resolution: "locate-path@npm:2.0.0"
@@ -19446,6 +19519,18 @@ __metadata:
languageName: node
linkType: hard

"toucan-js@npm:^3.0.0":
version: 3.3.1
resolution: "toucan-js@npm:3.3.1"
dependencies:
"@sentry/core": "npm:7.76.0"
"@sentry/integrations": "npm:7.76.0"
"@sentry/types": "npm:7.76.0"
"@sentry/utils": "npm:7.76.0"
checksum: 6985980551f68170bca01e932f32b06ec14698bcd661188084cfa741a98a789ac3f5195ec2da3b535b8a254fadc2c8a018f3a334b6788884178a624e337a5179
languageName: node
linkType: hard

"tough-cookie@npm:^4.1.2":
version: 4.1.3
resolution: "tough-cookie@npm:4.1.3"

0 comments on commit d4723ca

Please sign in to comment.