diff --git a/__tests__/contracts/stellar.test.ts b/__tests__/contracts/stellar.test.ts index 52a3736..0a776cd 100644 --- a/__tests__/contracts/stellar.test.ts +++ b/__tests__/contracts/stellar.test.ts @@ -11,8 +11,9 @@ import contractsMcpPackage from "@openzeppelin/contracts-mcp/package.json"; const STELLAR_TOOLS_NAMES = [ "stellar-fungible", - "stellar-non-fungible", + "stellar-governor", "stellar-stablecoin", + "stellar-non-fungible" ]; const STELLAR_ENDPOINT = "http://localhost:3000/contracts/stellar/mcp"; diff --git a/__tests__/metrics.test.ts b/__tests__/metrics.test.ts new file mode 100644 index 0000000..bb9755a --- /dev/null +++ b/__tests__/metrics.test.ts @@ -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)); + }); +}); diff --git a/app/libraries/requestCounter.ts b/app/libraries/requestCounter.ts new file mode 100644 index 0000000..7903cbd --- /dev/null +++ b/app/libraries/requestCounter.ts @@ -0,0 +1,32 @@ +// lib/requestCounter.ts + +// Use globalThis to persist counters across module reloads +const globalForCounters = globalThis as unknown as { + statusCounters: Record> | 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; +} diff --git a/app/metrics/route.ts b/app/metrics/route.ts new file mode 100644 index 0000000..c55e6ca --- /dev/null +++ b/app/metrics/route.ts @@ -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", + }, + }); +}