From f4af291b6f2f3113cfc03e25a4c405fa0ffad309 Mon Sep 17 00:00:00 2001 From: Akshay Date: Thu, 9 Sep 2021 22:14:34 +0530 Subject: [PATCH] Chore: Application monitoring support with sentry (#702) * setup sentry for server * setup sentry for client * update docs for sentry dns * explicitly specify apm vendor * add module for sentry * revise directory struct and make sentry debuggable * setup csp and trace headers for sentry * whitelist csp for all sentry urls * change senrty error sample rate to 50% * make sentry send all errors from backend --- .env.example | 5 + docs/docs/deployment/env-vars.md | 45 ++- frontend/package-lock.json | 292 +++++++++++++++++- frontend/package.json | 2 + frontend/src/index.jsx | 12 +- server/package-lock.json | 266 ++++++++++++++++ server/package.json | 2 + server/src/app.module.ts | 55 +++- server/src/interceptors/sentry.interceptor.ts | 40 +++ server/src/main.ts | 2 +- .../observability/sentry/sentry.module.ts | 32 ++ server/src/services/app_config.service.ts | 12 +- server/src/services/sentry.service.ts | 55 ++++ 13 files changed, 784 insertions(+), 36 deletions(-) create mode 100644 server/src/interceptors/sentry.interceptor.ts create mode 100644 server/src/modules/observability/sentry/sentry.module.ts create mode 100644 server/src/services/sentry.service.ts diff --git a/.env.example b/.env.example index 9b3a8caab9..fb2e0d8ab3 100644 --- a/.env.example +++ b/.env.example @@ -27,3 +27,8 @@ SMTP_ADDRESS= # DISABLE USER SIGNUPS (true or false). Default: true DISABLE_SIGNUPS= + +# OBSERVABILITY +APM_VENDOR= +SENTRY_DNS= +SENTRY_DEBUG= diff --git a/docs/docs/deployment/env-vars.md b/docs/docs/deployment/env-vars.md index 7f5dbb9da7..6e6adbb26f 100644 --- a/docs/docs/deployment/env-vars.md +++ b/docs/docs/deployment/env-vars.md @@ -86,14 +86,6 @@ If your ToolJet installation needs access to datasources such as Google sheets, | GOOGLE_CLIENT_ID | client id | | GOOGLE_CLIENT_SECRET | client secret | -## ToolJet client - -#### Server URL ( optional ) - -| variable | description | -| ----------- | ----------- | -| TOOLJET_SERVER_URL | the URL of ToolJet server ( eg: https://server.tooljet.io ) | - #### Google maps configuration ( optional ) If your ToolJet installation requires `Maps` widget, you need to create an API key for Google Maps API. @@ -101,3 +93,40 @@ If your ToolJet installation requires `Maps` widget, you need to create an API k | variable | description | | ----------- | ----------- | | GOOGLE_MAPS_API_KEY | Google maps API key | + +#### APM VENDOR ( optional ) + +Specify application monitoring vendor. Currently supported values - `sentry`. + +| variable | description | +| ----------- | ----------- | +| APM VENDOR | Application performance monitoring vendor | + +#### SENTRY DNS ( optional ) + +DSN tells a Sentry SDK where to send events so the events are associated with the correct project + +#### SENTRY DEBUG ( optional ) + +Prints logs for sentry. Supported values: `true` | `false` +Default value is `false` + +#### Server URL ( optional) +This is used to set up for CSP headers and put trace info to be used with APM vendors. + +| variable | description | +| ----------- | ----------- | +| TOOLJET_SERVER_URL | the URL of ToolJet server ( eg: https://server.tooljet.io ) | + + +#### RELEASE VERSION ( optional) +Once set any APM provider that supports segregation with releases will track it. + +## ToolJet client + +#### Server URL ( optionally required ) +This is required when client is built separately. + +| variable | description | +| ----------- | ----------- | +| TOOLJET_SERVER_URL | the URL of ToolJet server ( eg: https://server.tooljet.io ) | diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9acb5bcc78..16b3b7d7d8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -5,7 +5,6 @@ "requires": true, "packages": { "": { - "name": "frontend", "version": "0.1.0", "dependencies": { "@babel/core": "^7.4.3", @@ -13,6 +12,8 @@ "@babel/preset-env": "^7.4.3", "@babel/preset-react": "^7.0.0", "@react-google-maps/api": "^2.1.1", + "@sentry/react": "^6.12.0", + "@sentry/tracing": "^6.12.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", @@ -62,6 +63,7 @@ "rxjs": "^6.3.3", "semver": "^5.7.1", "tinycolor2": "^1.4.2", + "uuid": "8.3.2", "webpack-cli": "^3.3.0", "yup": "^0.27.0" }, @@ -1746,6 +1748,16 @@ "node": ">= 6" } }, + "node_modules/@cypress/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/@cypress/webpack-dev-server": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@cypress/webpack-dev-server/-/webpack-dev-server-1.3.1.tgz", @@ -2460,6 +2472,115 @@ } } }, + "node_modules/@sentry/browser": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.12.0.tgz", + "integrity": "sha512-wsJi1NLOmfwtPNYxEC50dpDcVY7sdYckzwfqz1/zHrede1mtxpqSw+7iP4bHADOJXuF+ObYYTHND0v38GSXznQ==", + "dependencies": { + "@sentry/core": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/core": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.12.0.tgz", + "integrity": "sha512-mU/zdjlzFHzdXDZCPZm8OeCw7c9xsbL49Mq0TrY0KJjLt4CJBkiq5SDTGfRsenBLgTedYhe5Z/J8Z+xVVq+MfQ==", + "dependencies": { + "@sentry/hub": "6.12.0", + "@sentry/minimal": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.12.0.tgz", + "integrity": "sha512-yR/UQVU+ukr42bSYpeqvb989SowIXlKBanU0cqLFDmv5LPCnaQB8PGeXwJAwWhQgx44PARhmB82S6Xor8gYNxg==", + "dependencies": { + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.12.0.tgz", + "integrity": "sha512-r3C54Q1KN+xIqUvcgX9DlcoWE7ezWvFk2pSu1Ojx9De81hVqR9u5T3sdSAP2Xma+um0zr6coOtDJG4WtYlOtsw==", + "dependencies": { + "@sentry/hub": "6.12.0", + "@sentry/types": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/react": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.12.0.tgz", + "integrity": "sha512-E8Nw9PPzP/EyMy64ksr9xcyYYlBmUA5ROnkPQp7o5wF0xf5/J+nMS1tQdyPnLQe2KUgHlN4kVs2HHft1m7mSYQ==", + "dependencies": { + "@sentry/browser": "6.12.0", + "@sentry/minimal": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "hoist-non-react-statics": "^3.3.2", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "react": "15.x || 16.x || 17.x" + } + }, + "node_modules/@sentry/tracing": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.12.0.tgz", + "integrity": "sha512-u10QHNknPBzbWSUUNMkvuH53sQd5NaBo6YdNPj4p5b7sE7445Sh0PwBpRbY3ZiUUiwyxV59fx9UQ4yVnPGxZQA==", + "dependencies": { + "@sentry/hub": "6.12.0", + "@sentry/minimal": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/types": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.12.0.tgz", + "integrity": "sha512-urtgLzE4EDMAYQHYdkgC0Ei9QvLajodK1ntg71bGn0Pm84QUpaqpPDfHRU+i6jLeteyC7kWwa5O5W1m/jrjGXA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.12.0.tgz", + "integrity": "sha512-oRHQ7TH5TSsJqoP9Gqq25Jvn9LKexXfAh/OoKwjMhYCGKGhqpDNUIZVgl9DWsGw5A5N5xnQyLOxDfyRV5RshdA==", + "dependencies": { + "@sentry/types": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@sheerun/mutationobserver-shim": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", @@ -17790,6 +17911,15 @@ "object.getownpropertydescriptors": "^2.0.3" } }, + "node_modules/react-scripts/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/react-scripts/node_modules/webpack-dev-server": { "version": "3.11.0", "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", @@ -18395,6 +18525,15 @@ "request": "^2.34" } }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -19395,6 +19534,16 @@ "ms": "^2.1.1" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -20951,12 +21100,11 @@ } }, "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "bin": { - "uuid": "bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache": { @@ -21533,6 +21681,15 @@ "node": ">= 6" } }, + "node_modules/webpack-log/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/webpack-manifest-plugin": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz", @@ -23511,6 +23668,14 @@ "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "@cypress/webpack-dev-server": { @@ -24090,6 +24255,88 @@ "any-observable": "^0.3.0" } }, + "@sentry/browser": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.12.0.tgz", + "integrity": "sha512-wsJi1NLOmfwtPNYxEC50dpDcVY7sdYckzwfqz1/zHrede1mtxpqSw+7iP4bHADOJXuF+ObYYTHND0v38GSXznQ==", + "requires": { + "@sentry/core": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + } + }, + "@sentry/core": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.12.0.tgz", + "integrity": "sha512-mU/zdjlzFHzdXDZCPZm8OeCw7c9xsbL49Mq0TrY0KJjLt4CJBkiq5SDTGfRsenBLgTedYhe5Z/J8Z+xVVq+MfQ==", + "requires": { + "@sentry/hub": "6.12.0", + "@sentry/minimal": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + } + }, + "@sentry/hub": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.12.0.tgz", + "integrity": "sha512-yR/UQVU+ukr42bSYpeqvb989SowIXlKBanU0cqLFDmv5LPCnaQB8PGeXwJAwWhQgx44PARhmB82S6Xor8gYNxg==", + "requires": { + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + } + }, + "@sentry/minimal": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.12.0.tgz", + "integrity": "sha512-r3C54Q1KN+xIqUvcgX9DlcoWE7ezWvFk2pSu1Ojx9De81hVqR9u5T3sdSAP2Xma+um0zr6coOtDJG4WtYlOtsw==", + "requires": { + "@sentry/hub": "6.12.0", + "@sentry/types": "6.12.0", + "tslib": "^1.9.3" + } + }, + "@sentry/react": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.12.0.tgz", + "integrity": "sha512-E8Nw9PPzP/EyMy64ksr9xcyYYlBmUA5ROnkPQp7o5wF0xf5/J+nMS1tQdyPnLQe2KUgHlN4kVs2HHft1m7mSYQ==", + "requires": { + "@sentry/browser": "6.12.0", + "@sentry/minimal": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "hoist-non-react-statics": "^3.3.2", + "tslib": "^1.9.3" + } + }, + "@sentry/tracing": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.12.0.tgz", + "integrity": "sha512-u10QHNknPBzbWSUUNMkvuH53sQd5NaBo6YdNPj4p5b7sE7445Sh0PwBpRbY3ZiUUiwyxV59fx9UQ4yVnPGxZQA==", + "requires": { + "@sentry/hub": "6.12.0", + "@sentry/minimal": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + } + }, + "@sentry/types": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.12.0.tgz", + "integrity": "sha512-urtgLzE4EDMAYQHYdkgC0Ei9QvLajodK1ntg71bGn0Pm84QUpaqpPDfHRU+i6jLeteyC7kWwa5O5W1m/jrjGXA==" + }, + "@sentry/utils": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.12.0.tgz", + "integrity": "sha512-oRHQ7TH5TSsJqoP9Gqq25Jvn9LKexXfAh/OoKwjMhYCGKGhqpDNUIZVgl9DWsGw5A5N5xnQyLOxDfyRV5RshdA==", + "requires": { + "@sentry/types": "6.12.0", + "tslib": "^1.9.3" + } + }, "@sheerun/mutationobserver-shim": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", @@ -36218,6 +36465,11 @@ "object.getownpropertydescriptors": "^2.0.3" } }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, "webpack-dev-server": { "version": "3.11.0", "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", @@ -36653,6 +36905,13 @@ "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } } }, "request-progress": { @@ -37469,6 +37728,14 @@ "faye-websocket": "^0.11.3", "uuid": "^3.4.0", "websocket-driver": "^0.7.4" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "sockjs-client": { @@ -38707,9 +38974,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { "version": "2.3.0", @@ -39254,6 +39521,13 @@ "requires": { "ansi-colors": "^3.0.0", "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } } }, "webpack-manifest-plugin": { diff --git a/frontend/package.json b/frontend/package.json index 42834ee8b7..d272f99ff8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,6 +8,8 @@ "@babel/preset-env": "^7.4.3", "@babel/preset-react": "^7.0.0", "@react-google-maps/api": "^2.1.1", + "@sentry/react": "^6.12.0", + "@sentry/tracing": "^6.12.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx index f7c067084e..53b503c36a 100755 --- a/frontend/src/index.jsx +++ b/frontend/src/index.jsx @@ -11,15 +11,25 @@ appService.getConfig().then((config) => { if (window.public_config.APM_VENDOR == 'sentry') { const history = createBrowserHistory(); + const tooljetServerUrl = window.public_config.TOOLJET_SERVER_URL; + const tracingOrigins = ['localhost', /^\//]; + const releaseVersion = window.public_config.RELEASE_VERSION + ? `tooljet-${window.public_config.RELEASE_VERSION}` + : 'toojet'; + + if (!!tooljetServerUrl) tracingOrigins.push(tooljetServerUrl); + Sentry.init({ dsn: window.public_config.SENTRY_DNS, debug: !!window.public_config.SENTRY_DEBUG, + release: releaseVersion, integrations: [ new Integrations.BrowserTracing({ routingInstrumentation: Sentry.reactRouterV5Instrumentation(history), + tracingOrigins: tracingOrigins, }), ], - tracesSampleRate: 0.25 + tracesSampleRate: 0.50, }); } }); diff --git a/server/package-lock.json b/server/package-lock.json index f3b88c6fa1..802ca7f185 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -19,6 +19,8 @@ "@nestjs/platform-express": "^8.0.0", "@nestjs/serve-static": "^2.2.2", "@nestjs/typeorm": "^8.0.0", + "@sentry/node": "^6.12.0", + "@sentry/tracing": "^6.12.0", "@types/got": "^9.6.12", "@types/humps": "^2.0.1", "@types/nodemailer": "^6.4.4", @@ -2463,6 +2465,139 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, + "node_modules/@sentry/core": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.12.0.tgz", + "integrity": "sha512-mU/zdjlzFHzdXDZCPZm8OeCw7c9xsbL49Mq0TrY0KJjLt4CJBkiq5SDTGfRsenBLgTedYhe5Z/J8Z+xVVq+MfQ==", + "dependencies": { + "@sentry/hub": "6.12.0", + "@sentry/minimal": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/hub": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.12.0.tgz", + "integrity": "sha512-yR/UQVU+ukr42bSYpeqvb989SowIXlKBanU0cqLFDmv5LPCnaQB8PGeXwJAwWhQgx44PARhmB82S6Xor8gYNxg==", + "dependencies": { + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/minimal": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.12.0.tgz", + "integrity": "sha512-r3C54Q1KN+xIqUvcgX9DlcoWE7ezWvFk2pSu1Ojx9De81hVqR9u5T3sdSAP2Xma+um0zr6coOtDJG4WtYlOtsw==", + "dependencies": { + "@sentry/hub": "6.12.0", + "@sentry/types": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/node": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.12.0.tgz", + "integrity": "sha512-hfAU3cX5sNWgqyDQBCOIQOZj21l0w1z2dG4MjmrMMHKrQ18pfMaaOtEwRXMCdjTUlwsK+L3TOoUB23lbezmu1A==", + "dependencies": { + "@sentry/core": "6.12.0", + "@sentry/hub": "6.12.0", + "@sentry/tracing": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@sentry/node/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/tracing": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.12.0.tgz", + "integrity": "sha512-u10QHNknPBzbWSUUNMkvuH53sQd5NaBo6YdNPj4p5b7sE7445Sh0PwBpRbY3ZiUUiwyxV59fx9UQ4yVnPGxZQA==", + "dependencies": { + "@sentry/hub": "6.12.0", + "@sentry/minimal": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/types": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.12.0.tgz", + "integrity": "sha512-urtgLzE4EDMAYQHYdkgC0Ei9QvLajodK1ntg71bGn0Pm84QUpaqpPDfHRU+i6jLeteyC7kWwa5O5W1m/jrjGXA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.12.0.tgz", + "integrity": "sha512-oRHQ7TH5TSsJqoP9Gqq25Jvn9LKexXfAh/OoKwjMhYCGKGhqpDNUIZVgl9DWsGw5A5N5xnQyLOxDfyRV5RshdA==", + "dependencies": { + "@sentry/types": "6.12.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@sideway/address": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz", @@ -9812,6 +9947,11 @@ "node": ">=8" } }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -15885,6 +16025,127 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, + "@sentry/core": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.12.0.tgz", + "integrity": "sha512-mU/zdjlzFHzdXDZCPZm8OeCw7c9xsbL49Mq0TrY0KJjLt4CJBkiq5SDTGfRsenBLgTedYhe5Z/J8Z+xVVq+MfQ==", + "requires": { + "@sentry/hub": "6.12.0", + "@sentry/minimal": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/hub": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.12.0.tgz", + "integrity": "sha512-yR/UQVU+ukr42bSYpeqvb989SowIXlKBanU0cqLFDmv5LPCnaQB8PGeXwJAwWhQgx44PARhmB82S6Xor8gYNxg==", + "requires": { + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/minimal": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.12.0.tgz", + "integrity": "sha512-r3C54Q1KN+xIqUvcgX9DlcoWE7ezWvFk2pSu1Ojx9De81hVqR9u5T3sdSAP2Xma+um0zr6coOtDJG4WtYlOtsw==", + "requires": { + "@sentry/hub": "6.12.0", + "@sentry/types": "6.12.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/node": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.12.0.tgz", + "integrity": "sha512-hfAU3cX5sNWgqyDQBCOIQOZj21l0w1z2dG4MjmrMMHKrQ18pfMaaOtEwRXMCdjTUlwsK+L3TOoUB23lbezmu1A==", + "requires": { + "@sentry/core": "6.12.0", + "@sentry/hub": "6.12.0", + "@sentry/tracing": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/tracing": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.12.0.tgz", + "integrity": "sha512-u10QHNknPBzbWSUUNMkvuH53sQd5NaBo6YdNPj4p5b7sE7445Sh0PwBpRbY3ZiUUiwyxV59fx9UQ4yVnPGxZQA==", + "requires": { + "@sentry/hub": "6.12.0", + "@sentry/minimal": "6.12.0", + "@sentry/types": "6.12.0", + "@sentry/utils": "6.12.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/types": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.12.0.tgz", + "integrity": "sha512-urtgLzE4EDMAYQHYdkgC0Ei9QvLajodK1ntg71bGn0Pm84QUpaqpPDfHRU+i6jLeteyC7kWwa5O5W1m/jrjGXA==" + }, + "@sentry/utils": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.12.0.tgz", + "integrity": "sha512-oRHQ7TH5TSsJqoP9Gqq25Jvn9LKexXfAh/OoKwjMhYCGKGhqpDNUIZVgl9DWsGw5A5N5xnQyLOxDfyRV5RshdA==", + "requires": { + "@sentry/types": "6.12.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "@sideway/address": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz", @@ -21722,6 +21983,11 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" }, + "lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", diff --git a/server/package.json b/server/package.json index a67febf456..b7e608cd9b 100644 --- a/server/package.json +++ b/server/package.json @@ -38,6 +38,8 @@ "@nestjs/platform-express": "^8.0.0", "@nestjs/serve-static": "^2.2.2", "@nestjs/typeorm": "^8.0.0", + "@sentry/node": "^6.12.0", + "@sentry/tracing": "^6.12.0", "@types/got": "^9.6.12", "@types/humps": "^2.0.1", "@types/nodemailer": "^6.4.4", diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 6a3369ace8..d91b344cea 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -1,12 +1,30 @@ -import { Module, OnApplicationBootstrap, OnModuleInit } from '@nestjs/common'; +import { + Module, + OnApplicationBootstrap, + OnModuleInit, + RequestMethod, + MiddlewareConsumer, +} from '@nestjs/common'; + +import { Connection } from 'typeorm'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import ormconfig from '../ormconfig'; +import { SeedsModule } from './modules/seeds/seeds.module'; +import { SeedsService } from '@services/seeds.service'; + +import { LoggerModule } from 'nestjs-pino'; +import { SentryModule } from './modules/observability/sentry/sentry.module'; +import * as Sentry from '@sentry/node'; + +import { ConfigModule } from '@nestjs/config'; +import { ServeStaticModule } from '@nestjs/serve-static'; +import { CaslModule } from './modules/casl/casl.module'; +import { EmailService } from '@services/email.service'; +import { MetaModule } from './modules/meta/meta.module'; import { AppController } from './controllers/app.controller'; import { AppService } from './services/app.service'; import { AuthModule } from './modules/auth/auth.module'; import { UsersModule } from './modules/users/users.module'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { Connection } from 'typeorm'; -import { SeedsModule } from './modules/seeds/seeds.module'; -import { SeedsService } from '@services/seeds.service'; import { AppConfigModule } from './modules/app_config/app_config.module' import { AppsModule } from './modules/apps/apps.module'; import { FoldersModule } from './modules/folders/folders.module'; @@ -14,14 +32,7 @@ import { FolderAppsModule } from './modules/folder_apps/folder_apps.module'; import { DataQueriesModule } from './modules/data_queries/data_queries.module'; import { DataSourcesModule } from './modules/data_sources/data_sources.module'; import { OrganizationsModule } from './modules/organizations/organizations.module'; -import { ConfigModule } from '@nestjs/config'; -import ormconfig from '../ormconfig'; -import { CaslModule } from './modules/casl/casl.module'; -import { EmailService } from '@services/email.service'; -import { MetaModule } from './modules/meta/meta.module'; -import { ServeStaticModule } from '@nestjs/serve-static'; import { join } from 'path'; -import { LoggerModule } from 'nestjs-pino'; const imports = [ ConfigModule.forRoot({ @@ -60,12 +71,23 @@ const imports = [ MetaModule, ]; -if (process.env.SERVE_CLIENT !== 'false') +if (process.env.SERVE_CLIENT !== 'false') { imports.unshift( ServeStaticModule.forRoot({ rootPath: join(__dirname, '../../../', 'frontend/build'), }), ); +} + +if (process.env.APM_VENDOR == 'sentry') { + imports.unshift( + SentryModule.forRoot({ + dsn: process.env.SENTRY_DNS, + tracesSampleRate: 1.0, + debug: !!process.env.SENTRY_DEBUG, + }), + ); +} @Module({ imports, @@ -75,6 +97,13 @@ if (process.env.SERVE_CLIENT !== 'false') export class AppModule implements OnModuleInit, OnApplicationBootstrap { constructor(private connection: Connection) {} + configure(consumer: MiddlewareConsumer): void { + consumer.apply(Sentry.Handlers.requestHandler()).forRoutes({ + path: '*', + method: RequestMethod.ALL, + }); + } + onModuleInit() { console.log(`Initializing ToolJet server modules 📡 `); } diff --git a/server/src/interceptors/sentry.interceptor.ts b/server/src/interceptors/sentry.interceptor.ts new file mode 100644 index 0000000000..cd553f5874 --- /dev/null +++ b/server/src/interceptors/sentry.interceptor.ts @@ -0,0 +1,40 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, + Scope, +} from '@nestjs/common'; +import { catchError, finalize, Observable, throwError } from 'rxjs'; +import { SentryService } from '../services/sentry.service'; +import * as Sentry from '@sentry/node'; + +/** + * We must be in Request scope as we inject SentryService + */ +@Injectable({ scope: Scope.REQUEST }) +export class SentryInterceptor implements NestInterceptor { + constructor(private sentryService: SentryService) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable { + // start a child span for performance tracing + const span = this.sentryService.startChild({ op: `route handler` }); + + return next.handle().pipe( + catchError((error) => { + // capture the error, you can filter out some errors here + Sentry.captureException( + error, + this.sentryService.span.getTraceContext(), + ); + + // throw again the error + return throwError(() => error); + }), + finalize(() => { + span.finish(); + this.sentryService.span.finish(); + }), + ); + } +} diff --git a/server/src/main.ts b/server/src/main.ts index 540e6f3e6d..ca329195af 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -24,7 +24,7 @@ async function bootstrap() { useDefaults: true, directives: { 'script-src': ["'self'", "'unsafe-inline'", "'unsafe-eval'", 'blob:'], - 'default-src': ["'self'", 'blob:'], + 'default-src': ["*.sentry.io", "'self'", 'blob:'], }, }), ); diff --git a/server/src/modules/observability/sentry/sentry.module.ts b/server/src/modules/observability/sentry/sentry.module.ts new file mode 100644 index 0000000000..e1600c711a --- /dev/null +++ b/server/src/modules/observability/sentry/sentry.module.ts @@ -0,0 +1,32 @@ +import { Module } from '@nestjs/common'; +import * as Sentry from '@sentry/node'; +import { APP_INTERCEPTOR } from '@nestjs/core'; +import { SentryService } from '../../../services/sentry.service'; +import { SentryInterceptor } from '../../../interceptors/sentry.interceptor'; + +export const SENTRY_OPTIONS = 'SENTRY_OPTIONS'; +@Module({ + providers: [SentryService], +}) +export class SentryModule { + static forRoot(options: Sentry.NodeOptions) { + // initialization of Sentry, this is where Sentry will create a Hub + Sentry.init(options); + + return { + module: SentryModule, + providers: [ + { + provide: SENTRY_OPTIONS, + useValue: options, + }, + SentryService, + { + provide: APP_INTERCEPTOR, + useClass: SentryInterceptor, + }, + ], + exports: [SentryService], + }; + } +} diff --git a/server/src/services/app_config.service.ts b/server/src/services/app_config.service.ts index 52692bdc07..1afc561de0 100644 --- a/server/src/services/app_config.service.ts +++ b/server/src/services/app_config.service.ts @@ -9,20 +9,24 @@ export class AppConfigService { ? this.fetchAllowedConfigFromEnv() : this.fetchDefaultConfig(); - const mapEntries = await Promise.all(whitelistedConfigVars.map( - (envVar) => [envVar, process.env[envVar]] as [string, string], - )); + const mapEntries = await Promise.all( + whitelistedConfigVars.map( + (envVar) => [envVar, process.env[envVar]] as [string, string], + ), + ); return Object.fromEntries(mapEntries); } fetchDefaultConfig() { return [ + 'TOOLJET_SERVER_URL', + 'RELEASE_VERSION', 'GOOGLE_MAPS_API_KEY', 'APM_VENDOR', 'SENTRY_DNS', 'SENTRY_DEBUG', - ] + ]; } fetchAllowedConfigFromEnv() { diff --git a/server/src/services/sentry.service.ts b/server/src/services/sentry.service.ts new file mode 100644 index 0000000000..3df1039136 --- /dev/null +++ b/server/src/services/sentry.service.ts @@ -0,0 +1,55 @@ +import { Request } from 'express'; +import { Scope } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; +import { REQUEST } from '@nestjs/core'; +import * as Sentry from '@sentry/node'; +import { Span, SpanContext } from '@sentry/types'; + +/** + * Because we inject REQUEST we need to set the service as request scoped + */ +@Injectable({ scope: Scope.REQUEST }) +export class SentryService { + /** + * Return the current span defined in the current Hub and Scope + */ + get span(): Span { + return Sentry.getCurrentHub().getScope().getSpan(); + } + + /** + * When injecting the service it will create the main transaction + * + * @param request + */ + constructor(@Inject(REQUEST) private request: Request) { + const { method, headers, url } = this.request; + + // recreate transaction based from HTTP request + const transaction = Sentry.startTransaction({ + name: `Route: ${method} ${url}`, + op: 'transaction', + }); + + // setup context of newly created transaction + Sentry.getCurrentHub().configureScope((scope) => { + scope.setSpan(transaction); + + // customize your context here + scope.setContext('http', { + method, + url, + headers, + }); + }); + } + + /** + * This will simply start a new child span in the current span + * + * @param spanContext + */ + startChild(spanContext: SpanContext) { + return this.span.startChild(spanContext); + } +}