Refactor monitoring services into separate packages
Separated and isolated the responsibilities of monitoring tools. Reorganized the code by introducing a core package that contains common code related to monitoring and moved all the service operations like error capturing and identification of users into their respective packages. This ensures each tool is independent and easy to maintain.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
# Monitoring / @kit/monitoring
|
||||
|
||||
Please set the following environment variable to your preferred monitoring provider:
|
||||
|
||||
```
|
||||
NEXT_PUBLIC_MONITORING_PROVIDER=
|
||||
MONITORING_INSTRUMENTATION_ENABLED=true
|
||||
```
|
||||
|
||||
## Available Providers
|
||||
|
||||
To use a specific provider, set the `NEXT_PUBLIC_MONITORING_PROVIDER` environment variable to one of the following values:
|
||||
|
||||
1. Baselime: `baselime`
|
||||
2. Sentry: `sentry`
|
||||
|
||||
## Baselime
|
||||
|
||||
To use Baselime, set the `NEXT_PUBLIC_MONITORING_PROVIDER` environment variable to `baselime`.
|
||||
|
||||
```
|
||||
NEXT_PUBLIC_MONITORING_PROVIDER=baselime
|
||||
```
|
||||
|
||||
## Sentry
|
||||
|
||||
To use Sentry, set the `NEXT_PUBLIC_MONITORING_PROVIDER` environment variable to `sentry`.
|
||||
|
||||
```
|
||||
NEXT_PUBLIC_MONITORING_PROVIDER=sentry
|
||||
```
|
||||
|
||||
## Instrumentation
|
||||
|
||||
To enable instrumentation, set the `MONITORING_INSTRUMENTATION_ENABLED` environment variable to `true`.
|
||||
|
||||
```
|
||||
MONITORING_INSTRUMENTATION_ENABLED=true
|
||||
```
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "@kit/monitoring",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"clean": "git clean -xdf ../.turbo node_modules",
|
||||
"format": "prettier --check \"**/*.{ts,tsx}\"",
|
||||
"lint": "eslint ..",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"prettier": "@kit/prettier-config",
|
||||
"exports": {
|
||||
"./server": "./src/server.ts",
|
||||
"./instrumentation": "./src/instrumentation.ts",
|
||||
"./hooks": "./src/hooks/index.ts",
|
||||
"./components": "./src/components/index.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/monitoring-core": "workspace:*",
|
||||
"@kit/baselime": "workspace:*",
|
||||
"@kit/eslint-config": "workspace:*",
|
||||
"@kit/prettier-config": "workspace:*",
|
||||
"@kit/sentry": "workspace:*",
|
||||
"@kit/tailwind-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@types/react": "^18.2.79",
|
||||
"react": "18.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"extends": [
|
||||
"@kit/eslint-config/base",
|
||||
"@kit/eslint-config/react"
|
||||
]
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-table": "^8.16.0",
|
||||
"next": "14.3.0-canary.7",
|
||||
"tailwind-merge": "^2.3.0",
|
||||
"zod": "^3.23.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { ErrorInfo, ReactNode } from 'react';
|
||||
import { Component } from 'react';
|
||||
|
||||
interface Props {
|
||||
onError?: (error: Error, info: ErrorInfo) => void;
|
||||
fallback: ReactNode;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export class ErrorBoundary extends Component<Props> {
|
||||
readonly state = { hasError: false, error: null };
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: unknown) {
|
||||
return {
|
||||
hasError: true,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: ErrorInfo) {
|
||||
this.props.onError?.(error, info);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return this.props.fallback;
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './error-boundary';
|
||||
export * from './provider';
|
||||
@@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
import { lazy } from 'react';
|
||||
|
||||
import { getMonitoringProvider } from '../get-monitoring-provider';
|
||||
import { InstrumentationProvider } from '../monitoring-providers.enum';
|
||||
|
||||
const BaselimeProvider = lazy(async () => {
|
||||
const { BaselimeProvider } = await import('@kit/baselime/provider');
|
||||
|
||||
return {
|
||||
default: BaselimeProvider,
|
||||
};
|
||||
});
|
||||
|
||||
const SentryProvider = lazy(async () => {
|
||||
const { SentryProvider } = await import('@kit/sentry/provider');
|
||||
|
||||
return {
|
||||
default: SentryProvider,
|
||||
};
|
||||
});
|
||||
|
||||
type Config = {
|
||||
provider: InstrumentationProvider;
|
||||
providerToken: string;
|
||||
};
|
||||
|
||||
export function MonitoringProvider(
|
||||
props: React.PropsWithChildren<{ config?: Config }>,
|
||||
) {
|
||||
const provider = getMonitoringProvider();
|
||||
|
||||
if (!props.config) {
|
||||
return <>{props.children}</>;
|
||||
}
|
||||
|
||||
switch (provider) {
|
||||
case InstrumentationProvider.Baselime:
|
||||
return (
|
||||
<BaselimeProvider apiKey={props.config?.providerToken} enableWebVitals>
|
||||
{props.children}
|
||||
</BaselimeProvider>
|
||||
);
|
||||
|
||||
case InstrumentationProvider.Sentry:
|
||||
return <SentryProvider>{props.children}</SentryProvider>;
|
||||
|
||||
default:
|
||||
return <>{props.children}</>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { InstrumentationProvider } from './monitoring-providers.enum';
|
||||
|
||||
export function getMonitoringProvider() {
|
||||
return process.env.NEXT_PUBLIC_MONITORING_PROVIDER as
|
||||
| InstrumentationProvider
|
||||
| undefined;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './use-monitoring';
|
||||
export * from './use-capture-exception';
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useMonitoring } from './use-monitoring';
|
||||
|
||||
export function useCaptureException(error: Error) {
|
||||
const service = useMonitoring();
|
||||
|
||||
return service.captureException(error);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { MonitoringContext } from '@kit/monitoring-core';
|
||||
|
||||
/**
|
||||
* @name useMonitoring
|
||||
* @description Asynchronously load the monitoring service based on the MONITORING_PROVIDER environment variable.
|
||||
* Use Suspense to suspend while loading the service.
|
||||
*/
|
||||
export function useMonitoring() {
|
||||
return useContext(MonitoringContext);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { getMonitoringProvider } from './get-monitoring-provider';
|
||||
import { InstrumentationProvider } from './monitoring-providers.enum';
|
||||
|
||||
const PROVIDER = getMonitoringProvider();
|
||||
|
||||
/**
|
||||
* @name registerMonitoringInstrumentation
|
||||
* @description Register monitoring instrumentation based on the MONITORING_PROVIDER environment variable.
|
||||
*
|
||||
* Please set the MONITORING_PROVIDER environment variable to register the monitoring instrumentation provider.
|
||||
*/
|
||||
export async function registerMonitoringInstrumentation() {
|
||||
if (!PROVIDER) {
|
||||
console.info(`No instrumentation provider specified. Skipping...`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (PROVIDER) {
|
||||
case InstrumentationProvider.Baselime: {
|
||||
const { registerInstrumentation } = await import(
|
||||
'@kit/baselime/instrumentation'
|
||||
);
|
||||
|
||||
return registerInstrumentation();
|
||||
}
|
||||
|
||||
case InstrumentationProvider.Sentry: {
|
||||
const { registerInstrumentation } = await import(
|
||||
'@kit/sentry/instrumentation'
|
||||
);
|
||||
|
||||
return registerInstrumentation();
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown instrumentation provider: ${process.env.MONITORING_PROVIDER}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum InstrumentationProvider {
|
||||
Baselime = 'baselime',
|
||||
Sentry = 'sentry',
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './services/get-server-monitoring-service';
|
||||
@@ -0,0 +1,44 @@
|
||||
import { ConsoleMonitoringService } from '@kit/monitoring-core';
|
||||
|
||||
import { getMonitoringProvider } from '../get-monitoring-provider';
|
||||
import { InstrumentationProvider } from '../monitoring-providers.enum';
|
||||
|
||||
const MONITORING_PROVIDER = getMonitoringProvider();
|
||||
|
||||
/**
|
||||
* @name getServerMonitoringService
|
||||
* @description Get the monitoring service based on the MONITORING_PROVIDER environment variable.
|
||||
*/
|
||||
export async function getServerMonitoringService() {
|
||||
if (!MONITORING_PROVIDER) {
|
||||
console.info(
|
||||
`No instrumentation provider specified. Returning console service...`,
|
||||
);
|
||||
|
||||
return new ConsoleMonitoringService();
|
||||
}
|
||||
|
||||
switch (MONITORING_PROVIDER) {
|
||||
case InstrumentationProvider.Baselime: {
|
||||
const { BaselimeServerMonitoringService } = await import(
|
||||
'@kit/baselime/server'
|
||||
);
|
||||
|
||||
return new BaselimeServerMonitoringService();
|
||||
}
|
||||
|
||||
case InstrumentationProvider.Sentry: {
|
||||
const { SentryServerMonitoringService } = await import(
|
||||
'@kit/sentry/server'
|
||||
);
|
||||
|
||||
return new SentryServerMonitoringService();
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new Error(
|
||||
`Please set the MONITORING_PROVIDER environment variable to register the monitoring instrumentation provider.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@kit/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||
},
|
||||
"include": ["*.ts", "src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user