From 2c5c0cf462ff22d6718459dd8263737861f0192a Mon Sep 17 00:00:00 2001 From: Ahyoung Ryu Date: Fri, 19 Apr 2024 13:28:18 +0900 Subject: [PATCH] [AC-2507] Add Datadog monitoring hook (#151) * Add datadog monitoring hook * Address @bang9 comment --- .env.example | 3 + .github/workflows/gh-deploy.yml | 4 +- .github/workflows/package-publish.yml | 4 +- package-lock.json | 191 +++++--------------------- package.json | 2 +- packages/self-service/src/App.tsx | 1 + src/App.tsx | 1 + src/components/ProviderContainer.tsx | 2 + src/const.ts | 1 + src/context/ConstantContext.tsx | 1 + src/hooks/useDatadog.ts | 38 +++++ src/vite-env.d.ts | 1 + vite.config.ts | 5 + 13 files changed, 96 insertions(+), 158 deletions(-) create mode 100644 src/hooks/useDatadog.ts diff --git a/.env.example b/.env.example index 21cb06132..4b73e248c 100644 --- a/.env.example +++ b/.env.example @@ -2,5 +2,8 @@ # Sendbird App ID # VITE_CHAT_WIDGET_APP_ID= # VITE_CHAT_WIDGET_BOT_ID= + # Only for internal use # VITE_CHAT_AI_WIDGET_KEY= +# VITE_CHAT_AI_WIDGET_DATADOG_CLIENT_TOKEN= +# VITE_CHAT_AI_WIDGET_DATADOG_APP_ID= diff --git a/.github/workflows/gh-deploy.yml b/.github/workflows/gh-deploy.yml index 63a407453..7def6a714 100644 --- a/.github/workflows/gh-deploy.yml +++ b/.github/workflows/gh-deploy.yml @@ -18,7 +18,9 @@ jobs: run: | npm install cp .env .env.production - echo "VITE_CHAT_AI_WIDGET_KEY=${{ secrets.chat_ai_widget_key }}" >> .env.production + echo "VITE_CHAT_AI_WIDGET_KEY=${{ secrets.chat_ai_widget_key }}" >> .env.production && \ + echo "VITE_CHAT_AI_WIDGET_DATADOG_CLIENT_TOKEN=${{ secrets.datadog_client_token }}" >> .env.production && \ + echo "VITE_CHAT_AI_WIDGET_DATADOG_APP_ID=${{ secrets.datadog_app_id }}" >> .env.production npm run build:pages - name: Deploy 🚀 diff --git a/.github/workflows/package-publish.yml b/.github/workflows/package-publish.yml index 90411cb49..975ac4f0e 100644 --- a/.github/workflows/package-publish.yml +++ b/.github/workflows/package-publish.yml @@ -34,7 +34,9 @@ jobs: run: | npm install touch .env.production - echo "VITE_CHAT_AI_WIDGET_KEY=${{ secrets.chat_ai_widget_key }}" >> .env.production + echo "VITE_CHAT_AI_WIDGET_KEY=${{ secrets.chat_ai_widget_key }}" >> .env.production && \ + echo "VITE_CHAT_AI_WIDGET_DATADOG_CLIENT_TOKEN=${{ secrets.datadog_client_token }}" >> .env.production && \ + echo "VITE_CHAT_AI_WIDGET_DATADOG_APP_ID=${{ secrets.datadog_app_id }}" >> .env.production npm run build:npm - name: Publish to npm run: | diff --git a/package-lock.json b/package-lock.json index 2fa807384..1f6f5c020 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sendbird/chat-ai-widget", - "version": "1.3.7", + "version": "1.3.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@sendbird/chat-ai-widget", - "version": "1.3.7", + "version": "1.3.8", "dependencies": { "@sendbird/chat": "^4.11.0", "@sendbird/uikit-react": "^3.13.4", @@ -16,7 +16,7 @@ "styled-components": "^5.3.11" }, "devDependencies": { - "@tanstack/eslint-plugin-query": "^5.17.22", + "@datadog/browser-rum": "^5.15.0", "@types/dompurify": "^3.0.5", "@types/react": "^18.0.37", "@types/react-dom": "^18.0.11", @@ -427,6 +427,39 @@ "node": ">=6.9.0" } }, + "node_modules/@datadog/browser-core": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@datadog/browser-core/-/browser-core-5.15.0.tgz", + "integrity": "sha512-vZeHK0aEqyQqJOal8skaGrGi788Tt+MSP+pH/+J56/Bj4tjLggeIw2SPN+j7XSS+3Q5atk1ZRoEMwvHVV7/s7Q==", + "dev": true + }, + "node_modules/@datadog/browser-rum": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@datadog/browser-rum/-/browser-rum-5.15.0.tgz", + "integrity": "sha512-bvY8Pgb92uB9+x7oVQTLq5P/fyMBTn+xoEzYJbMmJCEhKL1QPqtYdiRqTNhOLnemZFJ5OaaFNg9az68xJ/O0gg==", + "dev": true, + "dependencies": { + "@datadog/browser-core": "5.15.0", + "@datadog/browser-rum-core": "5.15.0" + }, + "peerDependencies": { + "@datadog/browser-logs": "5.15.0" + }, + "peerDependenciesMeta": { + "@datadog/browser-logs": { + "optional": true + } + } + }, + "node_modules/@datadog/browser-rum-core": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@datadog/browser-rum-core/-/browser-rum-core-5.15.0.tgz", + "integrity": "sha512-f8TghTfjEqEo/AexyfM0OCZ3aUOcN6+xg9XRohVC+kmmboo+7GcMLgNfW9zkEoCJvNOheSFN2xPZbNE6iUD3LA==", + "dev": true, + "dependencies": { + "@datadog/browser-core": "5.15.0" + } + }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", @@ -1085,146 +1118,6 @@ "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@tanstack/eslint-plugin-query": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.20.1.tgz", - "integrity": "sha512-oIp7Wh90KHOm1FKCvcv87fiD2H96xo/crFrlhbvqBzR2f0tMEGOK/ANKMGNFQprd6BT6lyZhQPlOEkFdezsjIg==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^6.20.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "eslint": "^8.0.0" - } - }, - "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@tanstack/eslint-plugin-query/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@tanstack/eslint-plugin-query/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@tanstack/query-core": { "version": "5.21.4", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.21.4.tgz", @@ -5156,18 +5049,6 @@ "node": ">=8.0" } }, - "node_modules/ts-api-utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", - "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, "node_modules/tsc-silent": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tsc-silent/-/tsc-silent-1.2.2.tgz", diff --git a/package.json b/package.json index 375983392..0fc0f3938 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "styled-components": "^5.3.11" }, "devDependencies": { - "@tanstack/eslint-plugin-query": "^5.17.22", + "@datadog/browser-rum": "^5.15.0", "@types/dompurify": "^3.0.5", "@types/react": "^18.0.37", "@types/react-dom": "^18.0.11", diff --git a/packages/self-service/src/App.tsx b/packages/self-service/src/App.tsx index e51e7ac90..257b3dd05 100644 --- a/packages/self-service/src/App.tsx +++ b/packages/self-service/src/App.tsx @@ -29,6 +29,7 @@ function App() { customUserAgentParam={{ 'chat-ai-widget-deployed': 'True', }} + serviceName="genai-self-service" {...chatbotConfigs} /> ); diff --git a/src/App.tsx b/src/App.tsx index ed88bb665..ed4469a91 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -39,6 +39,7 @@ const App = (props: Props) => { customUserAgentParam={props.customUserAgentParam} autoOpen={props.autoOpen} renderWidgetToggleButton={props.renderWidgetToggleButton} + serviceName={props.serviceName} /> ); }; diff --git a/src/components/ProviderContainer.tsx b/src/components/ProviderContainer.tsx index a2b55d0b0..e0c099a12 100644 --- a/src/components/ProviderContainer.tsx +++ b/src/components/ProviderContainer.tsx @@ -11,6 +11,7 @@ import { } from '../context/ConstantContext'; import { WidgetOpenProvider } from '../context/WidgetOpenContext'; import { useChannelStyle } from '../hooks/useChannelStyle'; +import useDatadogRum from '../hooks/useDatadog'; import useWidgetLocalStorage from '../hooks/useWidgetLocalStorage'; import { getTheme } from '../theme'; import { isMobile } from '../utils'; @@ -28,6 +29,7 @@ const SBComponent = ({ children }: { children: React.ReactElement }) => { stringSet, ...restConstantProps } = useConstantState(); + useDatadogRum(); const userAgentCustomParams = useRef({ ...customUserAgentParam, diff --git a/src/const.ts b/src/const.ts index 0f0853947..71ca153c3 100644 --- a/src/const.ts +++ b/src/const.ts @@ -99,6 +99,7 @@ export interface Constant { onClick: () => void; isOpen: boolean; }) => React.ReactElement; + serviceName?: string; } export interface SuggestedReply { diff --git a/src/context/ConstantContext.tsx b/src/context/ConstantContext.tsx index 7fc5ba821..2b01d69c1 100644 --- a/src/context/ConstantContext.tsx +++ b/src/context/ConstantContext.tsx @@ -81,6 +81,7 @@ export const ConstantStateProvider = (props: ProviderProps) => { enableMobileView: props.enableMobileView ?? initialState.enableMobileView, autoOpen: props.autoOpen, renderWidgetToggleButton: props.renderWidgetToggleButton, + serviceName: props.serviceName, }), [props] ); diff --git a/src/hooks/useDatadog.ts b/src/hooks/useDatadog.ts new file mode 100644 index 000000000..ce21a9f92 --- /dev/null +++ b/src/hooks/useDatadog.ts @@ -0,0 +1,38 @@ +import { datadogRum } from '@datadog/browser-rum'; +import { useEffect } from 'react'; + +import { useConstantState } from '../context/ConstantContext'; + +const DATADOG_APP_ID = import.meta.env.VITE_CHAT_AI_WIDGET_DATADOG_APP_ID; +const DATADOG_CLIENT_TOKEN = import.meta.env + .VITE_CHAT_AI_WIDGET_DATADOG_CLIENT_TOKEN; +const isProd = import.meta.env.PROD ? 'prod' : 'dev'; + +const useDatadogRum = () => { + const { serviceName } = useConstantState(); + useEffect(() => { + if (DATADOG_APP_ID != null && DATADOG_CLIENT_TOKEN != null) { + datadogRum.init({ + applicationId: DATADOG_APP_ID, + clientToken: DATADOG_CLIENT_TOKEN, + site: 'datadoghq.com', + sessionSampleRate: 100, + sessionReplaySampleRate: 100, + trackResources: true, + trackLongTasks: true, + trackUserInteractions: true, + service: serviceName || 'genai-chatbot-widget', + version: APP_VERSION, + env: isProd ? 'production' : 'development', + }); + + datadogRum.startSessionReplayRecording(); + } + + return () => { + datadogRum.stopSessionReplayRecording(); + }; + }, []); +}; + +export default useDatadogRum; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 11f02fe2a..46458c690 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1 +1,2 @@ /// +declare const APP_VERSION: string; diff --git a/vite.config.ts b/vite.config.ts index 5cc154c06..50d12e533 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -16,6 +16,9 @@ export default defineConfig({ svgr(), dts(), ], + define: { + APP_VERSION: JSON.stringify(process.env.npm_package_version), + }, // to point to correct path for gh-pages base: '/chat-ai-widget', build: { @@ -29,11 +32,13 @@ export default defineConfig({ external: [ 'react', 'react-dom', + '@datadog/browser-rum' ], output: { globals: { react: 'React', 'react-dom': 'ReactDOM', + '@datadog/browser-rum': 'datadogRum' } } }