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:
parent
e5156a44a9
commit
d5a0f5640c
@ -11,7 +11,8 @@
|
|||||||
"@tachybase/preset-hera-sancongtou": "workspace:*",
|
"@tachybase/preset-hera-sancongtou": "workspace:*",
|
||||||
"@tachybase/preset-mini": "workspace:*",
|
"@tachybase/preset-mini": "workspace:*",
|
||||||
"@tachybase/preset-tachybase": "workspace:*",
|
"@tachybase/preset-tachybase": "workspace:*",
|
||||||
"@tachybase/server": "workspace:*"
|
"@tachybase/server": "workspace:*",
|
||||||
|
"@tachybase/telemetry": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tachybase/cache": "workspace:*",
|
"@tachybase/cache": "workspace:*",
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
import { telemetryOptions as telemetry } from '@tachybase/telemetry';
|
||||||
|
|
||||||
import { cacheManager } from './cache';
|
import { cacheManager } from './cache';
|
||||||
import { parseDatabaseOptions } from './database';
|
import { parseDatabaseOptions } from './database';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
import plugins from './plugins';
|
import plugins from './plugins';
|
||||||
import resourcer from './resourcer';
|
import resourcer from './resourcer';
|
||||||
import { telemetry } from './telemetry';
|
|
||||||
|
|
||||||
export async function getConfig() {
|
export async function getConfig() {
|
||||||
return {
|
return {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import '@tachybase/telemetry'; // 尽早实例化,以尽力保证插桩成功,TODO: Koa 依然无法成功 hook
|
||||||
|
|
||||||
import { Gateway } from '@tachybase/server';
|
import { Gateway } from '@tachybase/server';
|
||||||
|
|
||||||
import { getConfig } from './config';
|
import { getConfig } from './config';
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
SystemLoggerOptions,
|
SystemLoggerOptions,
|
||||||
} from '@tachybase/logger';
|
} from '@tachybase/logger';
|
||||||
import { ResourceOptions, Resourcer } from '@tachybase/resourcer';
|
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 { applyMixins, AsyncEmitter, importModule, Toposort, ToposortOptions } from '@tachybase/utils';
|
||||||
|
|
||||||
import { Command, CommandOptions, ParseOptions } from 'commander';
|
import { Command, CommandOptions, ParseOptions } from 'commander';
|
||||||
@ -67,10 +67,6 @@ export interface AppLoggerOptions {
|
|||||||
system: SystemLoggerOptions;
|
system: SystemLoggerOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppTelemetryOptions extends TelemetryOptions {
|
|
||||||
enabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApplicationOptions {
|
export interface ApplicationOptions {
|
||||||
database?: IDatabaseOptions | Database;
|
database?: IDatabaseOptions | Database;
|
||||||
cacheManager?: CacheManagerOptions;
|
cacheManager?: CacheManagerOptions;
|
||||||
@ -310,10 +306,8 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
|||||||
return this._locales;
|
return this._locales;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _telemetry: Telemetry;
|
|
||||||
|
|
||||||
get telemetry() {
|
get telemetry() {
|
||||||
return this._telemetry;
|
return getTelemetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _version: ApplicationVersion;
|
protected _version: ApplicationVersion;
|
||||||
@ -516,12 +510,12 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
|||||||
await this.emitAsync('beforeLoad', this, options);
|
await this.emitAsync('beforeLoad', this, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Telemetry is initialized after beforeLoad hook
|
// Telemetry is already initialized in @tachybase/app
|
||||||
// since some configuration may be registered in beforeLoad hook
|
|
||||||
this.telemetry.init();
|
|
||||||
if (this.options.telemetry?.enabled) {
|
if (this.options.telemetry?.enabled) {
|
||||||
// Start collecting telemetry data if enabled
|
// Start collecting telemetry data if enabled
|
||||||
this.telemetry.start();
|
if (!this.telemetry.started) {
|
||||||
|
this.telemetry.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.pm.load(options);
|
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!');
|
this.log.warn('TELEMETRY_SERVICE_NAME is not set, will use default service name, please set it in .env file!');
|
||||||
serviceName = `tachybase-${this.name}`;
|
serviceName = `tachybase-${this.name}`;
|
||||||
}
|
}
|
||||||
this._telemetry = new Telemetry({
|
|
||||||
serviceName,
|
|
||||||
version: this.getVersion(),
|
|
||||||
...options.telemetry,
|
|
||||||
});
|
|
||||||
|
|
||||||
this._authManager = new AuthManager({
|
this._authManager = new AuthManager({
|
||||||
authKey: 'X-Authenticator',
|
authKey: 'X-Authenticator',
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { AppTelemetryOptions } from '@tachybase/server';
|
export const telemetryOptions = {
|
||||||
|
|
||||||
export const telemetry: AppTelemetryOptions = {
|
|
||||||
enabled: process.env.TELEMETRY_ENABLED === 'on',
|
enabled: process.env.TELEMETRY_ENABLED === 'on',
|
||||||
metric: {
|
metric: {
|
||||||
readerName: process.env.OTEL_METRICS_READER,
|
readerName: process.env.OTEL_METRICS_READER,
|
@ -1 +1,7 @@
|
|||||||
|
import { getTelemetry } from './telemetry';
|
||||||
|
|
||||||
|
if (!getTelemetry()) {
|
||||||
|
throw new Error('Failed to load telemetry');
|
||||||
|
}
|
||||||
|
|
||||||
export * from './telemetry';
|
export * from './telemetry';
|
||||||
|
@ -74,17 +74,31 @@ export class Metric {
|
|||||||
console.warn('Prometheus server is disabled');
|
console.warn('Prometheus server is disabled');
|
||||||
} else {
|
} else {
|
||||||
const port = Number(process.env.OTEL_PROMETHEUS_PORT) || 9464;
|
const port = Number(process.env.OTEL_PROMETHEUS_PORT) || 9464;
|
||||||
const reader = () =>
|
if (port <= 0 || port >= 65536) {
|
||||||
new PrometheusExporter(
|
throw new Error(`Invalid port: ${port}`);
|
||||||
{
|
}
|
||||||
port, // optional - default is 9464
|
if (port <= 1024) {
|
||||||
},
|
console.warn('Prometheus server will try to run on a privileged port, it may need root permission');
|
||||||
() => {
|
}
|
||||||
console.log(`Prometheus exporter endpoint started on http://localhost:${port}/metrics`);
|
if (port === 9464) {
|
||||||
},
|
console.warn('Prometheus server will run on default port 9464');
|
||||||
);
|
}
|
||||||
// 注册 Prometheus 作为指标 Reader
|
try {
|
||||||
this.registerReader('prometheus', reader);
|
// 启动 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) {
|
} catch (error) {
|
||||||
console.error('Failed to initialize Prometheus metrics reader:', error);
|
console.error('Failed to initialize Prometheus metrics reader:', error);
|
||||||
|
@ -4,9 +4,14 @@ import { InstrumentationOption, registerInstrumentations } from '@opentelemetry/
|
|||||||
import { Resource } from '@opentelemetry/resources';
|
import { Resource } from '@opentelemetry/resources';
|
||||||
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
|
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
|
||||||
|
|
||||||
|
// 防止加载什么奇怪的东西
|
||||||
|
import packageJson from '../../../../package.json';
|
||||||
|
import { telemetryOptions } from './config';
|
||||||
import { Metric, MetricOptions } from './metric';
|
import { Metric, MetricOptions } from './metric';
|
||||||
import { Trace, TraceOptions } from './trace';
|
import { Trace, TraceOptions } from './trace';
|
||||||
|
|
||||||
|
export { telemetryOptions } from './config';
|
||||||
|
|
||||||
export interface TelemetryOptions {
|
export interface TelemetryOptions {
|
||||||
serviceName?: string;
|
serviceName?: string;
|
||||||
version?: string;
|
version?: string;
|
||||||
@ -14,6 +19,10 @@ export interface TelemetryOptions {
|
|||||||
metric?: MetricOptions;
|
metric?: MetricOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AppTelemetryOptions extends TelemetryOptions {
|
||||||
|
enabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class Telemetry {
|
export class Telemetry {
|
||||||
serviceName: string;
|
serviceName: string;
|
||||||
version: string;
|
version: string;
|
||||||
@ -23,14 +32,13 @@ export class Telemetry {
|
|||||||
started = false;
|
started = false;
|
||||||
|
|
||||||
constructor(options?: TelemetryOptions) {
|
constructor(options?: TelemetryOptions) {
|
||||||
|
console.log('Create telemetry with options', options);
|
||||||
const { trace, metric, serviceName, version } = options || {};
|
const { trace, metric, serviceName, version } = options || {};
|
||||||
this.trace = new Trace({ tracerName: `${serviceName}-trace`, version, ...trace });
|
this.trace = new Trace({ tracerName: `${serviceName}-trace`, version, ...trace });
|
||||||
this.metric = new Metric({ meterName: `${serviceName}-meter`, version, ...metric });
|
this.metric = new Metric({ meterName: `${serviceName}-meter`, version, ...metric });
|
||||||
this.serviceName = serviceName || 'tachybase';
|
this.serviceName = serviceName || 'tachybase-main';
|
||||||
this.version = version || '';
|
this.version = version || '';
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
// 设置 OTel 日志等级
|
// 设置 OTel 日志等级
|
||||||
const diagLogLevel = process.env.OTEL_LOG_LEVEL;
|
const diagLogLevel = process.env.OTEL_LOG_LEVEL;
|
||||||
if (diagLogLevel) {
|
if (diagLogLevel) {
|
||||||
@ -51,6 +59,10 @@ export class Telemetry {
|
|||||||
registerInstrumentations({
|
registerInstrumentations({
|
||||||
instrumentations: this.instrumentations,
|
instrumentations: this.instrumentations,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
console.log('Start init telemetry', this.serviceName, this.version);
|
||||||
|
|
||||||
const resource = Resource.default().merge(
|
const resource = Resource.default().merge(
|
||||||
new Resource({
|
new Resource({
|
||||||
@ -80,3 +92,25 @@ export class Telemetry {
|
|||||||
this.instrumentations.push(...instrumentation);
|
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();
|
||||||
|
}
|
||||||
|
// 这里只需要 init,start 保留在 application 里
|
||||||
|
|
||||||
|
return _telemetry;
|
||||||
|
};
|
||||||
|
2074
pnpm-lock.yaml
2074
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user