Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 0 additions & 31 deletions libs/back/tracer/src/eventLoop.ts

This file was deleted.

12 changes: 8 additions & 4 deletions libs/back/tracer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { PrismaInstrumentation } from "@prisma/instrumentation";
import { ElasticsearchInstrumentation } from "opentelemetry-instrumentation-elasticsearch";
import { getAppRootFolderName } from "./utils";
import { initializeNodeRuntimeMetrics } from "./metric";

// Incubating attributes
const ATTR_CLOUD_REGION = "cloud.region";
Expand Down Expand Up @@ -68,14 +69,17 @@ if (process.env.NODE_ENV !== "test" && !process.env.OTEL_SDK_DISABLED) {
try {
sdk.start();
console.info("Telemetry started");

const cleanupRuntimeMetrics = initializeNodeRuntimeMetrics();

process.on("SIGTERM", () => {
cleanupRuntimeMetrics();
sdk.shutdown();
});
} catch (error) {
console.error(
"Error initializing OpenTelemetry SDK. Your application is not instrumented and will not produce telemetry",
error
);
}

process.on("SIGTERM", () => {
sdk.shutdown();
});
}
89 changes: 89 additions & 0 deletions libs/back/tracer/src/metric.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { metrics } from "@opentelemetry/api";

const METRIC_INTERVAL_MS = 30000;

export function initializeNodeRuntimeMetrics() {
const meter = metrics.getMeter("nodejs-runtime-metrics");

// Memory metrics (gauges)
const heapUsedGauge = meter.createGauge("nodejs_heap_used", {
description: "V8 heap memory used in bytes",
unit: "By"
});

const heapTotalGauge = meter.createGauge("nodejs_heap_total", {
description: "Total allocated V8 heap memory in bytes",
unit: "By"
});

const heapRssGauge = meter.createGauge("nodejs_heap_rss", {
description: "Resident set size (total memory usage) in bytes",
unit: "By"
});

const heapExternalGauge = meter.createGauge("nodejs_heap_external", {
description: "V8 external memory usage in bytes",
unit: "By"
});

// Process metrics (gauges)
const processUptimeGauge = meter.createGauge("nodejs_process_uptime", {
description: "Process uptime in seconds",
unit: "s"
});

// Performance metrics (histograms)
const eventLoopLagHistogram = meter.createHistogram("nodejs_eventloop_lag", {
description: "Event loop lag in milliseconds",
unit: "ms"
});

const cpuUserHistogram = meter.createHistogram("nodejs_cpu_user", {
description: "CPU user time delta in milliseconds",
unit: "ms"
});

const cpuSystemHistogram = meter.createHistogram("nodejs_cpu_system", {
description: "CPU system time delta in milliseconds",
unit: "ms"
});

let lastCpuUsage = process.cpuUsage();

const recordRuntimeMetrics = () => {
const memUsage = process.memoryUsage();

heapUsedGauge.record(memUsage.heapUsed);
heapTotalGauge.record(memUsage.heapTotal);
heapRssGauge.record(memUsage.rss);
heapExternalGauge.record(memUsage.external);

processUptimeGauge.record(process.uptime());

const start = process.hrtime.bigint();
setImmediate(() => {
const lag = Number(process.hrtime.bigint() - start) / 1e6; // Convert to milliseconds
eventLoopLagHistogram.record(lag);
});

// Record CPU metrics (deltas)
const currentCpuUsage = process.cpuUsage();
const userDelta = (currentCpuUsage.user - lastCpuUsage.user) / 1000; // Convert to milliseconds
const systemDelta = (currentCpuUsage.system - lastCpuUsage.system) / 1000;

cpuUserHistogram.record(userDelta);
cpuSystemHistogram.record(systemDelta);

lastCpuUsage = currentCpuUsage;
};

const metricsInterval = setInterval(recordRuntimeMetrics, METRIC_INTERVAL_MS);
// Allow process to exit even with pending timer
metricsInterval.unref();

recordRuntimeMetrics();

return () => {
clearInterval(metricsInterval);
};
}