Skip to content

Conversation

@wylited
Copy link

@wylited wylited commented Jan 20, 2026

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.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +93 to +115
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,
};
}
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +116
if (options.lokiHost) {
const lokiTransport = {
target: "pino-loki",
options: {
batching: true,
interval: 5, // Logs are sent every 5 seconds, default.
host: options.lokiHost,
labels: { application: packageJson.name },
},
};

const existingLogger = options.logger;

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,
};
}
}
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Loki transport configuration logic (lines 80-116) lacks test coverage. Since the repository uses comprehensive automated testing for TypeScript code, consider adding tests to verify the logger configuration is correctly merged when lokiHost is provided, especially for edge cases like when logger is a boolean, an object with existing transports (single or array), or undefined.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants