Files
oneuptime/Telemetry/API/Pyroscope.ts
Nawaz Dhandala e30f2587e8 feat: integrate Pyroscope for performance profiling
- Added @pyroscope/nodejs dependency to package.json and package-lock.json.
- Implemented Pyroscope API routes in Telemetry/Index.ts.
- Created new Pyroscope API handler in Telemetry/API/Pyroscope.ts to manage profile ingestion.
- Defined pprof protocol buffer schema in Telemetry/ProtoFiles/pprof/profile.proto.
- Developed PyroscopeIngestService for processing and converting pprof profiles to OTLP format.
- Enhanced error handling and request validation in the Pyroscope ingestion process.
2026-03-31 13:55:14 +01:00

77 lines
2.1 KiB
TypeScript

import TelemetryIngest, {
TelemetryRequest,
} from "Common/Server/Middleware/TelemetryIngest";
import ProductType from "Common/Types/MeteredPlan/ProductType";
import Express, {
ExpressRequest,
ExpressResponse,
ExpressRouter,
NextFunction,
RequestHandler,
} from "Common/Server/Utils/Express";
import PyroscopeIngestService from "../Services/PyroscopeIngestService";
import MultipartFormDataMiddleware from "Common/Server/Middleware/MultipartFormData";
const router: ExpressRouter = Express.getRouter();
// Set product type to Profiles for metering
const setProfilesProductType: RequestHandler = (
req: ExpressRequest,
_res: ExpressResponse,
next: NextFunction,
): void => {
(req as TelemetryRequest).productType = ProductType.Profiles;
next();
};
// Map Authorization: Bearer <token> to x-oneuptime-token header
// Pyroscope SDKs use authToken which sends Authorization: Bearer
const mapBearerTokenMiddleware: RequestHandler = (
req: ExpressRequest,
_res: ExpressResponse,
next: NextFunction,
): void => {
if (!req.headers["x-oneuptime-token"]) {
const authHeader: string | undefined = req.headers[
"authorization"
] as string;
if (authHeader && authHeader.startsWith("Bearer ")) {
req.headers["x-oneuptime-token"] = authHeader.substring(7);
}
}
next();
};
router.post(
"/pyroscope/ingest",
MultipartFormDataMiddleware,
mapBearerTokenMiddleware,
setProfilesProductType,
TelemetryIngest.isAuthorizedServiceMiddleware,
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction,
): Promise<void> => {
return PyroscopeIngestService.ingestPyroscopeProfile(req, res, next);
},
);
// Also mount at /ingest for Pyroscope SDKs that use serverAddress without a subpath
router.post(
"/ingest",
MultipartFormDataMiddleware,
mapBearerTokenMiddleware,
setProfilesProductType,
TelemetryIngest.isAuthorizedServiceMiddleware,
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction,
): Promise<void> => {
return PyroscopeIngestService.ingestPyroscopeProfile(req, res, next);
},
);
export default router;