mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 14:56:59 +08:00
refactor(server): config system (#11081)
This commit is contained in:
@@ -1,33 +1,16 @@
|
||||
import { defineStartupConfig, ModuleConfig } from '../config';
|
||||
import { defineModuleConfig } from '../config';
|
||||
|
||||
declare module '../config' {
|
||||
interface AppConfig {
|
||||
metrics: ModuleConfig<{
|
||||
/**
|
||||
* Enable metric and tracing collection
|
||||
*/
|
||||
declare global {
|
||||
interface AppConfigSchema {
|
||||
metrics: {
|
||||
enabled: boolean;
|
||||
/**
|
||||
* Enable telemetry
|
||||
*/
|
||||
telemetry: {
|
||||
enabled: boolean;
|
||||
token: string;
|
||||
};
|
||||
customerIo: {
|
||||
token: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
defineStartupConfig('metrics', {
|
||||
enabled: false,
|
||||
telemetry: {
|
||||
enabled: false,
|
||||
token: '',
|
||||
},
|
||||
customerIo: {
|
||||
token: '',
|
||||
defineModuleConfig('metrics', {
|
||||
enabled: {
|
||||
desc: 'Enable metric and tracing collection',
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,54 +1,14 @@
|
||||
import './config';
|
||||
|
||||
import {
|
||||
Global,
|
||||
Module,
|
||||
OnModuleDestroy,
|
||||
OnModuleInit,
|
||||
Provider,
|
||||
} from '@nestjs/common';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { NodeSDK } from '@opentelemetry/sdk-node';
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
|
||||
import { Config } from '../config';
|
||||
import {
|
||||
LocalOpentelemetryFactory,
|
||||
OpentelemetryFactory,
|
||||
registerCustomMetrics,
|
||||
} from './opentelemetry';
|
||||
|
||||
const factorProvider: Provider = {
|
||||
provide: OpentelemetryFactory,
|
||||
useFactory: (config: Config) => {
|
||||
return config.metrics.enabled ? new LocalOpentelemetryFactory() : null;
|
||||
},
|
||||
inject: [Config],
|
||||
};
|
||||
import { OpentelemetryFactory } from './opentelemetry';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [factorProvider],
|
||||
exports: [factorProvider],
|
||||
providers: [OpentelemetryFactory],
|
||||
})
|
||||
export class MetricsModule implements OnModuleInit, OnModuleDestroy {
|
||||
private sdk: NodeSDK | null = null;
|
||||
constructor(private readonly ref: ModuleRef) {}
|
||||
|
||||
onModuleInit() {
|
||||
const factor = this.ref.get(OpentelemetryFactory, { strict: false });
|
||||
if (factor) {
|
||||
this.sdk = factor.create();
|
||||
this.sdk.start();
|
||||
registerCustomMetrics();
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
if (this.sdk) {
|
||||
await this.sdk.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
export class MetricsModule {}
|
||||
|
||||
export * from './metrics';
|
||||
export * from './utils';
|
||||
|
||||
@@ -2,11 +2,28 @@ import {
|
||||
Gauge,
|
||||
Histogram,
|
||||
Meter,
|
||||
MeterProvider,
|
||||
MetricOptions,
|
||||
metrics as otelMetrics,
|
||||
UpDownCounter,
|
||||
} from '@opentelemetry/api';
|
||||
import { HostMetrics } from '@opentelemetry/host-metrics';
|
||||
|
||||
import { getMeter } from './opentelemetry';
|
||||
function getMeterProvider() {
|
||||
return otelMetrics.getMeterProvider();
|
||||
}
|
||||
|
||||
export function registerCustomMetrics() {
|
||||
const hostMetricsMonitoring = new HostMetrics({
|
||||
name: 'instance-host-metrics',
|
||||
meterProvider: getMeterProvider() as MeterProvider,
|
||||
});
|
||||
hostMetricsMonitoring.start();
|
||||
}
|
||||
|
||||
export function getMeter(name = 'business') {
|
||||
return getMeterProvider().getMeter(name);
|
||||
}
|
||||
|
||||
type MetricType = 'counter' | 'gauge' | 'histogram';
|
||||
type Metric<T extends MetricType> = T extends 'counter'
|
||||
@@ -122,5 +139,3 @@ export const metrics = new Proxy<Record<KnownMetricScopes, ScopedMetrics>>(
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export function stopMetrics() {}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { OnModuleDestroy } from '@nestjs/common';
|
||||
import { metrics } from '@opentelemetry/api';
|
||||
import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common';
|
||||
import {
|
||||
CompositePropagator,
|
||||
W3CBaggagePropagator,
|
||||
@@ -7,7 +6,6 @@ import {
|
||||
} from '@opentelemetry/core';
|
||||
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
|
||||
import { ZipkinExporter } from '@opentelemetry/exporter-zipkin';
|
||||
import { HostMetrics } from '@opentelemetry/host-metrics';
|
||||
import { Instrumentation } from '@opentelemetry/instrumentation';
|
||||
import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql';
|
||||
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
|
||||
@@ -15,7 +13,6 @@ import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
|
||||
import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
|
||||
import { SocketIoInstrumentation } from '@opentelemetry/instrumentation-socket.io';
|
||||
import { Resource } from '@opentelemetry/resources';
|
||||
import type { MeterProvider } from '@opentelemetry/sdk-metrics';
|
||||
import { MetricProducer, MetricReader } from '@opentelemetry/sdk-metrics';
|
||||
import { NodeSDK } from '@opentelemetry/sdk-node';
|
||||
import {
|
||||
@@ -30,11 +27,14 @@ import {
|
||||
} from '@opentelemetry/semantic-conventions/incubating';
|
||||
import prismaInstrument from '@prisma/instrumentation';
|
||||
|
||||
import { Config } from '../config';
|
||||
import { OnEvent } from '../event/def';
|
||||
import { registerCustomMetrics } from './metrics';
|
||||
import { PrismaMetricProducer } from './prisma';
|
||||
|
||||
const { PrismaInstrumentation } = prismaInstrument;
|
||||
|
||||
export abstract class OpentelemetryFactory {
|
||||
export abstract class BaseOpentelemetryFactory {
|
||||
abstract getMetricReader(): MetricReader;
|
||||
abstract getSpanExporter(): SpanExporter;
|
||||
|
||||
@@ -55,9 +55,9 @@ export abstract class OpentelemetryFactory {
|
||||
|
||||
getResource() {
|
||||
return new Resource({
|
||||
[ATTR_K8S_NAMESPACE_NAME]: AFFiNE.AFFINE_ENV,
|
||||
[ATTR_SERVICE_NAME]: AFFiNE.flavor.type,
|
||||
[ATTR_SERVICE_VERSION]: AFFiNE.version,
|
||||
[ATTR_K8S_NAMESPACE_NAME]: env.NAMESPACE,
|
||||
[ATTR_SERVICE_NAME]: env.FLAVOR,
|
||||
[ATTR_SERVICE_VERSION]: env.version,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -81,39 +81,58 @@ export abstract class OpentelemetryFactory {
|
||||
}
|
||||
}
|
||||
|
||||
export class LocalOpentelemetryFactory
|
||||
extends OpentelemetryFactory
|
||||
@Injectable()
|
||||
export class OpentelemetryFactory
|
||||
extends BaseOpentelemetryFactory
|
||||
implements OnModuleDestroy
|
||||
{
|
||||
private readonly metricsExporter = new PrometheusExporter({
|
||||
metricProducers: this.getMetricsProducers(),
|
||||
});
|
||||
private readonly logger = new Logger(OpentelemetryFactory.name);
|
||||
#sdk: NodeSDK | null = null;
|
||||
|
||||
constructor(private readonly config: Config) {
|
||||
super();
|
||||
}
|
||||
|
||||
@OnEvent('config.init')
|
||||
async init(event: Events['config.init']) {
|
||||
if (event.config.metrics.enabled) {
|
||||
await this.setup();
|
||||
registerCustomMetrics();
|
||||
}
|
||||
}
|
||||
|
||||
@OnEvent('config.changed')
|
||||
async onConfigChanged(event: Events['config.changed']) {
|
||||
if ('metrics' in event.updates) {
|
||||
await this.setup();
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
await this.metricsExporter.shutdown();
|
||||
await this.#sdk?.shutdown();
|
||||
}
|
||||
|
||||
override getMetricReader(): MetricReader {
|
||||
return this.metricsExporter;
|
||||
return new PrometheusExporter({
|
||||
metricProducers: this.getMetricsProducers(),
|
||||
});
|
||||
}
|
||||
|
||||
override getSpanExporter(): SpanExporter {
|
||||
return new ZipkinExporter();
|
||||
}
|
||||
}
|
||||
|
||||
function getMeterProvider() {
|
||||
return metrics.getMeterProvider();
|
||||
}
|
||||
|
||||
export function registerCustomMetrics() {
|
||||
const hostMetricsMonitoring = new HostMetrics({
|
||||
name: 'instance-host-metrics',
|
||||
meterProvider: getMeterProvider() as MeterProvider,
|
||||
});
|
||||
hostMetricsMonitoring.start();
|
||||
}
|
||||
|
||||
export function getMeter(name = 'business') {
|
||||
return getMeterProvider().getMeter(name);
|
||||
private async setup() {
|
||||
if (this.config.metrics.enabled) {
|
||||
if (!this.#sdk) {
|
||||
this.#sdk = this.create();
|
||||
}
|
||||
this.#sdk.start();
|
||||
this.logger.log('OpenTelemetry SDK started');
|
||||
} else {
|
||||
await this.#sdk?.shutdown();
|
||||
this.#sdk = null;
|
||||
this.logger.log('OpenTelemetry SDK stopped');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
ScopeMetrics,
|
||||
} from '@opentelemetry/sdk-metrics';
|
||||
|
||||
import { PrismaService } from '../prisma';
|
||||
import { PrismaFactory } from '../prisma/factory';
|
||||
|
||||
function transformPrismaKey(key: string) {
|
||||
// replace first '_' to '/' as a scope prefix
|
||||
@@ -30,11 +30,11 @@ export class PrismaMetricProducer implements MetricProducer {
|
||||
errors: [],
|
||||
};
|
||||
|
||||
if (!PrismaService.INSTANCE) {
|
||||
if (!PrismaFactory.INSTANCE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const prisma = PrismaService.INSTANCE;
|
||||
const prisma = PrismaFactory.INSTANCE;
|
||||
|
||||
const endTime = hrTime();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user