Yuki Takei 9 месяцев назад
Родитель
Сommit
2c53a86376

+ 42 - 0
apps/app/src/features/opentelemetry/server/custom-metrics/application-metrics.ts

@@ -0,0 +1,42 @@
+import { diag, metrics } from '@opentelemetry/api';
+
+const logger = diag.createComponentLogger({ namespace: 'growi:custom-metrics:application' });
+
+export interface ApplicationMetricsConfig {
+  enabled: boolean;
+}
+
+export function addApplicationMetrics(config: ApplicationMetricsConfig): void {
+  if (!config.enabled) {
+    logger.debug('Application metrics collection is disabled');
+    return;
+  }
+
+  logger.info('Starting application metrics collection');
+
+  const meter = metrics.getMeter('growi-application-metrics', '1.0.0');
+
+  // Dummy metrics (for future application-specific metrics)
+  const dummyGauge = meter.createObservableGauge('growi.app.dummy.metric', {
+    description: 'Dummy metric for application metrics (placeholder)',
+    unit: 'count',
+  });
+
+  // Metrics collection callback
+  meter.addBatchObservableCallback(
+    (result) => {
+      try {
+        // Currently sending dummy values (actual metrics to be implemented later)
+        result.observe(dummyGauge, 1, {
+          'app.name': 'growi',
+        });
+      }
+      catch (error) {
+        logger.error('Failed to collect application metrics', { error });
+      }
+    },
+    [dummyGauge],
+  );
+
+  logger.info('Application metrics collection started successfully');
+}

+ 15 - 0
apps/app/src/features/opentelemetry/server/custom-metrics/index.ts

@@ -0,0 +1,15 @@
+import { addApplicationMetrics, type ApplicationMetricsConfig } from './application-metrics';
+import { addSystemMetrics, type SystemMetricsConfig } from './system-metrics';
+
+export { addSystemMetrics, type SystemMetricsConfig } from './system-metrics';
+export { addApplicationMetrics, type ApplicationMetricsConfig } from './application-metrics';
+
+export interface CustomMetricsConfig {
+  systemMetrics: SystemMetricsConfig;
+  applicationMetrics: ApplicationMetricsConfig;
+}
+
+export function addCustomMetrics(config: CustomMetricsConfig): void {
+  addSystemMetrics(config.systemMetrics);
+  addApplicationMetrics(config.applicationMetrics);
+}

+ 131 - 0
apps/app/src/features/opentelemetry/server/custom-metrics/system-metrics.ts

@@ -0,0 +1,131 @@
+import * as os from 'os';
+import * as process from 'process';
+
+import { diag, metrics } from '@opentelemetry/api';
+
+const logger = diag.createComponentLogger({ namespace: 'growi:custom-metrics:system' });
+
+export interface SystemMetricsConfig {
+  enabled: boolean;
+  collectionInterval: number;
+}
+
+let lastCpuUsage: NodeJS.CpuUsage | null = null;
+
+function calculateCpuUsage(intervalMs: number): number | null {
+  try {
+    const currentUsage = process.cpuUsage();
+
+    if (lastCpuUsage === null) {
+      lastCpuUsage = currentUsage;
+      return null; // 最初の計測では使用率を計算できない
+    }
+
+    const userDiff = currentUsage.user - lastCpuUsage.user;
+    const systemDiff = currentUsage.system - lastCpuUsage.system;
+    const totalDiff = userDiff + systemDiff;
+
+    // マイクロ秒を秒に変換し、収集間隔で割って使用率を計算
+    const intervalUs = intervalMs * 1000; // マイクロ秒に変換
+    const usage = (totalDiff / intervalUs) * 100;
+
+    lastCpuUsage = currentUsage;
+
+    // 0-100%の範囲でクランプ
+    return Math.min(Math.max(usage, 0), 100);
+  }
+  catch (error) {
+    logger.error('Failed to calculate CPU usage', { error });
+    return null;
+  }
+}
+
+function getMemoryInfo(): { used: number; total: number; usagePercent: number } {
+  const totalMem = os.totalmem();
+  const freeMem = os.freemem();
+  const used = totalMem - freeMem;
+  const usagePercent = (used / totalMem) * 100;
+
+  return {
+    used,
+    total: totalMem,
+    usagePercent,
+  };
+}
+
+export function addSystemMetrics(config: SystemMetricsConfig): void {
+  if (!config.enabled) {
+    logger.debug('System metrics collection is disabled');
+    return;
+  }
+
+  logger.info('Starting system metrics collection');
+
+  const meter = metrics.getMeter('growi-system-metrics', '1.0.0');
+
+  // CPU usage
+  const cpuUsageGauge = meter.createObservableGauge('growi.system.cpu.usage', {
+    description: 'CPU usage percentage',
+    unit: '%',
+  });
+
+  // System memory usage
+  const memoryUsageGauge = meter.createObservableGauge('growi.system.memory.usage', {
+    description: 'System memory usage in bytes',
+    unit: 'bytes',
+  });
+
+  const memoryUsagePercentGauge = meter.createObservableGauge('growi.system.memory.usage_percent', {
+    description: 'System memory usage percentage',
+    unit: '%',
+  });
+  // Process memory usage
+  const processMemoryHeapGauge = meter.createObservableGauge('growi.process.memory.heap.used', {
+    description: 'Process heap memory usage in bytes',
+    unit: 'bytes',
+  });
+
+  const processMemoryRssGauge = meter.createObservableGauge('growi.process.memory.rss', {
+    description: 'Process resident set size in bytes',
+    unit: 'bytes',
+  });
+
+  // Metrics collection callback
+  meter.addBatchObservableCallback(
+    (result) => {
+      try {
+        // CPU usage
+        const cpuUsage = calculateCpuUsage(config.collectionInterval);
+        if (cpuUsage !== null) {
+          result.observe(cpuUsageGauge, cpuUsage, {
+            'host.name': os.hostname(),
+          });
+        }
+
+        // System memory
+        const memoryInfo = getMemoryInfo();
+        result.observe(memoryUsageGauge, memoryInfo.used, {
+          'host.name': os.hostname(),
+        });
+        result.observe(memoryUsagePercentGauge, memoryInfo.usagePercent, {
+          'host.name': os.hostname(),
+        });
+
+        // Process memory
+        const processMemory = process.memoryUsage();
+        result.observe(processMemoryHeapGauge, processMemory.heapUsed, {
+          'process.pid': process.pid.toString(),
+        });
+        result.observe(processMemoryRssGauge, processMemory.rss, {
+          'process.pid': process.pid.toString(),
+        });
+      }
+      catch (error) {
+        logger.error('Failed to collect system metrics', { error });
+      }
+    },
+    [cpuUsageGauge, memoryUsageGauge, memoryUsagePercentGauge, processMemoryHeapGauge, processMemoryRssGauge],
+  );
+
+  logger.info('System metrics collection started successfully');
+}