-
Notifications
You must be signed in to change notification settings - Fork 288
feat(o11y): add hibp request count and metrics endpoint #6365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8d167db
94b6dba
559a390
a1d4bd0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| /* This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
|
||
| import { NextResponse } from "next/server"; | ||
| import { registry } from "../../../../instrumentation.node"; | ||
|
|
||
| export async function GET() { | ||
| const metrics = await registry.metrics(); | ||
| return new NextResponse(metrics, { | ||
| status: 200, | ||
| headers: { | ||
| "Content-Type": | ||
| registry.contentType || "text/plain; version=0.0.4; charset=utf-8", | ||
| }, | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| /* This Source Code Form is subject to the terms of the Mozilla Public | ||
| * License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
|
||
| export const runtime = "nodejs"; | ||
|
|
||
| import client from "prom-client"; | ||
|
|
||
| type MetricsState = { | ||
| registry: Readonly<client.Registry>; | ||
| hibpNotifyRequestsTotal: client.Counter; | ||
| hibpNotifyRequestFailuresTotal: client.Counter<"error">; | ||
| }; | ||
|
|
||
| declare global { | ||
| var metrics: Readonly<MetricsState>; | ||
| } | ||
|
|
||
| function getOrInitMetrics(): MetricsState { | ||
| // Return cached state | ||
| if (globalThis.metrics !== undefined) return globalThis.metrics; | ||
| const registry = new client.Registry(); | ||
| client.collectDefaultMetrics({ register: registry }); | ||
|
Comment on lines
+21
to
+23
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just wondering if this shouldn't be done in (That would only work in the context of Next.js, but I imagine the setup is going to be different in different environments anyway.)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| /** | ||
| * hibp_notify_requests_total | ||
| * Metric to instrument the number of requests from HIBP notifying of breaches | ||
| * Scope: "/hibp/notify" | ||
| */ | ||
| const hibpNotifyRequestsTotal = new client.Counter({ | ||
| name: "hibp_notify_requests_total", | ||
| help: "Metric to instrument the number of requests from HIBP notifying of breaches", | ||
| registers: [registry], | ||
| }); | ||
|
|
||
| /** | ||
| * hibp_notify_request_failures_total{error="..."} | ||
| * Metric to instrument the number of failed requests on HIBP notify endpoint | ||
| * Labels: | ||
| * - error: one of "timeout", "bad-request", "rate-limited" | ||
| * Scope: "/hibp/notify" | ||
| */ | ||
| const hibpNotifyRequestFailuresTotal = new client.Counter<"error">({ | ||
| name: "hibp_notify_request_failures_total", | ||
| help: "Metric to instrument the number of failed requests on HIBP notify endpoint", | ||
| labelNames: ["error"], | ||
| registers: [registry], | ||
| }); | ||
|
|
||
| const state: MetricsState = { | ||
| registry, | ||
| hibpNotifyRequestFailuresTotal, | ||
| hibpNotifyRequestsTotal, | ||
| }; | ||
|
|
||
| // Make it readonly | ||
| Object.defineProperty(globalThis, "metrics", { | ||
| value: state, | ||
| writable: false, | ||
| configurable: false, | ||
| enumerable: false, | ||
| }); | ||
| return state; | ||
| } | ||
|
|
||
| export const { | ||
| registry, | ||
| hibpNotifyRequestsTotal, | ||
| hibpNotifyRequestFailuresTotal, | ||
| } = getOrInitMetrics(); | ||
|
|
||
| export type HibpNotifyFailureError = | ||
| | "server-error" | ||
| | "pubsub-error" | ||
| | "bad-request" | ||
| | "rate-limited" | ||
| | "unauthorized" | ||
| | "invalid-config"; | ||
|
|
||
| /** | ||
| * Increment helper to keep label values consistent | ||
| * on hibpNotifyRequestFailuresTotal | ||
| */ | ||
| export function incHibpNotifyFailure(error: HibpNotifyFailureError, by = 1) { | ||
| hibpNotifyRequestFailuresTotal.inc({ error }, by); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # This documents custom metrics that are implemented in the app | ||
| --- | ||
| version: 1.0 | ||
|
|
||
| service: Monitor | ||
|
|
||
| hibp: | ||
| hibp_notify_requests_total: | ||
| description: | | ||
| Metric to instrument the number of requests from HIBP notifying of breaches | ||
| type: counter | ||
| scope: | | ||
| The "/hibp/notify" endpoint | ||
| # No alerting | ||
| alert_policy: [] | ||
|
|
||
| hibp_notify_request_failures_total: | ||
| description: | | ||
| Metric to instrument the number of failed requests on HIBP notify endpoint | ||
| type: counter | ||
| labels: | ||
| - name: error | ||
| description: | | ||
| Record the error. Any of "server-error", "pubsub-error", "bad-request", | ||
| "rate-limited", "unauthorized", "invalid-config" | ||
| scope: | | ||
| The "/hibp/notify" endpoint | ||
| alert_policy: | ||
| - severity_level: S1 | ||
| trigger_condition: | | ||
| Trigger when the aggregate failure rate is greater than | ||
| 20 req/sec. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thought (unrelated to this PR): Hmm, maybe this approach is also something we could use to deal with env vars that we only need to be set at runtime, but not buildtime - only provide a fallback value if
process.env.CIis set.