From 63dd84339ee945d180e62295c34c35434e9fc935 Mon Sep 17 00:00:00 2001 From: Nawaz Dhandala Date: Tue, 31 Mar 2026 22:50:13 +0100 Subject: [PATCH] feat: update Pyroscope endpoints and documentation for improved profiling integration --- .../src/Components/Telemetry/Documentation.tsx | 2 +- App/FeatureSet/Docs/Content/telemetry/profiles.md | 2 +- Common/Server/Utils/Profiling.ts | 7 ++++--- Nginx/default.conf.template | 12 ++++++++++++ Telemetry/API/Pyroscope.ts | 15 --------------- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/App/FeatureSet/Dashboard/src/Components/Telemetry/Documentation.tsx b/App/FeatureSet/Dashboard/src/Components/Telemetry/Documentation.tsx index ef1be67db0..63bca8eb89 100644 --- a/App/FeatureSet/Dashboard/src/Components/Telemetry/Documentation.tsx +++ b/App/FeatureSet/Dashboard/src/Components/Telemetry/Documentation.tsx @@ -1114,7 +1114,7 @@ const TelemetryDocumentation: FunctionComponent = ( ? `${httpProtocol}://${HOST}/otlp` : ""; const pyroscopeUrl: string = HOST - ? `${httpProtocol}://${HOST}` + ? `${httpProtocol}://${HOST}/pyroscope` : ""; // Fetch ingestion keys on mount diff --git a/App/FeatureSet/Docs/Content/telemetry/profiles.md b/App/FeatureSet/Docs/Content/telemetry/profiles.md index 494c554def..2f9395e698 100644 --- a/App/FeatureSet/Docs/Content/telemetry/profiles.md +++ b/App/FeatureSet/Docs/Content/telemetry/profiles.md @@ -72,7 +72,7 @@ pyroscope.ebpf "default" { pyroscope.write "oneuptime" { endpoint { - url = "https://oneuptime.com" + url = "https://oneuptime.com/pyroscope" headers = { "x-oneuptime-token" = "YOUR_ONEUPTIME_SERVICE_TOKEN", } diff --git a/Common/Server/Utils/Profiling.ts b/Common/Server/Utils/Profiling.ts index fdc37bd633..d781581623 100644 --- a/Common/Server/Utils/Profiling.ts +++ b/Common/Server/Utils/Profiling.ts @@ -50,7 +50,7 @@ export default class Profiling { /* * Use the OTLP endpoint base URL as the Pyroscope server address. * The Pyroscope SDK will append /ingest to this URL. - * The Telemetry service has a Pyroscope-compatible /ingest endpoint. + * The final URL will be /pyroscope/ingest, routed by nginx to the telemetry service. */ const endpoint: string | undefined = process.env["OPENTELEMETRY_EXPORTER_OTLP_ENDPOINT"]; @@ -59,7 +59,8 @@ export default class Profiling { return undefined; } - // Strip /otlp suffix if present, since Pyroscope SDK appends /ingest + // 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); @@ -68,7 +69,7 @@ export default class Profiling { baseUrl = baseUrl.substring(0, baseUrl.length - 1); } - return baseUrl; + return `${baseUrl}/pyroscope`; } private static getAuthToken(): string | undefined { diff --git a/Nginx/default.conf.template b/Nginx/default.conf.template index f883eb9e56..f8049ef3e2 100644 --- a/Nginx/default.conf.template +++ b/Nginx/default.conf.template @@ -559,6 +559,18 @@ ${PROVISION_SSL_CERTIFICATE_KEY_DIRECTIVE} proxy_pass $backend_telemetry; } + # Pyroscope profiling ingestion endpoint + location /pyroscope { + resolver ${NGINX_RESOLVER} valid=30s; + set $backend_telemetry http://${SERVER_TELEMETRY_HOSTNAME}:${TELEMETRY_PORT}; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_pass $backend_telemetry; + } + location ~ /opentelemetry.proto.collector* { resolver ${NGINX_RESOLVER} valid=30s; set $backend_otel_grpc grpc://${SERVER_TELEMETRY_HOSTNAME}:4317; diff --git a/Telemetry/API/Pyroscope.ts b/Telemetry/API/Pyroscope.ts index d054f5a830..2bd60069ec 100644 --- a/Telemetry/API/Pyroscope.ts +++ b/Telemetry/API/Pyroscope.ts @@ -59,20 +59,5 @@ router.post( }, ); -// 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 => { - return PyroscopeIngestService.ingestPyroscopeProfile(req, res, next); - }, -); export default router;