use otel for observability (#635)

## Description:

## Please complete the following:

- [ ] I have added screenshots for all UI updates
- [ ] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [ ] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:

<DISCORD USERNAME>

Co-authored-by: evan <openfrontio@gmail.com>
This commit is contained in:
evanpelle
2025-05-01 11:22:56 -07:00
committed by GitHub
co-authored by evan
parent 2bef39408c
commit ffc2fadc20
17 changed files with 1859 additions and 430 deletions
+86 -28
View File
@@ -1,45 +1,103 @@
import promClient from "prom-client";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import {
MeterProvider,
PeriodicExportingMetricReader,
} from "@opentelemetry/sdk-metrics";
import * as dotenv from "dotenv";
import { getServerConfigFromServer } from "../core/configuration/ConfigLoader";
import { GameManager } from "./GameManager";
import { getOtelResource } from "./OtelResource";
// Initialize the Prometheus registry
const register = new promClient.Registry();
dotenv.config();
// Enable default Node.js metrics collection
promClient.collectDefaultMetrics({ register });
// Get server configuration
const config = getServerConfigFromServer();
// Add worker-specific metrics
const activeGamesGauge = new promClient.Gauge({
name: "openfront_active_games_count",
help: "Number of active games on this worker",
registers: [register],
// Create resource with worker information
const resource = getOtelResource();
// Configure headers for basic auth if provided
const getAuthHeaders = () => {
const headers = {};
if (config.otelEnabled()) {
headers["Authorization"] =
"Basic " +
Buffer.from(`${config.otelUsername()}:${config.otelPassword()}`).toString(
"base64",
);
}
return headers;
};
// Create metrics exporter
const metricExporter = new OTLPMetricExporter({
// Dummy endpoint if OTEL is not enabled to avoid parsing errors
url: `${config.otelEndpoint() || "https://dummy_endpoint.com"}/v1/metrics`,
headers: getAuthHeaders(),
});
const connectedClientsGauge = new promClient.Gauge({
name: "openfront_connected_clients_count",
help: "Number of connected clients on this worker",
registers: [register],
// Configure the metric reader
const metricReader = new PeriodicExportingMetricReader({
exporter: metricExporter,
exportIntervalMillis: 15000, // Export metrics every 15 seconds
});
const memoryUsageGauge = new promClient.Gauge({
name: "openfront_memory_usage_bytes",
help: "Current memory usage of the worker process in bytes",
registers: [register],
// Create a meter provider
const meterProvider = new MeterProvider({
resource,
readers: [metricReader],
});
// Get meter for creating metrics
const meter = meterProvider.getMeter("worker-metrics");
// Create OpenTelemetry metrics
const activeGamesCounter = meter.createUpDownCounter(
"openfront.active_games.count",
{
description: "Number of active games on this worker",
},
);
const connectedClientsCounter = meter.createUpDownCounter(
"openfront.connected_clients.count",
{
description: "Number of connected clients on this worker",
},
);
const memoryUsageObservable = meter.createObservableGauge(
"openfront.memory_usage.bytes",
{
description: "Current memory usage of the worker process in bytes",
},
);
// Register callback for the memory usage observable
memoryUsageObservable.addCallback((result) => {
const memoryUsage = process.memoryUsage();
result.observe(memoryUsage.heapUsed);
});
// Export the metrics for use in the worker
export const metrics = {
register,
activeGamesGauge,
connectedClientsGauge,
memoryUsageGauge,
// Function to update game-related metrics
updateGameMetrics: (gameManager: GameManager) => {
activeGamesGauge.set(gameManager.activeGames());
connectedClientsGauge.set(gameManager.activeClients());
console.log("Updating game metrics");
// Get the current counts
const currentActiveGames = gameManager.activeGames();
const currentActiveClients = gameManager.activeClients();
// Update memory usage metrics
const memoryUsage = process.memoryUsage();
memoryUsageGauge.set(memoryUsage.heapUsed);
// Set the absolute values (createUpDownCounter allows setting absolute values)
activeGamesCounter.add(currentActiveGames);
connectedClientsCounter.add(currentActiveClients);
// Memory metrics are automatically collected by the observable
},
// Expose the meter provider for potential additional metrics
meterProvider,
// Expose the meter for creating additional metrics
meter,
};