fix(server): duplicate registered otel providers (#11513)

This commit is contained in:
liuyi
2025-04-08 10:25:36 +08:00
committed by GitHub
parent e927d02c96
commit 646182ea2a
3 changed files with 44 additions and 34 deletions

View File

@@ -2,14 +2,18 @@ import './config';
import { Global, Module } from '@nestjs/common'; import { Global, Module } from '@nestjs/common';
import { OpentelemetryFactory } from './opentelemetry'; import {
OpentelemetryOptionsFactory,
OpentelemetryProvider,
} from './opentelemetry';
@Global() @Global()
@Module({ @Module({
providers: [OpentelemetryFactory], providers: [OpentelemetryOptionsFactory, OpentelemetryProvider],
exports: [OpentelemetryOptionsFactory],
}) })
export class MetricsModule {} export class MetricsModule {}
export * from './metrics'; export * from './metrics';
export * from './utils'; export * from './utils';
export { OpentelemetryFactory }; export { OpentelemetryOptionsFactory };

View File

@@ -1,4 +1,5 @@
import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { import {
CompositePropagator, CompositePropagator,
W3CBaggagePropagator, W3CBaggagePropagator,
@@ -14,7 +15,7 @@ import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core'
import { SocketIoInstrumentation } from '@opentelemetry/instrumentation-socket.io'; import { SocketIoInstrumentation } from '@opentelemetry/instrumentation-socket.io';
import { Resource } from '@opentelemetry/resources'; import { Resource } from '@opentelemetry/resources';
import { MetricProducer, MetricReader } from '@opentelemetry/sdk-metrics'; import { MetricProducer, MetricReader } from '@opentelemetry/sdk-metrics';
import { NodeSDK } from '@opentelemetry/sdk-node'; import { NodeSDK, NodeSDKConfiguration } from '@opentelemetry/sdk-node';
import { import {
BatchSpanProcessor, BatchSpanProcessor,
SpanExporter, SpanExporter,
@@ -34,7 +35,7 @@ import { PrismaMetricProducer } from './prisma';
const { PrismaInstrumentation } = prismaInstrument; const { PrismaInstrumentation } = prismaInstrument;
export abstract class BaseOpentelemetryFactory { export abstract class BaseOpentelemetryOptionsFactory {
abstract getMetricReader(): MetricReader; abstract getMetricReader(): MetricReader;
abstract getSpanExporter(): SpanExporter; abstract getSpanExporter(): SpanExporter;
@@ -61,9 +62,9 @@ export abstract class BaseOpentelemetryFactory {
}); });
} }
create() { create(): Partial<NodeSDKConfiguration> {
const traceExporter = this.getSpanExporter(); const traceExporter = this.getSpanExporter();
return new NodeSDK({ return {
resource: this.getResource(), resource: this.getResource(),
sampler: new TraceIdRatioBasedSampler(0.1), sampler: new TraceIdRatioBasedSampler(0.1),
traceExporter, traceExporter,
@@ -77,21 +78,32 @@ export abstract class BaseOpentelemetryFactory {
}), }),
instrumentations: this.getInstractions(), instrumentations: this.getInstractions(),
serviceName: 'affine-cloud', serviceName: 'affine-cloud',
}); };
} }
} }
@Injectable() @Injectable()
export class OpentelemetryFactory export class OpentelemetryOptionsFactory extends BaseOpentelemetryOptionsFactory {
extends BaseOpentelemetryFactory override getMetricReader(): MetricReader {
implements OnModuleDestroy return new PrometheusExporter({
{ metricProducers: this.getMetricsProducers(),
private readonly logger = new Logger(OpentelemetryFactory.name); });
}
override getSpanExporter(): SpanExporter {
return new ZipkinExporter();
}
}
@Injectable()
export class OpentelemetryProvider {
readonly #logger = new Logger(OpentelemetryProvider.name);
#sdk: NodeSDK | null = null; #sdk: NodeSDK | null = null;
constructor(private readonly config: Config) { constructor(
super(); private readonly config: Config,
} private readonly ref: ModuleRef
) {}
@OnEvent('config.init') @OnEvent('config.init')
async init(event: Events['config.init']) { async init(event: Events['config.init']) {
@@ -112,27 +124,21 @@ export class OpentelemetryFactory
await this.#sdk?.shutdown(); await this.#sdk?.shutdown();
} }
override getMetricReader(): MetricReader {
return new PrometheusExporter({
metricProducers: this.getMetricsProducers(),
});
}
override getSpanExporter(): SpanExporter {
return new ZipkinExporter();
}
private async setup() { private async setup() {
if (this.config.metrics.enabled) { if (this.config.metrics.enabled) {
if (!this.#sdk) { if (!this.#sdk) {
this.#sdk = this.create(); const factory = this.ref.get(OpentelemetryOptionsFactory, {
strict: false,
});
this.#sdk = new NodeSDK(factory.create());
} }
this.#sdk.start(); this.#sdk.start();
this.logger.log('OpenTelemetry SDK started'); this.#logger.log('OpenTelemetry SDK started');
} else { } else {
await this.#sdk?.shutdown(); await this.#sdk?.shutdown();
this.#sdk = null; this.#sdk = null;
this.logger.log('OpenTelemetry SDK stopped'); this.#logger.log('OpenTelemetry SDK stopped');
} }
} }
} }

View File

@@ -14,10 +14,10 @@ import {
SEMRESATTRS_K8S_POD_NAME, SEMRESATTRS_K8S_POD_NAME,
} from '@opentelemetry/semantic-conventions'; } from '@opentelemetry/semantic-conventions';
import { OpentelemetryFactory } from '../../base/metrics'; import { OpentelemetryOptionsFactory } from '../../base/metrics';
@Injectable() @Injectable()
export class GCloudOpentelemetryFactory extends OpentelemetryFactory { export class GCloudOpentelemetryOptionsFactory extends OpentelemetryOptionsFactory {
override getResource(): Resource { override getResource(): Resource {
const env = getEnv(); const env = getEnv();
return super return super
@@ -48,8 +48,8 @@ export class GCloudOpentelemetryFactory extends OpentelemetryFactory {
} }
const FactorProvider: Provider = { const FactorProvider: Provider = {
provide: OpentelemetryFactory, provide: OpentelemetryOptionsFactory,
useClass: GCloudOpentelemetryFactory, useClass: GCloudOpentelemetryOptionsFactory,
}; };
@Global() @Global()