Files
oneuptime/Common/Server/Utils/Profiling.ts

102 lines
2.7 KiB
TypeScript

import Pyroscope from "@pyroscope/nodejs";
import { EnableProfiling } from "../EnvironmentConfig";
import logger from "./Logger";
export default class Profiling {
public static init(data: { serviceName: string }): void {
if (!EnableProfiling) {
return;
}
const serverAddress: string | undefined = this.getServerAddress();
const authToken: string | undefined = this.getAuthToken();
if (!serverAddress) {
logger.warn(
"Profiling enabled but OPENTELEMETRY_EXPORTER_OTLP_ENDPOINT not configured. Skipping profiling initialization.",
);
return;
}
try {
Pyroscope.init({
appName: data.serviceName,
serverAddress: serverAddress,
authToken: authToken,
wall: {
collectCpuTime: true,
},
});
Pyroscope.start();
logger.info(
`Profiling initialized for service: ${data.serviceName} -> ${serverAddress}`,
);
} catch (err) {
logger.error("Failed to initialize profiling:");
logger.error(err);
}
process.on("SIGTERM", () => {
Pyroscope.stop().catch((err: unknown) => {
logger.error("Error stopping profiler:");
logger.error(err);
});
});
}
private static getServerAddress(): string | undefined {
/*
* Use the OTLP endpoint base URL as the Pyroscope server address.
* The Pyroscope SDK will append /ingest to this URL.
* The final URL will be /pyroscope/ingest, routed by nginx to the telemetry service.
*/
const endpoint: string | undefined =
process.env["OPENTELEMETRY_EXPORTER_OTLP_ENDPOINT"];
if (!endpoint) {
return undefined;
}
/*
* Strip /otlp suffix if present and append /pyroscope
* The Pyroscope SDK appends /ingest, so the final URL will be /pyroscope/ingest
*/
let baseUrl: string = endpoint;
if (baseUrl.endsWith("/otlp")) {
baseUrl = baseUrl.substring(0, baseUrl.length - 5);
}
if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.substring(0, baseUrl.length - 1);
}
return `${baseUrl}/pyroscope`;
}
private static getAuthToken(): string | undefined {
/*
* Extract the OneUptime token from OTLP headers
* Format: "x-oneuptime-token=<value>;other-header=value"
*/
const headersStr: string | undefined =
process.env["OPENTELEMETRY_EXPORTER_OTLP_HEADERS"];
if (!headersStr) {
return undefined;
}
const parts: Array<string> = headersStr.split(";");
for (const part of parts) {
const [key, value]: Array<string | undefined> = part.split("=") as Array<
string | undefined
>;
if (key === "x-oneuptime-token" && value) {
return value;
}
}
return undefined;
}
}