From aaaad99f5c0aceabae5560d0a6f50a0eeb901a13 Mon Sep 17 00:00:00 2001 From: Chavda Bhavik Date: Wed, 16 Nov 2022 17:24:01 +0530 Subject: [PATCH] Logging (#66) * feat: Added basic error tracing to widget and api * feat: Added AppShell --- apps/api/package.json | 1 + apps/api/src/.env.development | 3 + apps/api/src/.env.production | 3 + apps/api/src/bootstrap.ts | 22 +++- apps/api/src/config/env-validator.ts | 1 + apps/api/src/types/env.d.ts | 2 +- apps/widget/.example.env | 3 +- apps/widget/package.json | 2 + apps/widget/src/components/App.tsx | 31 ++++- .../components/Common/AppShell/AppShell.tsx | 25 ++++ .../src/components/Common/AppShell/index.ts | 1 + apps/widget/src/config/app.config.ts | 4 + pnpm-lock.yaml | 111 ++++++++++++++++-- 13 files changed, 188 insertions(+), 21 deletions(-) create mode 100644 apps/widget/src/components/Common/AppShell/AppShell.tsx create mode 100644 apps/widget/src/components/Common/AppShell/index.ts diff --git a/apps/api/package.json b/apps/api/package.json index 861121332..556e3278a 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -26,6 +26,7 @@ "@nestjs/core": "^9.1.2", "@nestjs/platform-express": "^9.1.2", "@nestjs/swagger": "^6.1.2", + "@sentry/node": "^7.19.0", "ajv": "^8.11.0", "ajv-formats": "^2.1.1", "ajv-keywords": "^5.1.0", diff --git a/apps/api/src/.env.development b/apps/api/src/.env.development index 76cf09286..8dc9d3585 100644 --- a/apps/api/src/.env.development +++ b/apps/api/src/.env.development @@ -13,3 +13,6 @@ S3_BUCKET_NAME=impler # Security ACCESS-KEY= + +# Analytics +SENTRY_DSN= diff --git a/apps/api/src/.env.production b/apps/api/src/.env.production index 76cf09286..8dc9d3585 100644 --- a/apps/api/src/.env.production +++ b/apps/api/src/.env.production @@ -13,3 +13,6 @@ S3_BUCKET_NAME=impler # Security ACCESS-KEY= + +# Analytics +SENTRY_DSN= diff --git a/apps/api/src/bootstrap.ts b/apps/api/src/bootstrap.ts index 4773f1b50..bdef678aa 100644 --- a/apps/api/src/bootstrap.ts +++ b/apps/api/src/bootstrap.ts @@ -1,5 +1,6 @@ import './config'; +import * as Sentry from '@sentry/node'; import { INestApplication, ValidationPipe, Logger } from '@nestjs/common'; import * as compression from 'compression'; import { NestFactory } from '@nestjs/core'; @@ -9,6 +10,20 @@ import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppModule } from './app.module'; import { validateEnv } from './config/env-validator'; import { ACCESS_KEY_NAME } from '@impler/shared'; +import { version } from '../package.json'; + +if (process.env.SENTRY_DSN) { + Sentry.init({ + dsn: process.env.SENTRY_DSN, + environment: process.env.NODE_ENV, + release: `v${version}`, + ignoreErrors: ['Non-Error exception captured'], + integrations: [ + // enable HTTP calls tracing + new Sentry.Integrations.Http({ tracing: true }), + ], + }); +} // Validate the ENV variables after launching SENTRY, so missing variables will report to sentry validateEnv(); @@ -21,6 +36,11 @@ export async function bootstrap(expressApp?): Promise { app = await NestFactory.create(AppModule); } + if (process.env.SENTRY_DSN) { + app.use(Sentry.Handlers.requestHandler()); + app.use(Sentry.Handlers.tracingHandler()); + } + app.enableCors(corsOptionsDelegate); app.setGlobalPrefix('v1'); @@ -69,7 +89,7 @@ const corsOptionsDelegate = function (req, callback) { const corsOptions = { origin: false as boolean | string | string[], preflightContinue: false, - allowedHeaders: ['Content-Type', ACCESS_KEY_NAME], + allowedHeaders: ['Content-Type', ACCESS_KEY_NAME, 'sentry-trace', 'baggage'], methods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], }; diff --git a/apps/api/src/config/env-validator.ts b/apps/api/src/config/env-validator.ts index 1efca9f6c..8678b5514 100644 --- a/apps/api/src/config/env-validator.ts +++ b/apps/api/src/config/env-validator.ts @@ -16,6 +16,7 @@ const validators: { [K in keyof any]: ValidatorSpec } = { FRONT_BASE_URL: url(), MONGO_URL: str(), RABBITMQ_CONN_URL: str(), + SENTRY_DSN: str(), }; export function validateEnv() { diff --git a/apps/api/src/types/env.d.ts b/apps/api/src/types/env.d.ts index 6f2dbb19e..729e3b815 100644 --- a/apps/api/src/types/env.d.ts +++ b/apps/api/src/types/env.d.ts @@ -5,7 +5,7 @@ declare namespace NodeJS { PORT: number; 'ACCESS-KEY'?: string; FRONT_BASE_URL: string; - + SENTRY_DSN: string; MONGO_URL: string; S3_LOCAL_STACK: string; S3_REGION: string; diff --git a/apps/widget/.example.env b/apps/widget/.example.env index b4ab84278..ca047b134 100644 --- a/apps/widget/.example.env +++ b/apps/widget/.example.env @@ -1 +1,2 @@ -REACT_APP_API_URL=http://localhost:3000 \ No newline at end of file +REACT_APP_API_URL=http://localhost:3000 +REACT_APP_SENTRY_DSN= \ No newline at end of file diff --git a/apps/widget/package.json b/apps/widget/package.json index 6990cefd6..64598727a 100644 --- a/apps/widget/package.json +++ b/apps/widget/package.json @@ -45,6 +45,8 @@ "@mantine/core": "^5.6.3", "@mantine/dropzone": "^5.6.3", "@mantine/notifications": "5.6.3", + "@sentry/react": "^7.19.0", + "@sentry/tracing": "^7.19.0", "@storybook/addon-essentials": "^6.5.13", "@storybook/react": "^6.5.13", "@tanstack/react-query": "^4.14.5", diff --git a/apps/widget/src/components/App.tsx b/apps/widget/src/components/App.tsx index 306a0b3a1..424dddbf4 100644 --- a/apps/widget/src/components/App.tsx +++ b/apps/widget/src/components/App.tsx @@ -1,11 +1,28 @@ +import * as Sentry from '@sentry/react'; +import { Integrations } from '@sentry/tracing'; import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { NotificationsProvider } from '@mantine/notifications'; import { MantineProvider } from '@mantine/core'; -import { CONTEXT_PATH, mantineConfig, variables } from '@config'; +import { CONTEXT_PATH, mantineConfig, variables, SENTRY_DSN, ENV } from '@config'; import { WidgetShell } from './ApplicationShell'; import { Container } from './Common/Container'; import { Widget } from './widget'; +import { AppShell } from './Common/AppShell'; + +if (SENTRY_DSN) { + Sentry.init({ + dsn: SENTRY_DSN, + integrations: [new Integrations.BrowserTracing()], + environment: ENV, + /* + * Set tracesSampleRate to 1.0 to capture 100% + * of transactions for performance monitoring. + * We recommend adjusting this value in production + */ + tracesSampleRate: 1.0, + }); +} export function App() { const queryClient = new QueryClient({ @@ -28,11 +45,13 @@ export function App() { - - - - + + + + + + + } /> diff --git a/apps/widget/src/components/Common/AppShell/AppShell.tsx b/apps/widget/src/components/Common/AppShell/AppShell.tsx new file mode 100644 index 000000000..93bd89402 --- /dev/null +++ b/apps/widget/src/components/Common/AppShell/AppShell.tsx @@ -0,0 +1,25 @@ +import { PropsWithChildren } from 'react'; +import * as Sentry from '@sentry/react'; + +export function AppShell({ children }: PropsWithChildren<{}>) { + return ( + ( + <> + Sorry, but something went wrong.
+ Our team been notified about it and we will look at it asap. +
+ + + Event Id: {eventId}. +
+ {error.toString()} +
+
+ + )} + > + {children} +
+ ); +} diff --git a/apps/widget/src/components/Common/AppShell/index.ts b/apps/widget/src/components/Common/AppShell/index.ts new file mode 100644 index 000000000..5af13488e --- /dev/null +++ b/apps/widget/src/components/Common/AppShell/index.ts @@ -0,0 +1 @@ +export * from './AppShell'; diff --git a/apps/widget/src/config/app.config.ts b/apps/widget/src/config/app.config.ts index b06df3406..0eb4127e5 100644 --- a/apps/widget/src/config/app.config.ts +++ b/apps/widget/src/config/app.config.ts @@ -6,4 +6,8 @@ export const API_URL = ? window._env_?.REACT_APP_API_URL || process.env.REACT_APP_API_URL || 'http://localhost:1336' : window._env_?.REACT_APP_API_URL || process.env.REACT_APP_API_URL || 'http://localhost:3000'; +export const SENTRY_DSN = window._env_?.REACT_APP_API_URL || process.env.REACT_APP_SENTRY_DSN || undefined; + +export const ENV = window._env_?.REACT_APP_ENVIRONMENT || process.env.REACT_APP_SENTRY_DSN || 'local'; + export const CONTEXT_PATH = getContextPath(ImplerComponentEnum.WIDGET); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1c64ab5e..5ac311417 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,6 +67,7 @@ importers: '@nestjs/core': ^9.1.2 '@nestjs/platform-express': ^9.1.2 '@nestjs/swagger': ^6.1.2 + '@sentry/node': ^7.19.0 '@types/chai': ^4.3.4 '@types/express': ^4.17.14 '@types/mocha': ^10.0.0 @@ -99,6 +100,7 @@ importers: '@nestjs/core': 9.1.2_foclggtudvr6cnfc5hz7ukfode '@nestjs/platform-express': 9.1.2_umdjpi6fqr4wvw73r3mhthbyle '@nestjs/swagger': 6.1.2_o5ym4ozkuwx4tb6ybx3mgktlhe + '@sentry/node': 7.19.0 ajv: 8.11.0 ajv-formats: 2.1.1_ajv@8.11.0 ajv-keywords: 5.1.0_ajv@8.11.0 @@ -160,6 +162,8 @@ importers: '@mantine/core': ^5.6.3 '@mantine/dropzone': ^5.6.3 '@mantine/notifications': 5.6.3 + '@sentry/react': ^7.19.0 + '@sentry/tracing': ^7.19.0 '@storybook/addon-essentials': ^6.5.13 '@storybook/react': ^6.5.13 '@tanstack/react-query': ^4.14.5 @@ -188,6 +192,8 @@ importers: '@mantine/core': 5.6.3_lha5ulipqaccx6prm56igoh3zy '@mantine/dropzone': 5.6.3_hliqdvg4trnmlenfv3ync25tfa '@mantine/notifications': 5.6.3_hliqdvg4trnmlenfv3ync25tfa + '@sentry/react': 7.19.0_react@18.2.0 + '@sentry/tracing': 7.19.0 '@storybook/addon-essentials': 6.5.13_jbjkrzxoioyjn2npuaofuog6gm '@storybook/react': 6.5.13_sevfxvufqxngza4zj5dbnig4vq '@tanstack/react-query': 4.14.5_biqbaboplfbrettd7655fr4n2y @@ -6033,6 +6039,77 @@ packages: resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==} dev: false + /@sentry/browser/7.19.0: + resolution: {integrity: sha512-dWi5VjEwiLb4ofata0UCLdTbXLD1uDUebe9rNSBkHZ3fHF4eap4ZJlu3dYePKB0CKZhZrjzbydimMhaMUNdnug==} + engines: {node: '>=8'} + dependencies: + '@sentry/core': 7.19.0 + '@sentry/types': 7.19.0 + '@sentry/utils': 7.19.0 + tslib: 1.14.1 + dev: false + + /@sentry/core/7.19.0: + resolution: {integrity: sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==} + engines: {node: '>=8'} + dependencies: + '@sentry/types': 7.19.0 + '@sentry/utils': 7.19.0 + tslib: 1.14.1 + dev: false + + /@sentry/node/7.19.0: + resolution: {integrity: sha512-yG7Tx32WqOkEHVotFLrumCcT9qlaSDTkFNZ+yLSvZXx74ifsE781DzBA9W7K7bBdYO3op+p2YdsOKzf3nPpAyQ==} + engines: {node: '>=8'} + dependencies: + '@sentry/core': 7.19.0 + '@sentry/types': 7.19.0 + '@sentry/utils': 7.19.0 + cookie: 0.4.2 + https-proxy-agent: 5.0.1 + lru_map: 0.3.3 + tslib: 1.14.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@sentry/react/7.19.0_react@18.2.0: + resolution: {integrity: sha512-ooF1TwdgkHkUo7u9Bx+kab51gpVNUW7b+5A2krfhk/Fx2eY8z5VilzUzHCRq2jbTE9yJTpfRL3KlEXROs0AUvg==} + engines: {node: '>=8'} + peerDependencies: + react: 15.x || 16.x || 17.x || 18.x + dependencies: + '@sentry/browser': 7.19.0 + '@sentry/types': 7.19.0 + '@sentry/utils': 7.19.0 + hoist-non-react-statics: 3.3.2 + react: 18.2.0 + tslib: 1.14.1 + dev: false + + /@sentry/tracing/7.19.0: + resolution: {integrity: sha512-SWY17M3TsgBePaGowUcSqBwaT0TJQzuNexVnLojuU0k6F57L9hubvP9zaoosoCfARXQ/3NypAFWnlJyf570rFQ==} + engines: {node: '>=8'} + dependencies: + '@sentry/core': 7.19.0 + '@sentry/types': 7.19.0 + '@sentry/utils': 7.19.0 + tslib: 1.14.1 + dev: false + + /@sentry/types/7.19.0: + resolution: {integrity: sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA==} + engines: {node: '>=8'} + dev: false + + /@sentry/utils/7.19.0: + resolution: {integrity: sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==} + engines: {node: '>=8'} + dependencies: + '@sentry/types': 7.19.0 + tslib: 1.14.1 + dev: false + /@sinclair/typebox/0.24.42: resolution: {integrity: sha512-d+2AtrHGyWek2u2ITF0lHRIv6Tt7X0dEHW+0rP+5aDCEjC3fiN2RBjrLD0yU0at52BcZbRGxLbAtXiR0hFCjYw==} @@ -7910,6 +7987,7 @@ packages: typescript: 4.8.4 transitivePeerDependencies: - supports-color + dev: true /@typescript-eslint/parser/5.38.0_irgkl5vooow2ydyo6aokmferha: resolution: {integrity: sha512-/F63giJGLDr0ms1Cr8utDAxP2SPiglaD6V+pCOcG35P2jCqdfR7uuEhz1GIC3oy4hkUF8xA1XSXmd9hOh/a5EA==} @@ -7929,7 +8007,6 @@ packages: typescript: 4.8.3 transitivePeerDependencies: - supports-color - dev: false /@typescript-eslint/scope-manager/5.38.0: resolution: {integrity: sha512-ByhHIuNyKD9giwkkLqzezZ9y5bALW8VNY6xXcP+VxoH4JBDKjU5WNnsiD4HJdglHECdV+lyaxhvQjTUbRboiTA==} @@ -8014,7 +8091,6 @@ packages: typescript: 4.8.3 transitivePeerDependencies: - supports-color - dev: false /@typescript-eslint/typescript-estree/5.38.0_typescript@4.8.4: resolution: {integrity: sha512-6P0RuphkR+UuV7Avv7MU3hFoWaGcrgOdi8eTe1NwhMp2/GjUJoODBTRWzlHpZh6lFOaPmSvgxGlROa0Sg5Zbyg==} @@ -8035,6 +8111,7 @@ packages: typescript: 4.8.4 transitivePeerDependencies: - supports-color + dev: true /@typescript-eslint/typescript-estree/5.40.1_typescript@4.8.3: resolution: {integrity: sha512-5QTP/nW5+60jBcEPfXy/EZL01qrl9GZtbgDZtDPlfW5zj/zjNrdI2B5zMUHmOsfvOr2cWqwVdWjobCiHcedmQA==} @@ -8930,7 +9007,7 @@ packages: resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} engines: {node: '>=4'} dependencies: - tslib: 2.4.0 + tslib: 2.4.1 dev: false /astral-regex/2.0.0: @@ -9817,7 +9894,7 @@ packages: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} dependencies: pascal-case: 3.1.2 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /camelcase-css/2.0.1: @@ -10513,6 +10590,11 @@ packages: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} dev: false + /cookie/0.4.2: + resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} + engines: {node: '>= 0.6'} + dev: false + /cookie/0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} @@ -11473,7 +11555,7 @@ packages: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: no-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /dot-prop/5.3.0: @@ -12079,7 +12161,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.38.0_abkyjrvwkq25uefwggm6hc3u2a + '@typescript-eslint/parser': 5.38.0_irgkl5vooow2ydyo6aokmferha debug: 3.2.7 eslint: 8.23.1 eslint-import-resolver-node: 0.3.6 @@ -12145,7 +12227,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.38.0_abkyjrvwkq25uefwggm6hc3u2a + '@typescript-eslint/parser': 5.38.0_irgkl5vooow2ydyo6aokmferha array-includes: 3.1.5 array.prototype.flat: 1.3.0 debug: 2.6.9 @@ -16198,7 +16280,7 @@ packages: /lower-case/2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.4.0 + tslib: 2.4.1 dev: false /lru-cache/5.1.1: @@ -16218,6 +16300,10 @@ packages: engines: {node: '>=12'} dev: true + /lru_map/0.3.3: + resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} + dev: false + /lunr/2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} dev: true @@ -16905,7 +16991,7 @@ packages: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: lower-case: 2.0.2 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /node-abort-controller/3.0.1: @@ -17738,7 +17824,7 @@ packages: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: dot-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /parent-module/1.0.1: @@ -17827,7 +17913,7 @@ packages: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} dependencies: no-case: 3.0.4 - tslib: 2.4.0 + tslib: 2.4.1 dev: false /pascalcase/0.1.1: @@ -21763,7 +21849,6 @@ packages: dependencies: tslib: 1.14.1 typescript: 4.8.3 - dev: false /tsutils/3.21.0_typescript@4.8.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -21773,6 +21858,7 @@ packages: dependencies: tslib: 1.14.1 typescript: 4.8.4 + dev: true /tty-browserify/0.0.0: resolution: {integrity: sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==} @@ -21865,6 +21951,7 @@ packages: resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} engines: {node: '>=4.2.0'} hasBin: true + dev: true /uglify-js/3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==}