Skip to content

Commit 70368bc

Browse files
committed
Fix Prometheus exporter
1 parent d0448c3 commit 70368bc

File tree

1 file changed

+54
-80
lines changed

1 file changed

+54
-80
lines changed

packages/lib/vendor/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts

+54-80
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,8 @@ import {
2121
AggregationTemporality,
2222
MetricReader,
2323
} from "../../opentelemetry-sdk-metrics/mod.ts";
24-
import {
25-
createServer,
26-
IncomingMessage,
27-
Server,
28-
ServerResponse,
29-
} from "node:http";
3024
import { ExporterConfig } from "./export/types.ts";
3125
import { PrometheusSerializer } from "./PrometheusSerializer.ts";
32-
/** Node.js v8.x compat */
33-
import { URL } from "node:url";
3426

3527
export class PrometheusExporter extends MetricReader {
3628
static readonly DEFAULT_OPTIONS = {
@@ -45,7 +37,8 @@ export class PrometheusExporter extends MetricReader {
4537
private readonly _port: number;
4638
private readonly _baseUrl: string;
4739
private readonly _endpoint: string;
48-
private readonly _server: Server;
40+
private _server?: Deno.Server;
41+
private _serverAbort?: AbortController;
4942
private readonly _prefix?: string;
5043
private readonly _appendTimestamp: boolean;
5144
private _serializer: PrometheusSerializer;
@@ -81,8 +74,6 @@ export class PrometheusExporter extends MetricReader {
8174
typeof config.appendTimestamp === "boolean"
8275
? config.appendTimestamp
8376
: PrometheusExporter.DEFAULT_OPTIONS.appendTimestamp;
84-
// unref to prevent prometheus exporter from holding the process open on exit
85-
this._server = createServer(this._requestHandler).unref();
8677
this._serializer = new PrometheusSerializer(
8778
this._prefix,
8879
this._appendTimestamp,
@@ -120,25 +111,16 @@ export class PrometheusExporter extends MetricReader {
120111
stopServer(): Promise<void> {
121112
if (!this._server) {
122113
diag.debug(
123-
"Prometheus stopServer() was called but server was never started.",
114+
"Prometheus stopServer() was called but server wasn't started.",
124115
);
125116
return Promise.resolve();
126117
} else {
127-
return new Promise((resolve) => {
128-
this._server.close((err) => {
129-
if (!err) {
130-
diag.debug("Prometheus exporter was stopped");
131-
} else {
132-
if (
133-
(err as unknown as { code: string }).code !==
134-
"ERR_SERVER_NOT_RUNNING"
135-
) {
136-
globalErrorHandler(err);
137-
}
138-
}
139-
resolve();
140-
});
141-
});
118+
this._serverAbort?.abort();
119+
return this._server.finished
120+
.then(() => {
121+
this._server = undefined;
122+
})
123+
.catch(globalErrorHandler);
142124
}
143125
}
144126

@@ -147,32 +129,49 @@ export class PrometheusExporter extends MetricReader {
147129
*/
148130
startServer(): Promise<void> {
149131
return new Promise((resolve, reject) => {
150-
this._server.once("error", reject);
151-
this._server.listen(
152-
{
153-
port: this._port,
154-
host: this._host,
155-
},
156-
() => {
157-
diag.debug(
158-
`Prometheus exporter server started: ${this._host}:${this._port}/${this._endpoint}`,
159-
);
160-
resolve();
161-
},
162-
);
132+
try {
133+
this._serverAbort = new AbortController();
134+
this._server = Deno.serve(
135+
{
136+
hostname: this._host,
137+
port: this._port,
138+
onListen: () => {
139+
diag.debug(
140+
`Prometheus exporter server started: ${this._host}:${this._port}/${this._endpoint}`,
141+
);
142+
resolve();
143+
},
144+
signal: this._serverAbort.signal,
145+
},
146+
this._requestHandler,
147+
);
148+
this._server.unref();
149+
} catch (error) {
150+
reject(error);
151+
}
163152
});
164153
}
165154

166155
/**
167-
* Request handler that responds with the current state of metrics
168-
* @param _request Incoming HTTP request of server instance
169-
* @param response HTTP response object used to response to request
156+
* Creates a response for the current state of metrics.
170157
*/
171-
public getMetricsRequestHandler(
172-
_request: IncomingMessage,
173-
response: ServerResponse,
174-
): void {
175-
this._exportMetrics(response);
158+
public async getMetricsResponse(): Promise<Response> {
159+
try {
160+
const { resourceMetrics, errors } = await this.collect();
161+
if (errors.length) {
162+
diag.error("PrometheusExporter: metrics collection errors", ...errors);
163+
}
164+
165+
return new Response(this._serializer.serialize(resourceMetrics), {
166+
status: 200,
167+
headers: { "content-type": "text/plain" },
168+
});
169+
} catch (error) {
170+
return new Response(`# failed to export metrics: ${error}`, {
171+
status: 500,
172+
headers: { "content-type": "text/plain" },
173+
});
174+
}
176175
}
177176

178177
/**
@@ -183,47 +182,22 @@ export class PrometheusExporter extends MetricReader {
183182
* @param response HTTP response object used to respond to request
184183
*/
185184
private _requestHandler = (
186-
request: IncomingMessage,
187-
response: ServerResponse,
188-
) => {
185+
request: Request,
186+
): Response | Promise<Response> => {
189187
if (
190188
request.url != null &&
191189
new URL(request.url, this._baseUrl).pathname === this._endpoint
192190
) {
193-
this._exportMetrics(response);
191+
return this.getMetricsResponse();
194192
} else {
195-
this._notFound(response);
193+
return this._notFound();
196194
}
197195
};
198196

199-
/**
200-
* Responds to incoming message with current state of all metrics.
201-
*/
202-
private _exportMetrics = (response: ServerResponse) => {
203-
response.statusCode = 200;
204-
response.setHeader("content-type", "text/plain");
205-
this.collect().then(
206-
(collectionResult) => {
207-
const { resourceMetrics, errors } = collectionResult;
208-
if (errors.length) {
209-
diag.error(
210-
"PrometheusExporter: metrics collection errors",
211-
...errors,
212-
);
213-
}
214-
response.end(this._serializer.serialize(resourceMetrics));
215-
},
216-
(err) => {
217-
response.end(`# failed to export metrics: ${err}`);
218-
},
219-
);
220-
};
221-
222197
/**
223198
* Responds with 404 status code to all requests that do not match the configured endpoint.
224199
*/
225-
private _notFound = (response: ServerResponse) => {
226-
response.statusCode = 404;
227-
response.end();
228-
};
200+
private _notFound(): Response {
201+
return new Response(null, { status: 404 });
202+
}
229203
}

0 commit comments

Comments
 (0)