perf(telemetry): load telemetry asap (#1431)

Reviewed-on: daoyoucloud/tachybase#1431
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-02 11:57:29 +08:00 committed by sealday
parent e5156a44a9
commit d5a0f5640c
9 changed files with 933 additions and 1258 deletions

View File

@ -11,7 +11,8 @@
"@tachybase/preset-hera-sancongtou": "workspace:*",
"@tachybase/preset-mini": "workspace:*",
"@tachybase/preset-tachybase": "workspace:*",
"@tachybase/server": "workspace:*"
"@tachybase/server": "workspace:*",
"@tachybase/telemetry": "workspace:*"
},
"devDependencies": {
"@tachybase/cache": "workspace:*",

View File

@ -1,9 +1,10 @@
import { telemetryOptions as telemetry } from '@tachybase/telemetry';
import { cacheManager } from './cache';
import { parseDatabaseOptions } from './database';
import logger from './logger';
import plugins from './plugins';
import resourcer from './resourcer';
import { telemetry } from './telemetry';
export async function getConfig() {
return {

View File

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

View File

@ -17,7 +17,7 @@ import {
SystemLoggerOptions,
} from '@tachybase/logger';
import { ResourceOptions, Resourcer } from '@tachybase/resourcer';
import { Telemetry, TelemetryOptions } from '@tachybase/telemetry';
import { AppTelemetryOptions, getTelemetry } from '@tachybase/telemetry';
import { applyMixins, AsyncEmitter, importModule, Toposort, ToposortOptions } from '@tachybase/utils';
import { Command, CommandOptions, ParseOptions } from 'commander';
@ -67,10 +67,6 @@ export interface AppLoggerOptions {
system: SystemLoggerOptions;
}
export interface AppTelemetryOptions extends TelemetryOptions {
enabled?: boolean;
}
export interface ApplicationOptions {
database?: IDatabaseOptions | Database;
cacheManager?: CacheManagerOptions;
@ -310,10 +306,8 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return this._locales;
}
protected _telemetry: Telemetry;
get telemetry() {
return this._telemetry;
return getTelemetry();
}
protected _version: ApplicationVersion;
@ -516,12 +510,12 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
await this.emitAsync('beforeLoad', this, options);
}
// Telemetry is initialized after beforeLoad hook
// since some configuration may be registered in beforeLoad hook
this.telemetry.init();
// Telemetry is already initialized in @tachybase/app
if (this.options.telemetry?.enabled) {
// Start collecting telemetry data if enabled
this.telemetry.start();
if (!this.telemetry.started) {
this.telemetry.start();
}
}
await this.pm.load(options);
@ -1071,11 +1065,6 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
this.log.warn('TELEMETRY_SERVICE_NAME is not set, will use default service name, please set it in .env file!');
serviceName = `tachybase-${this.name}`;
}
this._telemetry = new Telemetry({
serviceName,
version: this.getVersion(),
...options.telemetry,
});
this._authManager = new AuthManager({
authKey: 'X-Authenticator',

View File

@ -1,6 +1,4 @@
import { AppTelemetryOptions } from '@tachybase/server';
export const telemetry: AppTelemetryOptions = {
export const telemetryOptions = {
enabled: process.env.TELEMETRY_ENABLED === 'on',
metric: {
readerName: process.env.OTEL_METRICS_READER,

View File

@ -1 +1,7 @@
import { getTelemetry } from './telemetry';
if (!getTelemetry()) {
throw new Error('Failed to load telemetry');
}
export * from './telemetry';

View File

@ -74,17 +74,31 @@ export class Metric {
console.warn('Prometheus server is disabled');
} else {
const port = Number(process.env.OTEL_PROMETHEUS_PORT) || 9464;
const reader = () =>
new PrometheusExporter(
{
port, // optional - default is 9464
},
() => {
console.log(`Prometheus exporter endpoint started on http://localhost:${port}/metrics`);
},
);
// 注册 Prometheus 作为指标 Reader
this.registerReader('prometheus', reader);
if (port <= 0 || port >= 65536) {
throw new Error(`Invalid port: ${port}`);
}
if (port <= 1024) {
console.warn('Prometheus server will try to run on a privileged port, it may need root permission');
}
if (port === 9464) {
console.warn('Prometheus server will run on default port 9464');
}
try {
// 启动 Prometheus Exporter
const reader = () =>
new PrometheusExporter(
{
port,
},
() => {
console.log(`Prometheus exporter endpoint started on http://localhost:${port}/metrics`);
},
);
// 注册 Prometheus Exporter 作为指标 Reader
this.registerReader('prometheus', reader);
} catch (error) {
console.error('Failed to initialize Prometheus metrics reader:', error);
}
}
} catch (error) {
console.error('Failed to initialize Prometheus metrics reader:', error);

View File

@ -4,9 +4,14 @@ import { InstrumentationOption, registerInstrumentations } from '@opentelemetry/
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
// 防止加载什么奇怪的东西
import packageJson from '../../../../package.json';
import { telemetryOptions } from './config';
import { Metric, MetricOptions } from './metric';
import { Trace, TraceOptions } from './trace';
export { telemetryOptions } from './config';
export interface TelemetryOptions {
serviceName?: string;
version?: string;
@ -14,6 +19,10 @@ export interface TelemetryOptions {
metric?: MetricOptions;
}
export interface AppTelemetryOptions extends TelemetryOptions {
enabled?: boolean;
}
export class Telemetry {
serviceName: string;
version: string;
@ -23,14 +32,13 @@ export class Telemetry {
started = false;
constructor(options?: TelemetryOptions) {
console.log('Create telemetry with options', options);
const { trace, metric, serviceName, version } = options || {};
this.trace = new Trace({ tracerName: `${serviceName}-trace`, version, ...trace });
this.metric = new Metric({ meterName: `${serviceName}-meter`, version, ...metric });
this.serviceName = serviceName || 'tachybase';
this.serviceName = serviceName || 'tachybase-main';
this.version = version || '';
}
init() {
// 设置 OTel 日志等级
const diagLogLevel = process.env.OTEL_LOG_LEVEL;
if (diagLogLevel) {
@ -51,6 +59,10 @@ export class Telemetry {
registerInstrumentations({
instrumentations: this.instrumentations,
});
}
init() {
console.log('Start init telemetry', this.serviceName, this.version);
const resource = Resource.default().merge(
new Resource({
@ -80,3 +92,25 @@ export class Telemetry {
this.instrumentations.push(...instrumentation);
}
}
let _telemetry: Telemetry;
let serviceName = process.env.TELEMETRY_SERVICE_NAME;
if (!serviceName) {
console.warn('TELEMETRY_SERVICE_NAME is not set, will use default service name, please set it in .env file!');
serviceName = `tachybase-main`;
}
export const getTelemetry = () => {
if (!_telemetry || typeof _telemetry === 'undefined') {
_telemetry = new Telemetry({
serviceName,
version: packageJson.version || 'UnkVer',
...telemetryOptions,
});
_telemetry.init();
}
// 这里只需要 initstart 保留在 application 里
return _telemetry;
};

File diff suppressed because it is too large Load Diff