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