refactor(server): standarderlize metrics and trace with OTEL (#5054)

you can now export span to Zipkin and metrics to Prometheus when developing locally
follow the docs of OTEL: https://opentelemetry.io/docs/instrumentation/js/exporters/

<img width="2357" alt="image" src="https://github.com/toeverything/AFFiNE/assets/8281226/ec615e1f-3e91-43f7-9111-d7d2629e9679">
This commit is contained in:
liuyi
2023-11-24 15:19:22 +00:00
parent cf65a5cd93
commit 91efca107a
23 changed files with 298 additions and 360 deletions

View File

@@ -5,7 +5,6 @@ import test from 'ava';
import { ConfigModule } from '../src/config';
import { GqlModule } from '../src/graphql.module';
import { MetricsModule } from '../src/metrics';
import { AuthModule } from '../src/modules/auth';
import { AuthResolver } from '../src/modules/auth/resolver';
import { AuthService } from '../src/modules/auth/service';
@@ -40,7 +39,6 @@ test.beforeEach(async () => {
PrismaModule,
GqlModule,
AuthModule,
MetricsModule,
RateLimiterModule,
],
}).compile();

View File

@@ -10,7 +10,6 @@ import { Doc as YDoc, encodeStateAsUpdate } from 'yjs';
import { CacheModule } from '../src/cache';
import { Config, ConfigModule } from '../src/config';
import { MetricsModule } from '../src/metrics';
import { DocManager, DocModule } from '../src/modules/doc';
import { PrismaModule, PrismaService } from '../src/prisma';
import { flushDB } from './utils';
@@ -19,7 +18,6 @@ const createModule = () => {
return Test.createTestingModule({
imports: [
PrismaModule,
MetricsModule,
CacheModule,
EventEmitterModule.forRoot(),
ConfigModule.forRoot(),

View File

@@ -6,7 +6,6 @@ import test from 'ava';
import * as Sinon from 'sinon';
import { ConfigModule } from '../src/config';
import { MetricsModule } from '../src/metrics';
import { DocHistoryManager } from '../src/modules/doc';
import { PrismaModule, PrismaService } from '../src/prisma';
import { flushDB } from './utils';
@@ -20,12 +19,7 @@ let db: PrismaService;
test.beforeEach(async () => {
await flushDB();
m = await Test.createTestingModule({
imports: [
PrismaModule,
MetricsModule,
ScheduleModule.forRoot(),
ConfigModule.forRoot(),
],
imports: [PrismaModule, ScheduleModule.forRoot(), ConfigModule.forRoot()],
providers: [DocHistoryManager],
}).compile();

View File

@@ -12,7 +12,6 @@ import ava, { type TestFn } from 'ava';
import { ConfigModule } from '../src/config';
import { GqlModule } from '../src/graphql.module';
import { MetricsModule } from '../src/metrics';
import { AuthModule } from '../src/modules/auth';
import { AuthService } from '../src/modules/auth/service';
import { PrismaModule } from '../src/prisma';
@@ -44,7 +43,6 @@ test.beforeEach(async t => {
PrismaModule,
GqlModule,
AuthModule,
MetricsModule,
RateLimiterModule,
],
}).compile();

View File

@@ -1,78 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import test from 'ava';
import { register } from 'prom-client';
import { MetricsModule } from '../src/metrics';
import { Metrics } from '../src/metrics/metrics';
import { PrismaModule } from '../src/prisma';
let metrics: Metrics;
let module: TestingModule;
test.beforeEach(async () => {
module = await Test.createTestingModule({
imports: [MetricsModule, PrismaModule],
}).compile();
metrics = module.get(Metrics);
});
test.afterEach.always(async () => {
await module.close();
});
test('should be able to increment counter', async t => {
metrics.socketIOEventCounter(1, { event: 'client-handshake' });
const socketIOCounterMetric = register.getSingleMetric('socket_io_counter');
t.truthy(socketIOCounterMetric);
t.truthy(
JSON.stringify((await socketIOCounterMetric!.get()).values) ===
'[{"value":1,"labels":{"event":"client-handshake"}}]'
);
t.pass();
});
test('should be able to timer', async t => {
let minimum: number;
{
const endTimer = metrics.socketIOEventTimer({ event: 'client-handshake' });
const a = performance.now();
await new Promise(resolve => setTimeout(resolve, 50));
const b = performance.now();
minimum = b - a;
endTimer();
}
let maximum: number;
{
const a = performance.now();
const endTimer = metrics.socketIOEventTimer({ event: 'client-handshake' });
await new Promise(resolve => setTimeout(resolve, 100));
endTimer();
const b = performance.now();
maximum = b - a;
}
const socketIOTimerMetric = register.getSingleMetric('socket_io_timer');
t.truthy(socketIOTimerMetric);
const observations = (await socketIOTimerMetric!.get()).values;
for (const observation of observations) {
if (
observation.labels.event === 'client-handshake' &&
'quantile' in observation.labels
) {
t.truthy(
observation.value >= minimum / 1000,
'observation.value should be greater than minimum'
);
t.truthy(
observation.value <= maximum / 1000,
'observation.value should be less than maximum'
);
}
}
t.pass();
});