Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds API monitoring capabilities to the template API service by integrating Prometheus metrics collection via fastify-metrics and optional Loki logging via pino-loki. The metrics endpoint can be optionally protected with a bearer token for security.
Changes:
- Added fastify-metrics and pino-loki dependencies for monitoring and logging
- Implemented configurable Loki transport for centralized logging
- Created /metrics endpoint with optional authentication via PROMETHEUS_KEY
- Updated test helper to support passing custom options for testing different configurations
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Added dependencies for fastify-metrics, pino-loki, prom-client, and related packages |
| package.json | Added fastify-metrics and pino-loki to dependencies |
| src/app.ts | Implemented Loki transport configuration, metrics endpoint with optional authentication |
| test/helper.ts | Modified build function to accept optional AppOptions for flexible testing |
| test/routes/metrics.test.ts | Added comprehensive tests for metrics endpoint with and without authentication |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| }); | ||
|
|
||
| // Register Metrics | ||
| const metricsEndpoint: RouteOptions | string | null = opts.prometheusKey |
There was a problem hiding this comment.
The metricsEndpoint variable is typed as 'RouteOptions | string | null' but is only assigned either a RouteOptions object or a string. The 'null' type is never used and should be removed from the type annotation to accurately reflect the actual possible values.
| const metricsEndpoint: RouteOptions | string | null = opts.prometheusKey | |
| const metricsEndpoint: RouteOptions | string = opts.prometheusKey |
There was a problem hiding this comment.
Since opts.prometheusKey, is nullable, so is the route.
There was a problem hiding this comment.
I see that the entire statement is always non-null:
const metricsEndpoint: RouteOptions | string | null = opts.prometheusKey
? {
url: "/metrics",
method: "GET",
handler: async () => {}, // Overridden by fastify-metrics
onRequest: async (request: FastifyRequest, reply: FastifyReply) => {
if (
request.headers.authorization !== `Bearer ${opts.prometheusKey}`
) {
reply.code(401).send({ status: "error", message: "Unauthorized" });
return;
}
},
}
: "/metrics";If opts.prometheusKey is null then it goes to the falsy branch and evaluates to "/metrics". Maybe it's not what you intended?
| if (existingLogger && typeof existingLogger === "object") { | ||
| const loggerOptions = existingLogger as { transport?: unknown }; | ||
| const existingTransport = loggerOptions.transport; | ||
|
|
||
| let mergedTransport: unknown; | ||
| if (Array.isArray(existingTransport)) { | ||
| mergedTransport = [...existingTransport, lokiTransport]; | ||
| } else if (existingTransport) { | ||
| mergedTransport = [existingTransport, lokiTransport]; | ||
| } else { | ||
| mergedTransport = lokiTransport; | ||
| } | ||
|
|
||
| options.logger = { | ||
| ...(existingLogger as object), | ||
| transport: mergedTransport, | ||
| } as Exclude<FastifyServerOptions["logger"], boolean | undefined>; | ||
| } else { | ||
| options.logger = { | ||
| level: "info", | ||
| transport: lokiTransport, | ||
| }; | ||
| } |
There was a problem hiding this comment.
The Loki transport configuration modifies the options object at module level before the app is created. If the logger option is a boolean (true/false) rather than an object, the code in the else block (lines 111-114) will override it, potentially causing unexpected behavior. The condition on line 93 should also check if existingLogger is not a boolean value to properly handle all logger configuration types.
There was a problem hiding this comment.
For the above comment, you can proceed as follows if the type is a boolean:
- If the boolean is
true, you existing logic should already work properly. - If
falsethen you might not want to enable loki.
2d48362 to
9380014
Compare
polyipseity
left a comment
There was a problem hiding this comment.
Reviewed! Once you address the two minor issues by yourself, you can merge it.
How this monitoring system will work is that each API service will expose a fastify-metrics endpoint at /metrics, which requires a prometheus key to access.
this is a Prometheus scrapable endpoint for our monitoring platform.
Furthermore, if provided, it will fastify will log automatically to a Loki logging server using pino-loki.
The rest of the setup for monitoring will be done on the usthing server.