-
Notifications
You must be signed in to change notification settings - Fork 281
Expand file tree
/
Copy pathmonitoringServer.ts
More file actions
105 lines (96 loc) · 3.92 KB
/
Copy pathmonitoringServer.ts
File metadata and controls
105 lines (96 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import type express from "express";
import { LogId } from "../common/logging/loggingDefinitions.js";
import { packageInfo } from "../common/packageInfo.js";
import type {
DefaultMetrics,
MonitoringServerFeature,
Metrics,
LoggerBase,
UserConfig,
MonitoringServerConstructorArgs,
} from "../lib.js";
import { ExpressBasedHttpServer } from "./expressBasedHttpServer.js";
export class MonitoringServer<TMetrics extends DefaultMetrics = DefaultMetrics> extends ExpressBasedHttpServer {
private readonly features: MonitoringServerFeature[];
private readonly metrics: Metrics<TMetrics>;
constructor({
host,
port,
features,
logger,
metrics,
}: {
host: string;
port: number;
features: MonitoringServerFeature[];
logger: LoggerBase;
metrics: Metrics<TMetrics>;
}) {
super({ port, hostname: host, logger, logContext: "monitoringServer" });
this.features = features;
this.metrics = metrics;
}
static fromConfig<TMetrics extends DefaultMetrics = DefaultMetrics>({
userConfig,
logger,
metrics,
}: {
userConfig: UserConfig;
logger: LoggerBase;
metrics: Metrics<TMetrics>;
}): MonitoringServer<TMetrics> | undefined {
const host = userConfig.monitoringServerHost ?? userConfig.healthCheckHost;
const port = userConfig.monitoringServerPort ?? userConfig.healthCheckPort;
if (host === undefined || port === undefined) {
return undefined;
}
return new MonitoringServer({ host, port, features: userConfig.monitoringServerFeatures, logger, metrics });
}
protected override setupRoutes(): Promise<void> {
if (this.features.includes("health-check")) {
this.app.get("/health", (_req: express.Request, res: express.Response) => {
// Health responses should never be cached by proxies or load balancers.
res.set("Cache-Control", "no-store");
res.json({
status: "ok",
version: packageInfo.version,
uptimeSeconds: Math.floor(process.uptime()),
timestamp: new Date().toISOString(),
});
});
}
if (this.features.includes("metrics") && this.metrics?.getMetrics) {
const getMetrics = this.metrics.getMetrics.bind(this.metrics);
this.app.get("/metrics", async (_req: express.Request, res: express.Response) => {
try {
const output = await getMetrics();
res.set("Content-Type", "text/plain");
res.send(output);
} catch (error: unknown) {
this.logger.error({
id: LogId.monitoringServerMetricsFailure,
context: "monitoringServer",
message: `Failed to retrieve metrics: ${String(error)}`,
});
res.status(500).json({ error: "Failed to retrieve metrics" });
}
});
}
return Promise.resolve();
}
}
/**
* A function to create a custom MonitoringServer instance.
* When provided, the runner will use this function instead of the default MonitoringServer constructor.
*/
export type CreateMonitoringServerFn<TMetrics extends DefaultMetrics = DefaultMetrics> = (
args: MonitoringServerConstructorArgs<TMetrics>
) => MonitoringServer<TMetrics> | undefined;
/**
* Creates a default MonitoringServer instance from the provided constructor arguments.
*/
export const createDefaultMonitoringServer: <TMetrics extends DefaultMetrics = DefaultMetrics>(
args: MonitoringServerConstructorArgs<TMetrics>
) => MonitoringServer<TMetrics> = <TMetrics extends DefaultMetrics = DefaultMetrics>(
args: MonitoringServerConstructorArgs<TMetrics>
) => new MonitoringServer<TMetrics>(args);