Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions __tests__/metrics.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { GET } from "../app/metrics/route";

describe("metrics route", () => {
it("returns status ok", async () => {
const response = await GET();
const text = await response.text();
expect(text).toEqual(expect.any(String));
});
});
32 changes: 32 additions & 0 deletions app/libraries/requestCounter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// lib/requestCounter.ts

// Use globalThis to persist counters across module reloads
const globalForCounters = globalThis as unknown as {
statusCounters: Record<string, Record<string, number>> | undefined;
};

if (!globalForCounters.statusCounters) {
globalForCounters.statusCounters = {};
}

const { statusCounters } = globalForCounters;

export function incrementCounter(route: string, statusCode?: number) {
// Registers status of the response for the given route. If statusCode is undefined, it will not be counted.
if (statusCode !== undefined) {
if (!statusCounters[route]) {
statusCounters[route] = {};
}
const status = statusCode.toString();
if (statusCounters[route][status] === undefined) {
statusCounters[route][status] = 0;
}
statusCounters[route][status] += 1;
} else {
console.log(`Error in counting requests for the ${route}`);
}
}

export function getStatusCounters() {
return statusCounters;
}
48 changes: 48 additions & 0 deletions app/metrics/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { getStatusCounters } from "../libraries/requestCounter";

export async function GET() {
const usage = process.cpuUsage();
const totalSeconds = (usage.user + usage.system) / 1e6;
const memoria = process.memoryUsage();
let body = `
# HELP up If the server is responding to HTTP requests
# TYPE up gauge
up 1
# HELP process_cpu_seconds_total Total CPU time in seconds
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total ${totalSeconds}
# HELP process_memoria_rss Resident Set Size: total memory allocated for the process
# TYPE process_memoria_rss gauge
process_memoria_rss ${memoria.rss}
# HELP process_memoria_heapTotal Total heap size allocated.
# TYPE process_memoria_heapTotal gauge
process_memoria_heapTotal ${memoria.heapTotal}
# HELP process_memoria_heapUsed Total heap size used.
# TYPE process_memoria_heapUsed gauge
process_memoria_heapUsed ${memoria.heapUsed}
# HELP process_memoria_external Total external memory size.
# TYPE process_memoria_external gauge
process_memoria_external ${memoria.external}
# HELP process_memoria_arrayBuffers Total array buffers size.
# TYPE process_memoria_arrayBuffers gauge
process_memoria_arrayBuffers ${memoria.arrayBuffers}
`;

// Add status code metrics
body += "# HELP http_requests_by_status Total requests per route and status code\n";
body += "# TYPE http_requests_by_status counter\n";
const statusCounters = getStatusCounters();
// for loop inside for loop to iterate over all routes and status
Object.entries(statusCounters).forEach(([route, statuses]) => {
Object.entries(statuses).forEach(([status, count]) => {
body += `http_requests_by_status{route="${route}",status="${status}"} ${count}\n`;
});
});

return new Response(body, {
status: 200,
headers: {
"Content-Type": "text/plain; version=0.0.4",
},
});
}