diff --git a/packages/service/common/metrics/runtime.ts b/packages/service/common/metrics/runtime.ts index 75dba5cd30..2071a18105 100644 --- a/packages/service/common/metrics/runtime.ts +++ b/packages/service/common/metrics/runtime.ts @@ -5,6 +5,7 @@ import type { ObservableGauge } from '@opentelemetry/api'; import { getMeter } from '@fastgpt-sdk/otel/metrics'; +import { cpus } from 'os'; type RuntimeMetricAttributes = Record; @@ -15,6 +16,9 @@ type RuntimeObservableSet = { processMemoryHeapTotal: ObservableGauge; processMemoryExternal: ObservableGauge; processMemoryArrayBuffers: ObservableGauge; + processCpuUser: ObservableGauge; + processCpuSystem: ObservableGauge; + processCpuUtilization: ObservableGauge; processUptime: ObservableGauge; }; @@ -25,6 +29,9 @@ let runtimeMeter: Meter | undefined; let runtimeObservables: Observable[] = []; let runtimeMetricsCallback: BatchObservableCallback | undefined; +let previousCpuUsage: NodeJS.CpuUsage | undefined; +let previousCpuTimestamp: number | undefined; + function createRuntimeObservables(): RuntimeObservableSet { const meter = getMeter('fastgpt.runtime'); @@ -50,6 +57,18 @@ function createRuntimeObservables(): RuntimeObservableSet { description: 'Memory allocated for ArrayBuffer and SharedArrayBuffer instances', unit: 'By' }), + processCpuUser: meter.createObservableGauge(`${prefix}.cpu.user`, { + description: 'Cumulative user CPU time of the current process', + unit: 'us' + }), + processCpuSystem: meter.createObservableGauge(`${prefix}.cpu.system`, { + description: 'Cumulative system CPU time of the current process', + unit: 'us' + }), + processCpuUtilization: meter.createObservableGauge(`${prefix}.cpu.utilization`, { + description: 'CPU utilization ratio of the current process (0~1, across all cores)', + unit: '1' + }), processUptime: meter.createObservableGauge(`${prefix}.uptime`, { description: 'Process uptime', unit: 's' @@ -69,6 +88,9 @@ export function startRuntimeMetrics() { observables.processMemoryHeapTotal, observables.processMemoryExternal, observables.processMemoryArrayBuffers, + observables.processCpuUser, + observables.processCpuSystem, + observables.processCpuUtilization, observables.processUptime ]; runtimeMetricsCallback = (result) => { @@ -79,6 +101,29 @@ export function startRuntimeMetrics() { result.observe(observables.processMemoryHeapTotal, memoryUsage.heapTotal); result.observe(observables.processMemoryExternal, memoryUsage.external); result.observe(observables.processMemoryArrayBuffers, memoryUsage.arrayBuffers); + + const currentCpuUsage = process.cpuUsage(); + const currentTimestamp = Date.now(); + + result.observe(observables.processCpuUser, currentCpuUsage.user); + result.observe(observables.processCpuSystem, currentCpuUsage.system); + + if (previousCpuUsage && previousCpuTimestamp) { + const elapsedUs = (currentTimestamp - previousCpuTimestamp) * 1000; + if (elapsedUs > 0) { + const cpuDeltaUs = + currentCpuUsage.user - + previousCpuUsage.user + + (currentCpuUsage.system - previousCpuUsage.system); + const coreCount = cpus().length || 1; + const utilization = cpuDeltaUs / (elapsedUs * coreCount); + result.observe(observables.processCpuUtilization, Math.min(1, Math.max(0, utilization))); + } + } + + previousCpuUsage = currentCpuUsage; + previousCpuTimestamp = currentTimestamp; + result.observe(observables.processUptime, process.uptime()); }; @@ -95,4 +140,6 @@ export function stopRuntimeMetrics() { runtimeMeter = undefined; runtimeObservables = []; runtimeMetricsCallback = undefined; + previousCpuUsage = undefined; + previousCpuTimestamp = undefined; } diff --git a/packages/service/common/tracing/client.ts b/packages/service/common/tracing/client.ts index 33d9661d38..7d632f9f85 100644 --- a/packages/service/common/tracing/client.ts +++ b/packages/service/common/tracing/client.ts @@ -29,7 +29,7 @@ export type ActiveSpanOptions = { attributes?: Record; }; -const DEFAULT_PRODUCTION_TRACING_SAMPLE_RATIO = 0.05; +const DEFAULT_PRODUCTION_TRACING_SAMPLE_RATIO = 0.01; const DEFAULT_NON_PRODUCTION_TRACING_SAMPLE_RATIO = 1; function getDefaultTracingSampleRatio() { diff --git a/packages/service/env.ts b/packages/service/env.ts index 8935f7850d..1dca0ddb03 100644 --- a/packages/service/env.ts +++ b/packages/service/env.ts @@ -36,7 +36,7 @@ export const env = createEnv({ LOG_OTEL_URL: z.url().optional(), METRICS_ENABLE_OTEL: BoolSchema.default(false), - METRICS_EXPORT_INTERVAL: z.coerce.number().int().positive().default(15000), + METRICS_EXPORT_INTERVAL: z.coerce.number().int().positive().default(30000), METRICS_OTEL_SERVICE_NAME: z.string().default('fastgpt-client'), METRICS_OTEL_URL: z.url().optional(), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22ea9139f2..421d415b29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,8 +34,8 @@ catalogs: specifier: 0.1.2 version: 0.1.2 '@fastgpt-sdk/otel': - specifier: 0.1.0 - version: 0.1.0 + specifier: 0.1.2 + version: 0.1.2 '@fastgpt-sdk/storage': specifier: 0.6.15 version: 0.6.15 @@ -248,7 +248,7 @@ importers: version: 11.7.2 '@fastgpt-sdk/otel': specifier: 'catalog:' - version: 0.1.0 + version: 0.1.2 '@fastgpt-sdk/sandbox-adapter': specifier: ^0.0.34 version: 0.0.34 @@ -2728,8 +2728,8 @@ packages: resolution: {integrity: sha512-nt1qCq7frcRiR+406vEERWC1vEPVIKPUGH/ZRP/mlBxvNJp1RycWQT8RhK7/tHmW6xPNZoRL/q2WfhM4Q+L7eg==} engines: {node: '>=20', pnpm: '>=9'} - '@fastgpt-sdk/otel@0.1.0': - resolution: {integrity: sha512-wpZUcpoU4u1/UxC8R0KU7spZ1Ku2FbGerp4eBeOCgCDaiJGUbm3P7d1D97wzrPIK3QUgWj2g8N6dS0vrlryp2Q==} + '@fastgpt-sdk/otel@0.1.2': + resolution: {integrity: sha512-niJXceEdyZSWN/syE9aRlTb6Gk6Posez9j4eiuyoQWgF0wGH1IWDbC2c0tlSglZQn6ONY90HzyRsSLOGlpd9KQ==} engines: {node: '>=20', pnpm: '>=9'} '@fastgpt-sdk/plugin@0.3.8': @@ -13724,7 +13724,7 @@ snapshots: '@opentelemetry/sdk-logs': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.39.0 - '@fastgpt-sdk/otel@0.1.0': + '@fastgpt-sdk/otel@0.1.2': dependencies: '@logtape/logtape': 2.0.2 '@logtape/pretty': 2.0.2(@logtape/logtape@2.0.2) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 89fc6ad41c..f08ebd1df7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -23,7 +23,7 @@ catalog: '@emotion/react': ^11 '@emotion/styled': ^11 '@fastgpt-sdk/logger': 0.1.2 - '@fastgpt-sdk/otel': 0.1.0 + '@fastgpt-sdk/otel': 0.1.2 '@fastgpt-sdk/storage': 0.6.15 '@modelcontextprotocol/sdk': ^1 '@types/lodash': ^4 diff --git a/sdk/otel/package.json b/sdk/otel/package.json index 45c3190efd..045d6c7f3e 100644 --- a/sdk/otel/package.json +++ b/sdk/otel/package.json @@ -1,7 +1,7 @@ { "name": "@fastgpt-sdk/otel", "private": false, - "version": "0.1.0", + "version": "0.1.2", "description": "FastGPT SDK for OpenTelemetry observability", "type": "module", "main": "./dist/index.mjs", diff --git a/sdk/otel/src/logger/env.ts b/sdk/otel/src/logger/env.ts index 5cf642e3b3..485d236f1a 100644 --- a/sdk/otel/src/logger/env.ts +++ b/sdk/otel/src/logger/env.ts @@ -50,7 +50,7 @@ export function createLoggerOptionsFromEnv( parseStringEnv(env.LOG_OTEL_URL) ?? options.defaultOtelUrl ?? 'http://localhost:4318/v1/logs', - level: parseLogLevel(env.LOG_OTEL_LEVEL, options.defaultOtelLevel ?? 'info') + level: parseLogLevel(env.LOG_OTEL_LEVEL, options.defaultOtelLevel ?? 'warning') } : false, sensitiveProperties: options.sensitiveProperties diff --git a/sdk/otel/src/logger/otel.ts b/sdk/otel/src/logger/otel.ts index 79ebbbc2e6..a12e6d1d25 100644 --- a/sdk/otel/src/logger/otel.ts +++ b/sdk/otel/src/logger/otel.ts @@ -10,7 +10,7 @@ import { import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; import type { Resource } from '@opentelemetry/resources'; import { defaultResource, resourceFromAttributes } from '@opentelemetry/resources'; -import { LoggerProvider, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs'; +import { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs'; import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; import { inspect as nodeInspect } from 'util'; import { mapLevelToSeverityNumber } from './helpers'; @@ -88,7 +88,7 @@ async function initializeLoggerProvider( const otlpExporter = new OTLPLogExporter(options.otlpExporterConfig); const loggerProvider = new LoggerProvider({ resource, - processors: [new SimpleLogRecordProcessor(otlpExporter)] + processors: [new BatchLogRecordProcessor(otlpExporter)] }); return loggerProvider; diff --git a/sdk/otel/src/metrics/client.ts b/sdk/otel/src/metrics/client.ts index 7aa4eb9090..13d90843a1 100644 --- a/sdk/otel/src/metrics/client.ts +++ b/sdk/otel/src/metrics/client.ts @@ -47,14 +47,14 @@ function normalizeMetricsOptions(options?: false | MetricsOptions) { if (options === false) { return { enabled: false, - exportIntervalMillis: 15000 + exportIntervalMillis: 30000 }; } return { enabled: options?.enabled ?? false, serviceName: options?.serviceName, - exportIntervalMillis: options?.exportIntervalMillis ?? 15000, + exportIntervalMillis: options?.exportIntervalMillis ?? 30000, otlpExporterConfig: { url: options?.url, headers: options?.headers diff --git a/sdk/otel/src/metrics/env.ts b/sdk/otel/src/metrics/env.ts index 367df9fa33..644e2caf25 100644 --- a/sdk/otel/src/metrics/env.ts +++ b/sdk/otel/src/metrics/env.ts @@ -40,7 +40,7 @@ export function createMetricsOptionsFromEnv( options.defaultMetricsUrl, exportIntervalMillis: parsePositiveNumberEnv( env.METRICS_EXPORT_INTERVAL ?? env.OTEL_METRIC_EXPORT_INTERVAL, - options.defaultExportIntervalMillis ?? 15000 + options.defaultExportIntervalMillis ?? 30000 ) } : false