feat(telemetry): add Sentry as frontend telemetry (#1458)

Co-authored-by: sealday <zhanglin@daoyoucloud.com>
Reviewed-on: daoyoucloud/tachybase#1458
Reviewed-by: sealday <zhanglin@daoyoucloud.com>
Co-authored-by: TomyJan <TomyJan6@gmail.com>
Co-committed-by: TomyJan <TomyJan6@gmail.com>
This commit is contained in:
TomyJan 2024-08-14 14:32:14 +08:00 committed by sealday
parent f020a2a4d3
commit 0dc784c2be
8 changed files with 178 additions and 4 deletions

View File

@ -58,9 +58,17 @@ INIT_ROOT_USERNAME=tachybase
# TELEMETRY_ENABLED=on # 是否启用遥测 # TELEMETRY_ENABLED=on # 是否启用遥测
# TELEMETRY_SERVICE_NAME=tachybase-default-demo # 服务名称,默认为 tachybase-main # TELEMETRY_SERVICE_NAME=tachybase-default-demo # 服务名称,默认为 tachybase-main
# OTEL_LOG_LEVEL=debug # OpenTelemetry 日志级别,仅输出到控制台,见 [官方文档](https://open-telemetry.github.io/opentelemetry-js/enums/_opentelemetry_api.DiagLogLevel.html) ,不配置则默认不输出日志 # OTEL_LOG_LEVEL=debug # OpenTelemetry 日志级别,仅输出到控制台,见 https://open-telemetry.github.io/opentelemetry-js/enums/_opentelemetry_api.DiagLogLevel.html ,不配置则默认不输出日志
# OTEL_METRICS_READER=console,prometheus # 指标读取器两个均为内置console 为控制台prometheus 为 prometheus 服务器 # OTEL_METRICS_READER=console,prometheus # 指标读取器两个均为内置console 为控制台prometheus 为 prometheus 服务器
# OTEL_PROMETHEUS_SERVER=on # 是否启用 Prometheus 服务,用于创建服务器以供导出指标数据 # OTEL_PROMETHEUS_SERVER=on # 是否启用 Prometheus 服务,用于创建服务器以供导出指标数据
# OTEL_PROMETHEUS_PORT=9464 # Prometheus 服务器端口,默认为 9464注意此服务器没有鉴权 # OTEL_PROMETHEUS_PORT=9464 # Prometheus 服务器端口,默认为 9464注意此服务器没有鉴权
# OTEL_TRACES_PROCESSOR=console,otlp # 链路追踪处理器两个均为内置console 为控制台otlp 为 OTLP 规范的追踪服务器 # OTEL_TRACES_PROCESSOR=console,otlp # 链路追踪处理器两个均为内置console 为控制台otlp 为 OTLP 规范的追踪服务器
# OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_GRPC=http://localhost:4317 # [OTLP](https://opentelemetry.io/docs/specs/otlp/) 规范的追踪服务器的 gRPC 端点地址 # OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_GRPC=http://localhost:4317 # OTLP https://opentelemetry.io/docs/specs/otlp/ 规范的追踪服务器的 gRPC 端点地址
# 下面为前端 Sentry 跟踪配置,注意修改后需要重新 build 前端代码
# SENTRY_DSN= # Sentry DSN用于前端遥测
# SENTRY_TRACE_ENABLE=on # 是否启用 Sentry 跟踪,文档 https://docs.sentry.io/platforms/javascript/guides/react/tracing/
# SENTRY_TRACE_SAMPLE_RATE=1.0 # Sentry 跟踪采样率1.0 为 100%0.0 为 0%,默认为 1.0
# SENTRY_TRACE_PROPAGATION_TARGETS=localhost,/^\// # Sentry 跟踪传播目标过滤器,多个用逗号分隔,支持正则,默认为 localhost,/^\// ,文档 https://docs.sentry.io/platforms/javascript/tracing/instrumentation/automatic-instrumentation/#tracepropagationtargets
# SENTRY_SESSION_REPLAY_ENABLE=on # 是否启用 Sentry 会话重放,文档 https://docs.sentry.io/platforms/javascript/guides/react/session-replay/
# SENTRY_SESSION_REPLAY_SAMPLE_RATE=0.1 # Sentry 会话重放采样率1.0 为 100%0.0 为 0%,默认为 0.1
# SENTRY_SESSION_REPLAY_ONERROR_SAMPLE_RATE=1.0 # Sentry 会话重放错误采样率1.0 为 100%0.0 为 0%,默认为 1.0

View File

@ -31,4 +31,11 @@ jobs:
- name: Publish - name: Publish
env: env:
NODE_AUTH_TOKEN: ${{ secrets.NPMTOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPMTOKEN }}
SENTRY_DSN: ${{ vars.SENTRY_DSN }}
SENTRY_TRACE_ENABLE: ${{ vars.SENTRY_TRACE_ENABLE }}
SENTRY_TRACE_SAMPLE_RATE: ${{ vars.SENTRY_TRACE_SAMPLE_RATE }}
SENTRY_TRACE_PROPAGATION_TARGETS: ${{ vars.SENTRY_TRACE_PROPAGATION_TARGETS }}
SENTRY_SESSION_REPLAY_ENABLE: ${{ vars.SENTRY_SESSION_REPLAY_ENABLE }}
SENTRY_SESSION_REPLAY_SAMPLE_RATE: ${{ vars.SENTRY_SESSION_REPLAY_SAMPLE_RATE }}
SENTRY_SESSION_REPLAY_ONERROR_SAMPLE_RATE: ${{ vars.SENTRY_SESSION_REPLAY_ONERROR_SAMPLE_RATE }}
run: pnpm -r publish --access=public --no-git-checks --publish-branch ${{ gitea.ref }} run: pnpm -r publish --access=public --no-git-checks --publish-branch ${{ gitea.ref }}

View File

@ -57,6 +57,13 @@ export default defineConfig({
define: { define: {
...umiConfig.define, ...umiConfig.define,
'process.env.USE_REMOTE_PLUGIN': process.env.USE_REMOTE_PLUGIN, 'process.env.USE_REMOTE_PLUGIN': process.env.USE_REMOTE_PLUGIN,
'process.env.SENTRY_DSN': process.env.SENTRY_DSN,
'process.env.SENTRY_TRACE_ENABLE': process.env.SENTRY_TRACE_ENABLE,
'process.env.SENTRY_TRACE_SAMPLE_RATE': process.env.SENTRY_TRACE_SAMPLE_RATE,
'process.env.SENTRY_TRACE_PROPAGATION_TARGETS': process.env.SENTRY_TRACE_PROPAGATION_TARGETS,
'process.env.SENTRY_SESSION_REPLAY_ENABLE': process.env.SENTRY_SESSION_REPLAY_ENABLE,
'process.env.SENTRY_SESSION_REPLAY_SAMPLE_RATE': process.env.SENTRY_SESSION_REPLAY_SAMPLE_RATE,
'process.env.SENTRY_SESSION_REPLAY_ONERROR_SAMPLE_RATE': process.env.SENTRY_SESSION_REPLAY_ONERROR_SAMPLE_RATE,
}, },
proxy: { proxy: {
...umiConfig.proxy, ...umiConfig.proxy,

View File

@ -1,4 +1,4 @@
import '@tachybase/telemetry'; // 尽早实例化,以尽力保证插桩成功TODO: Koa 依然无法成功 hook import '@tachybase/telemetry'; // 尽早实例化,以尽力保证插桩成功
import { Gateway } from '@tachybase/server'; import { Gateway } from '@tachybase/server';

View File

@ -18,6 +18,7 @@
"@dnd-kit/sortable": "^8.0.0", "@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
"@emotion/css": "^11.7.1", "@emotion/css": "^11.7.1",
"@sentry/react": "^8.25.0",
"@tachybase/components": "workspace:*", "@tachybase/components": "workspace:*",
"@tachybase/evaluators": "workspace:*", "@tachybase/evaluators": "workspace:*",
"@tachybase/schema": "workspace:*", "@tachybase/schema": "workspace:*",

View File

@ -2,6 +2,7 @@ import React, { ComponentType, ReactElement } from 'react';
import { define, observable } from '@tachybase/schema'; import { define, observable } from '@tachybase/schema';
import { APIClientOptions, getSubAppName } from '@tachybase/sdk'; import { APIClientOptions, getSubAppName } from '@tachybase/sdk';
import * as Sentry from '@sentry/react';
import { i18n as i18next } from 'i18next'; import { i18n as i18next } from 'i18next';
import { get, merge, set } from 'lodash'; import { get, merge, set } from 'lodash';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
@ -273,6 +274,59 @@ export class Application {
}; };
console.error(error, this.error); console.error(error, this.error);
} }
if (process.env.APP_ENV === 'production') {
const dsn = process.env.SENTRY_DSN;
if (dsn) {
const integrations = [];
let tracesSampleRate = 1.0;
let tracePropagationTargets = ['localhost', /^\//];
if (process.env.SENTRY_TRACE_ENABLE === 'on') {
integrations.push(Sentry.browserTracingIntegration());
if (Number(process.env.SENTRY_TRACE_SAMPLE_RATE) >= 0 && Number(process.env.SENTRY_TRACE_SAMPLE_RATE) <= 1) {
tracesSampleRate = parseFloat(process.env.SENTRY_TRACE_SAMPLE_RATE);
}
if (process.env.SENTRY_TRACE_PROPAGATION_TARGETS) {
tracePropagationTargets = process.env.SENTRY_TRACE_PROPAGATION_TARGETS.split(',').map((target) => {
if (target.startsWith('/')) {
return new RegExp(target);
}
return target;
});
}
}
let replaysSessionSampleRate = 0.1;
let replaysOnErrorSampleRate = 1.0;
if (process.env.SENTRY_SESSION_REPLAY_ENABLE === 'on') {
integrations.push(Sentry.replayIntegration());
if (
Number(process.env.SENTRY_SESSION_REPLAY_SAMPLE_RATE) >= 0 &&
Number(process.env.SENTRY_SESSION_REPLAY_SAMPLE_RATE) <= 1
) {
replaysSessionSampleRate = parseFloat(process.env.SENTRY_SESSION_REPLAY_SAMPLE_RATE);
}
if (
Number(process.env.SENTRY_SESSION_REPLAY_ONERROR_SAMPLE_RATE) >= 0 &&
Number(process.env.SENTRY_SESSION_REPLAY_ONERROR_SAMPLE_RATE) <= 1
) {
replaysOnErrorSampleRate = parseFloat(process.env.SENTRY_SESSION_REPLAY_ON_ERROR_SAMPLE_RATE);
}
}
Sentry.init({
dsn,
integrations,
// Performance Monitoring
tracesSampleRate, // Capture 100% of the transactions
// Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
tracePropagationTargets,
// Session Replay
replaysSessionSampleRate, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
replaysOnErrorSampleRate, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
});
} else {
console.log('SENTRY_DSN is not set, Sentry will not be initialized');
}
}
this.loading = false; this.loading = false;
} }

View File

@ -1,5 +1,5 @@
export const telemetryOptions = { export const telemetryOptions = {
enabled: process.env.TELEMETRY_ENABLED === 'on', enabled: process.env.TELEMETRY_ENABLED === 'on' && process.env.APP_ENV === 'production',
metric: { metric: {
readerName: process.env.OTEL_METRICS_READER, readerName: process.env.OTEL_METRICS_READER,
}, },

View File

@ -473,6 +473,9 @@ importers:
'@emotion/css': '@emotion/css':
specifier: ^11.7.1 specifier: ^11.7.1
version: 11.11.2 version: 11.11.2
'@sentry/react':
specifier: ^8.25.0
version: 8.25.0(react@18.3.1)
'@tachybase/components': '@tachybase/components':
specifier: workspace:* specifier: workspace:*
version: link:../components version: link:../components
@ -8672,10 +8675,34 @@ packages:
'@scure/bip39@1.3.0': '@scure/bip39@1.3.0':
resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==}
'@sentry-internal/browser-utils@8.25.0':
resolution: {integrity: sha512-nlWgp1lVhNQOTUplW85G3qm0fOIgAhJ/sl/31OIuScVrITYhYDF2bO+Zv/jQ8YsdUBAUXqY1tPT9wwPJklnPhw==}
engines: {node: '>=14.18'}
'@sentry-internal/feedback@8.25.0':
resolution: {integrity: sha512-327I5XJAFrsgjc5qUKxZ9rff3WNCfGvf1fIii70LQ2YQhQgG4XHZILmkD06ETEyXb+H1tkrNQQEJ1/d4ai+q5g==}
engines: {node: '>=14.18'}
'@sentry-internal/replay-canvas@8.25.0':
resolution: {integrity: sha512-dPXlkAbkFL1DBum8rGTaHS+apJKaXEZJF9gLcBBKTruhTCizrugFLxajzIfVSiFVuwNKuJWa2fzhzbeQM0ee7w==}
engines: {node: '>=14.18'}
'@sentry-internal/replay@8.25.0':
resolution: {integrity: sha512-3f7x8EYthyj157uV9V8vBjun+1gJnHhh2+i0qxYLhMGx7N2Fq0J3Bvvo1rosSg+fYh5HzPNZDufwIRdg5C/MQw==}
engines: {node: '>=14.18'}
'@sentry/browser@8.25.0':
resolution: {integrity: sha512-51bdVGXjyooqVGzaSGsnExqRTt9NvZ1zGFsxbbCSXi5UoEFN6zdMUz6jKYsL2K80eeELP2VKOVlobHlEzeJQfw==}
engines: {node: '>=14.18'}
'@sentry/core@5.30.0': '@sentry/core@5.30.0':
resolution: {integrity: sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==} resolution: {integrity: sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==}
engines: {node: '>=6'} engines: {node: '>=6'}
'@sentry/core@8.25.0':
resolution: {integrity: sha512-7KtglbrW1eX4DOHkf6i4rRIExEf2CgtQ99qZ8gn5FUaAmNMg0rK7bb1yZMx0RZtp5G1TSz/S0jQQgxHWebaEig==}
engines: {node: '>=14.18'}
'@sentry/hub@5.30.0': '@sentry/hub@5.30.0':
resolution: {integrity: sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==} resolution: {integrity: sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -8688,6 +8715,12 @@ packages:
resolution: {integrity: sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==} resolution: {integrity: sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==}
engines: {node: '>=6'} engines: {node: '>=6'}
'@sentry/react@8.25.0':
resolution: {integrity: sha512-A3QeSCJEa+lpo5nh0kxKeqsmnuW2nycKNN/0bpXPv5T5jiEfExSmEBVM0zutrQpf+J0WRIl1AGunUYGPO+GPQg==}
engines: {node: '>=14.18'}
peerDependencies:
react: ^16.14.0 || 17.x || 18.x || 19.x
'@sentry/tracing@5.30.0': '@sentry/tracing@5.30.0':
resolution: {integrity: sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==} resolution: {integrity: sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -8696,10 +8729,18 @@ packages:
resolution: {integrity: sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==} resolution: {integrity: sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==}
engines: {node: '>=6'} engines: {node: '>=6'}
'@sentry/types@8.25.0':
resolution: {integrity: sha512-ojim0gDcRhGJPguYrtms4FsprX4xZz3LGNk9Z0hwTbSVEdlhQIInsQ7CYcdM3sjUs+qT7kfpxTRZGUeZNRRJcA==}
engines: {node: '>=14.18'}
'@sentry/utils@5.30.0': '@sentry/utils@5.30.0':
resolution: {integrity: sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==} resolution: {integrity: sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==}
engines: {node: '>=6'} engines: {node: '>=6'}
'@sentry/utils@8.25.0':
resolution: {integrity: sha512-mVlkV7S62ZZ2jM38/kOwWx2xoW8fUv2cjw2IwFKoAIPyLBh3mo1WJtvfdtN/rXGjQWZJBKW53EWaWnD00rkjyA==}
engines: {node: '>=14.18'}
'@sinclair/typebox@0.27.8': '@sinclair/typebox@0.27.8':
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
@ -24734,6 +24775,42 @@ snapshots:
'@noble/hashes': 1.4.0 '@noble/hashes': 1.4.0
'@scure/base': 1.1.7 '@scure/base': 1.1.7
'@sentry-internal/browser-utils@8.25.0':
dependencies:
'@sentry/core': 8.25.0
'@sentry/types': 8.25.0
'@sentry/utils': 8.25.0
'@sentry-internal/feedback@8.25.0':
dependencies:
'@sentry/core': 8.25.0
'@sentry/types': 8.25.0
'@sentry/utils': 8.25.0
'@sentry-internal/replay-canvas@8.25.0':
dependencies:
'@sentry-internal/replay': 8.25.0
'@sentry/core': 8.25.0
'@sentry/types': 8.25.0
'@sentry/utils': 8.25.0
'@sentry-internal/replay@8.25.0':
dependencies:
'@sentry-internal/browser-utils': 8.25.0
'@sentry/core': 8.25.0
'@sentry/types': 8.25.0
'@sentry/utils': 8.25.0
'@sentry/browser@8.25.0':
dependencies:
'@sentry-internal/browser-utils': 8.25.0
'@sentry-internal/feedback': 8.25.0
'@sentry-internal/replay': 8.25.0
'@sentry-internal/replay-canvas': 8.25.0
'@sentry/core': 8.25.0
'@sentry/types': 8.25.0
'@sentry/utils': 8.25.0
'@sentry/core@5.30.0': '@sentry/core@5.30.0':
dependencies: dependencies:
'@sentry/hub': 5.30.0 '@sentry/hub': 5.30.0
@ -24742,6 +24819,11 @@ snapshots:
'@sentry/utils': 5.30.0 '@sentry/utils': 5.30.0
tslib: 1.14.1 tslib: 1.14.1
'@sentry/core@8.25.0':
dependencies:
'@sentry/types': 8.25.0
'@sentry/utils': 8.25.0
'@sentry/hub@5.30.0': '@sentry/hub@5.30.0':
dependencies: dependencies:
'@sentry/types': 5.30.0 '@sentry/types': 5.30.0
@ -24768,6 +24850,15 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@sentry/react@8.25.0(react@18.3.1)':
dependencies:
'@sentry/browser': 8.25.0
'@sentry/core': 8.25.0
'@sentry/types': 8.25.0
'@sentry/utils': 8.25.0
hoist-non-react-statics: 3.3.2
react: 18.3.1
'@sentry/tracing@5.30.0': '@sentry/tracing@5.30.0':
dependencies: dependencies:
'@sentry/hub': 5.30.0 '@sentry/hub': 5.30.0
@ -24778,11 +24869,17 @@ snapshots:
'@sentry/types@5.30.0': {} '@sentry/types@5.30.0': {}
'@sentry/types@8.25.0': {}
'@sentry/utils@5.30.0': '@sentry/utils@5.30.0':
dependencies: dependencies:
'@sentry/types': 5.30.0 '@sentry/types': 5.30.0
tslib: 1.14.1 tslib: 1.14.1
'@sentry/utils@8.25.0':
dependencies:
'@sentry/types': 8.25.0
'@sinclair/typebox@0.27.8': {} '@sinclair/typebox@0.27.8': {}
'@sindresorhus/is@0.14.0': {} '@sindresorhus/is@0.14.0': {}