diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3b326a11..52c0d923 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -51,10 +51,8 @@ jobs: PROOFMARKET_TOOLCHAIN_REPO = "${{ secrets.PROOFMARKET_TOOLCHAIN_REPO }}" CIRCUIT_DEVELOPER_GUIDE_URL = "${{ secrets.CIRCUIT_DEVELOPER_GUIDE_URL }}" API_RESPONSE_WAIT_TIMEOUT = "${{ secrets.API_RESPONSE_WAIT_TIMEOUT }}" - DATADOG_APPLICATION_ID = "${{ secrets.DATADOG_APPLICATION_ID }}" - DATADOG_CLIENT_TOKEN = "${{ secrets.DATADOG_CLIENT_TOKEN }}" - DATADOG_SITE = "${{ secrets.DATADOG_SITE }}" - DATADOG_SERVICE_NAME = "${{ secrets.DATADOG_SERVICE_NAME }}" + OTEL_TRACE_EXPORTER_URL = "${{ secrets.OTEL_TRACE_EXPORTER_URL }}" + OTEL_SERVICE_NAME = "${{ secrets.OTEL_SERVICE_NAME }}" ' echo "$config" > runtime-config.toml echo runtime-config.toml diff --git a/package-lock.json b/package-lock.json index 071e3eba..31fdd6ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,11 @@ "name": "proof-market.frontend", "version": "0.1.9", "dependencies": { - "@datadog/browser-rum": "^5.17.1", "@loadable/component": "^5.15.3", "@nilfoundation/react-components": "^0.8.3", "@nilfoundation/ui-kit": "^2.1.3", + "@opentelemetry/exporter-trace-otlp-http": "^0.51.1", + "@opentelemetry/sdk-trace-web": "^1.24.1", "@reduxjs/toolkit": "^1.8.5", "baseui": "^13.0.0", "bootstrap-sass": "^3.4.3", @@ -1932,36 +1933,6 @@ "w3c-keyname": "^2.2.4" } }, - "node_modules/@datadog/browser-core": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/@datadog/browser-core/-/browser-core-5.17.1.tgz", - "integrity": "sha512-OsdjeRCCT1U9+ddygqg3PkqHOkkk7Vr2tI3U8xgZQyiqCzCuooDHebq/40UMP6mf9OVETDkqlSWQ/ouqvFKjUQ==" - }, - "node_modules/@datadog/browser-rum": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/@datadog/browser-rum/-/browser-rum-5.17.1.tgz", - "integrity": "sha512-BiXA47pBTCjifQxseEVDsrKzLByMdhcp223z8LpyJfjAjMsJad0qxJV5EC+sXANN6yXvpCiC0IjMWNpNHRqpIA==", - "dependencies": { - "@datadog/browser-core": "5.17.1", - "@datadog/browser-rum-core": "5.17.1" - }, - "peerDependencies": { - "@datadog/browser-logs": "5.17.1" - }, - "peerDependenciesMeta": { - "@datadog/browser-logs": { - "optional": true - } - } - }, - "node_modules/@datadog/browser-rum-core": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/@datadog/browser-rum-core/-/browser-rum-core-5.17.1.tgz", - "integrity": "sha512-tb+kzIR5C5EHeCXLiTW5bQTm4T1KsuBUE+Xdf1MJJlpcPKtiqnNKS2gJ1OJQpfuOHWwWrGsH2tIO7fZUtALqAg==", - "dependencies": { - "@datadog/browser-core": "5.17.1" - } - }, "node_modules/@date-io/core": { "version": "2.17.0", "resolved": "https://registry.npmjs.org/@date-io/core/-/core-2.17.0.tgz", @@ -2658,6 +2629,177 @@ "node": ">= 8" } }, + "node_modules/@opentelemetry/api": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", + "integrity": "sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.51.1.tgz", + "integrity": "sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.24.1.tgz", + "integrity": "sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.51.1.tgz", + "integrity": "sha512-n+LhLPsX07URh+HhV2SHVSvz1t4G/l/CE5BjpmhAPqeTceFac1VpyQkavWEJbvnK5bUEXijWt4LxAxFpt2fXyw==", + "dependencies": { + "@opentelemetry/core": "1.24.1", + "@opentelemetry/otlp-exporter-base": "0.51.1", + "@opentelemetry/otlp-transformer": "0.51.1", + "@opentelemetry/resources": "1.24.1", + "@opentelemetry/sdk-trace-base": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.51.1.tgz", + "integrity": "sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==", + "dependencies": { + "@opentelemetry/core": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.51.1.tgz", + "integrity": "sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==", + "dependencies": { + "@opentelemetry/api-logs": "0.51.1", + "@opentelemetry/core": "1.24.1", + "@opentelemetry/resources": "1.24.1", + "@opentelemetry/sdk-logs": "0.51.1", + "@opentelemetry/sdk-metrics": "1.24.1", + "@opentelemetry/sdk-trace-base": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.24.1.tgz", + "integrity": "sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==", + "dependencies": { + "@opentelemetry/core": "1.24.1", + "@opentelemetry/semantic-conventions": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.51.1.tgz", + "integrity": "sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==", + "dependencies": { + "@opentelemetry/core": "1.24.1", + "@opentelemetry/resources": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.9.0", + "@opentelemetry/api-logs": ">=0.39.1" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.24.1.tgz", + "integrity": "sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==", + "dependencies": { + "@opentelemetry/core": "1.24.1", + "@opentelemetry/resources": "1.24.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.24.1.tgz", + "integrity": "sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==", + "dependencies": { + "@opentelemetry/core": "1.24.1", + "@opentelemetry/resources": "1.24.1", + "@opentelemetry/semantic-conventions": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-web": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-web/-/sdk-trace-web-1.24.1.tgz", + "integrity": "sha512-0w+aKRai9VREeo3VrtW+hcbrE2Fl/uKL7G+oXgRNf6pI9QLaEGuEzUTX+oxXVPBadzjOd+5dqCHYdX7UeVjzwA==", + "dependencies": { + "@opentelemetry/core": "1.24.1", + "@opentelemetry/sdk-trace-base": "1.24.1", + "@opentelemetry/semantic-conventions": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.24.1.tgz", + "integrity": "sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==", + "engines": { + "node": ">=14" + } + }, "node_modules/@redux-saga/core": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz", @@ -7268,9 +7410,7 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "peer": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/log-update": { "version": "5.0.1", diff --git a/package.json b/package.json index 34dc3a8d..0cfb45a7 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,11 @@ "version": "0.1.9", "private": true, "dependencies": { - "@datadog/browser-rum": "^5.17.1", "@loadable/component": "^5.15.3", "@nilfoundation/react-components": "^0.8.3", "@nilfoundation/ui-kit": "^2.1.3", + "@opentelemetry/exporter-trace-otlp-http": "^0.51.1", + "@opentelemetry/sdk-trace-web": "^1.24.1", "@reduxjs/toolkit": "^1.8.5", "baseui": "^13.0.0", "bootstrap-sass": "^3.4.3", diff --git a/public/runtime-config.toml b/public/runtime-config.toml index 595eaea5..f1a85096 100644 --- a/public/runtime-config.toml +++ b/public/runtime-config.toml @@ -8,7 +8,5 @@ GA_TRACKING_ID = "" PROOFMARKET_TOOLCHAIN_REPO = "" CIRCUIT_DEVELOPER_GUIDE_URL = "" API_RESPONSE_WAIT_TIMEOUT = "" -DATADOG_APPLICATION_ID = "" -DATADOG_CLIENT_TOKEN = "" -DATADOG_SITE = "" -DATADOG_SERVICE_NAME = "" +OTEL_TRACE_EXPORTER_URL = "" +OTEL_SERVICE_NAME = "" diff --git a/src/App.tsx b/src/App.tsx index 0104e07b..f5f2bde5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,7 @@ import { useBreakpoint } from './features/shared'; import { MobileRouter } from './features/mobile'; import { NotificationContainer } from './features/notifications'; import { BREAKPOINT } from './styles/Breakpoint'; +import { exportErrorToOtelCollector } from './opentelemetry'; const baseDocumentTitle = getRuntimeConfigOrThrow().SITE_DEFAULT_TITLE; @@ -25,7 +26,10 @@ function App(): ReactElement { const bp = useBreakpoint(); return ( - }> + } + onError={e => exportErrorToOtelCollector(e)} + > - */ - -import { datadogRum } from '@datadog/browser-rum'; -import { getRuntimeConfigOrThrow } from './utils'; - -/** - * Configures Datadog Real User Monitoring (RUM) for the application. - * - * @see {@link https://docs.datadoghq.com/real_user_monitoring/browser/} - */ -export const configureDatadogRUM = (): void => { - // if (!import.meta.env.PROD) { - // return; - // } - - const { DATADOG_APPLICATION_ID, DATADOG_CLIENT_TOKEN, DATADOG_SERVICE_NAME, DATADOG_SITE } = - getRuntimeConfigOrThrow(); - - if (!DATADOG_APPLICATION_ID || !DATADOG_CLIENT_TOKEN || !DATADOG_SERVICE_NAME || !DATADOG_SITE) { - return; - } - - datadogRum.init({ - applicationId: DATADOG_APPLICATION_ID, - clientToken: DATADOG_CLIENT_TOKEN, - site: DATADOG_SITE, - service: DATADOG_SERVICE_NAME, - env: 'production', - sessionSampleRate: 100, - sessionReplaySampleRate: 100, - trackResources: false, - trackLongTasks: false, - trackUserInteractions: false, - silentMultipleInit: true, - allowFallbackToLocalStorage: true, - }); -}; diff --git a/src/features/routing/constants/routesConfig.tsx b/src/features/routing/constants/routesConfig.tsx index f23e7e86..1b39a35c 100644 --- a/src/features/routing/constants/routesConfig.tsx +++ b/src/features/routing/constants/routesConfig.tsx @@ -3,7 +3,6 @@ * @copyright Yury Korotovskikh */ -import { lazy } from 'react'; import { Navigate } from 'react-router-dom'; import type { RouteObject } from 'react-router-dom'; import { RouterParam } from '@/enums'; diff --git a/src/index.tsx b/src/index.tsx index 077e1744..d161e789 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -15,15 +15,15 @@ import { Provider as StyletronProvider } from 'styletron-react'; import { BaseProvider } from 'baseui'; import App from './App'; import { store } from './redux'; -import { configureDatadogRUM } from './datadog'; +import { initOpentelemetry } from './opentelemetry'; import { reportWebVitals } from './reportWebVitals'; import configureGA from './ga'; //import * as serviceWorkerRegistration from './serviceWorkerRegistration'; import { checkRuntimeConfig } from './utils'; checkRuntimeConfig(); -configureDatadogRUM(); configureGA(); +initOpentelemetry(); const engine = new Styletron(); const { theme } = createTheme(engine, { enableDefaultFonts: false }); diff --git a/src/opentelemetry.ts b/src/opentelemetry.ts new file mode 100644 index 00000000..4dbb2fa0 --- /dev/null +++ b/src/opentelemetry.ts @@ -0,0 +1,58 @@ +/** + * @file Opentelemtry configuration. + * @copyright Yury Korotovskikh + */ + +import { BatchSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web'; +import { OTLPTraceExporter as HttpTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import type { OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { SEMRESATTRS_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; +import { Resource } from '@opentelemetry/resources'; +import { getRuntimeConfigOrThrow } from './utils'; +import packageJson from '../package.json'; + +const { OTEL_TRACE_EXPORTER_URL, OTEL_SERVICE_NAME = packageJson.name } = getRuntimeConfigOrThrow(); + +const exporterOptions = { + url: OTEL_TRACE_EXPORTER_URL, +} satisfies OTLPExporterNodeConfigBase; + +const provider = new WebTracerProvider({ + resource: new Resource({ + [SEMRESATTRS_SERVICE_NAME]: OTEL_SERVICE_NAME, + }), +}); + +const exporter = new HttpTraceExporter(exporterOptions); + +// eslint-disable-next-line jsdoc/require-jsdoc +export const initOpentelemetry = () => { + provider.addSpanProcessor(new BatchSpanProcessor(exporter)); + provider.register(); +}; + +/** + * Register error in opentelemetry. + * + * @param error - Error to be reported. + */ +export const exportErrorToOtelCollector = (error: Error) => { + const span = provider.getTracer('default').startSpan('Operational error'); + + span.setAttribute('message', error.message); + span.recordException(error); + span.setStatus({ code: 2, message: error.message }); + + span.end(); +}; + +// Register global error handler to report uncaught errors. +window.onerror = function (message, _source, _lineno, _colno, error) { + const span = provider.getTracer('default').startSpan('Uncaught error'); + + error && span.recordException(error); + typeof message === 'string' && span.setAttribute('message', message); + span.setStatus({ code: 2, message: typeof message === 'string' ? message : '' }); + + span.end(); +}; diff --git a/src/types/runtime-config.d.ts b/src/types/runtime-config.d.ts index 743e26d6..25a744eb 100644 --- a/src/types/runtime-config.d.ts +++ b/src/types/runtime-config.d.ts @@ -16,10 +16,8 @@ const keys = [ 'GA_TRACKING_ID', 'CIRCUIT_DEVELOPER_GUIDE_URL', 'API_RESPONSE_WAIT_TIMEOUT', - 'DATADOG_APPLICATION_ID', - 'DATADOG_CLIENT_TOKEN', - 'DATADOG_SITE', - 'DATADOG_SERVICE_NAME', + 'OTEL_TRACE_EXPORTER_URL', + 'OTEL_SERVICE_NAME', ] as const; type RuntimConfigKeys = (typeof keys)[number]; diff --git a/src/utils/runtimeConfig/checkRuntimeConfig.ts b/src/utils/runtimeConfig/checkRuntimeConfig.ts index c6684d10..0633fe13 100644 --- a/src/utils/runtimeConfig/checkRuntimeConfig.ts +++ b/src/utils/runtimeConfig/checkRuntimeConfig.ts @@ -15,10 +15,6 @@ const requiredEnvs: Array = [ 'PROOFMARKET_TOOLCHAIN_REPO', 'SITE_DEFAULT_TITLE', 'CIRCUIT_DEVELOPER_GUIDE_URL', - 'DATADOG_APPLICATION_ID', - 'DATADOG_CLIENT_TOKEN', - 'DATADOG_SITE', - 'DATADOG_SERVICE_NAME', ]; /**