implement a helper to fetch metrics
This commit is contained in:
parent
a57f54bd58
commit
c3c12e1591
@ -1,6 +1,7 @@
|
|||||||
import { ServerlessCallback, ServerlessFunctionSignature } from "@twilio-labs/serverless-runtime-types/types";
|
import { ServerlessCallback, ServerlessFunctionSignature } from "@twilio-labs/serverless-runtime-types/types";
|
||||||
import { PrometheusContentType, Registry, Pushgateway, Summary } from "prom-client";
|
import { PrometheusContentType, Registry, Pushgateway, Summary, Counter } from "prom-client";
|
||||||
import { DoormanLambdaContext } from "./DoormanHandlerContext";
|
import { DoormanLambdaContext } from "./DoormanHandlerContext";
|
||||||
|
import { shouldBlockRequest } from "../utils/blockUserAgent";
|
||||||
|
|
||||||
export type BaseEvent = { request: { cookies: {}; headers: {}; }; }
|
export type BaseEvent = { request: { cookies: {}; headers: {}; }; }
|
||||||
|
|
||||||
@ -13,6 +14,12 @@ export type DoormanLambda<T extends DoormanLambdaContext, U extends BaseEvent> =
|
|||||||
|
|
||||||
export enum CommonMetrics {
|
export enum CommonMetrics {
|
||||||
RUNTIME = "FunctionRuntime",
|
RUNTIME = "FunctionRuntime",
|
||||||
|
BLOCKED_REQUEST = "BlockedRequest",
|
||||||
|
HTTP_CLIENT_ERROR = "HttpClientError",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getMetricFromRegistry<T>(metricsRegistry: Registry, metric: string): T {
|
||||||
|
return metricsRegistry.getSingleMetric(metric) as T;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,7 +41,18 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
|
|||||||
help: "Runtime of the function",
|
help: "Runtime of the function",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const summaryTimer = (metricsRegistry.getSingleMetric(CommonMetrics.RUNTIME) as Summary).startTimer();
|
metricsRegistry.registerMetric(new Counter({
|
||||||
|
name: CommonMetrics.BLOCKED_REQUEST,
|
||||||
|
help: "Blocked requests",
|
||||||
|
}));
|
||||||
|
|
||||||
|
metricsRegistry.registerMetric(new Counter({
|
||||||
|
name: CommonMetrics.HTTP_CLIENT_ERROR,
|
||||||
|
help: "Client side HTTP error",
|
||||||
|
labelNames: [ "ErrorCode" ],
|
||||||
|
}));
|
||||||
|
|
||||||
|
const summaryTimer = getMetricFromRegistry<Summary>(metricsRegistry, CommonMetrics.RUNTIME).startTimer();
|
||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
console.log(`[CommonHandler] started handler at ${startTime}`);
|
console.log(`[CommonHandler] started handler at ${startTime}`);
|
||||||
@ -46,11 +64,28 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
|
|||||||
callbackResult = [err, payload];
|
callbackResult = [err, payload];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// block requests before we even call the handler
|
||||||
|
if (shouldBlockRequest(event)) {
|
||||||
|
getMetricFromRegistry<Counter>(metricsRegistry, CommonMetrics.BLOCKED_REQUEST).inc(1);
|
||||||
|
const response = new Twilio.Response();
|
||||||
|
response.setStatusCode(200);
|
||||||
|
callbackResult = [null, response];
|
||||||
|
} else {
|
||||||
await handler(context, event, tempCallback, metricsRegistry);
|
await handler(context, event, tempCallback, metricsRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
if (!callbackResult) {
|
if (!callbackResult) {
|
||||||
reject("No callback was given");
|
reject("No callback was given");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let statusCode: number | undefined = (callbackResult?.[1] as any)?.statusCode;
|
||||||
|
|
||||||
|
if (statusCode && statusCode >= 400) {
|
||||||
|
getMetricFromRegistry<Counter>(metricsRegistry, CommonMetrics.HTTP_CLIENT_ERROR).inc({
|
||||||
|
ErrorCode: statusCode,
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
resolve(callbackResult as Parameters<ServerlessCallback>);
|
resolve(callbackResult as Parameters<ServerlessCallback>);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -66,7 +101,7 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
|
|||||||
console.log(`[CommonHandler] there is ${remainingTime} ms left to send metrics`);
|
console.log(`[CommonHandler] there is ${remainingTime} ms left to send metrics`);
|
||||||
|
|
||||||
let metricsTimeout = setTimeout(() => {
|
let metricsTimeout = setTimeout(() => {
|
||||||
console.log("[CommonHandler] cutting it too close, abandoning metrics");
|
console.error("[CommonHandler] cutting it too close, abandoning metrics");
|
||||||
callback(...result);
|
callback(...result);
|
||||||
}, remainingTime - 250);
|
}, remainingTime - 250);
|
||||||
|
|
||||||
@ -74,6 +109,7 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
|
|||||||
|
|
||||||
console.log("[CommonHandler] attempting to push metrics...");
|
console.log("[CommonHandler] attempting to push metrics...");
|
||||||
try {
|
try {
|
||||||
|
console.log(await getMetricFromRegistry<Summary>(metricsRegistry, CommonMetrics.RUNTIME).get());
|
||||||
await pushGateway.push({
|
await pushGateway.push({
|
||||||
jobName: functionName,
|
jobName: functionName,
|
||||||
groupings: {
|
groupings: {
|
||||||
@ -82,7 +118,7 @@ export function withMetrics<T extends DoormanLambdaContext, U extends BaseEvent>
|
|||||||
});
|
});
|
||||||
console.log("[CommonHandler] pushed metrics successfully");
|
console.log("[CommonHandler] pushed metrics successfully");
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.log("[CommonHandler] failed to push metrics, quietly discarding them", e);
|
console.error("[CommonHandler] failed to push metrics, quietly discarding them", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTimeout(metricsTimeout);
|
clearTimeout(metricsTimeout);
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import VoiceResponse from 'twilio/lib/twiml/VoiceResponse';
|
|||||||
import { DoorStatus } from '../../../doorman-api/src/types/DoorStatus';
|
import { DoorStatus } from '../../../doorman-api/src/types/DoorStatus';
|
||||||
import { StatusResponse } from '../../../doorman-api/src/functions/api/door/status';
|
import { StatusResponse } from '../../../doorman-api/src/functions/api/door/status';
|
||||||
import { InfoResponseClient } from '../../../doorman-api/src/functions/api/door/info';
|
import { InfoResponseClient } from '../../../doorman-api/src/functions/api/door/info';
|
||||||
import { withMetrics } from '../../../doorman-api/src/common/DoormanHandler';
|
import { getMetricFromRegistry, withMetrics } from '../../../doorman-api/src/common/DoormanHandler';
|
||||||
import { Counter, Summary } from 'prom-client';
|
import { Counter, Summary } from 'prom-client';
|
||||||
import { BuzzerActivatedMetrics, registerMetrics } from '../metrics/BuzzerActivatedMetrics';
|
import { BuzzerActivatedMetrics, registerMetrics } from '../metrics/BuzzerActivatedMetrics';
|
||||||
|
|
||||||
@ -43,7 +43,9 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
|
|||||||
if (!config || !config.door) {
|
if (!config || !config.door) {
|
||||||
let twiml = new Twilio.twiml.VoiceResponse();
|
let twiml = new Twilio.twiml.VoiceResponse();
|
||||||
twiml.reject();
|
twiml.reject();
|
||||||
(metricsRegistry.getSingleMetric(BuzzerActivatedMetrics.CALL_REJECTED) as Counter).inc({ From: event.From }, 1);
|
|
||||||
|
getMetricFromRegistry<Counter>(metricsRegistry, BuzzerActivatedMetrics.CALL_REJECTED)
|
||||||
|
.inc({ From: event.From }, 1);
|
||||||
callback(null, twiml);
|
callback(null, twiml);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -62,8 +64,11 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
|
|||||||
|
|
||||||
const unlockPromise = new Promise<VoiceResponse>((resolve, reject) => {
|
const unlockPromise = new Promise<VoiceResponse>((resolve, reject) => {
|
||||||
intervals.push(setInterval(() => {
|
intervals.push(setInterval(() => {
|
||||||
(metricsRegistry.getSingleMetric(BuzzerActivatedMetrics.POLL_ATTEMPTS) as Counter).inc({ door: config.door }, 1);
|
getMetricFromRegistry<Counter>(metricsRegistry, BuzzerActivatedMetrics.POLL_ATTEMPTS)
|
||||||
const recordPollLatency = (metricsRegistry.getSingleMetric(BuzzerActivatedMetrics.POLL_LATENCY) as Summary).startTimer({ door: config.door });
|
.inc({ door: config.door }, 1);
|
||||||
|
|
||||||
|
const recordPollLatency = getMetricFromRegistry<Summary>(metricsRegistry, BuzzerActivatedMetrics.POLL_LATENCY)
|
||||||
|
.startTimer({ door: config.door });
|
||||||
|
|
||||||
fetch(context.DOORMAN_URL + `/api/door/status?door=${config.door}`)
|
fetch(context.DOORMAN_URL + `/api/door/status?door=${config.door}`)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
@ -79,7 +84,8 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
|
|||||||
console.log(
|
console.log(
|
||||||
invokeId, "UnlockPromise: I was the fastest, so I will attempt to notify discord users before resolving with unlock"
|
invokeId, "UnlockPromise: I was the fastest, so I will attempt to notify discord users before resolving with unlock"
|
||||||
);
|
);
|
||||||
(metricsRegistry.getSingleMetric(BuzzerActivatedMetrics.API_UNLOCK) as Counter).inc({ door: config.door }, 1);
|
getMetricFromRegistry<Counter>(metricsRegistry, BuzzerActivatedMetrics.API_UNLOCK)
|
||||||
|
.inc({ door: config.door }, 1);
|
||||||
await notifyAllDiscord(context, config, `🔓 Doorman buzzed someone up @ Door "${config.door}"`, metricsRegistry, JSON.stringify(body.fingerprint));
|
await notifyAllDiscord(context, config, `🔓 Doorman buzzed someone up @ Door "${config.door}"`, metricsRegistry, JSON.stringify(body.fingerprint));
|
||||||
|
|
||||||
resolve(twiml);
|
resolve(twiml);
|
||||||
@ -99,7 +105,8 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
|
|||||||
|
|
||||||
if (!discordLock) {
|
if (!discordLock) {
|
||||||
discordLock = true;
|
discordLock = true;
|
||||||
(metricsRegistry.getSingleMetric(BuzzerActivatedMetrics.DIAL_THROUGH) as Counter).inc({ door: config.door }, 1);
|
getMetricFromRegistry<Counter>(metricsRegistry, BuzzerActivatedMetrics.DIAL_THROUGH)
|
||||||
|
.inc({ door: config.door }, 1);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
invokeId, "GracefulFallbackPromise: I was the fastest, so I will attempt to notify discord users before resolving with a call"
|
invokeId, "GracefulFallbackPromise: I was the fastest, so I will attempt to notify discord users before resolving with a call"
|
||||||
@ -121,8 +128,11 @@ export const handler: ServerlessFunctionSignature<TwilioContext, BuzzerDialEvent
|
|||||||
|
|
||||||
const ungracefulFallbackPromise = new Promise<VoiceResponse>((resolve, reject) => {
|
const ungracefulFallbackPromise = new Promise<VoiceResponse>((resolve, reject) => {
|
||||||
timeouts.push(setTimeout(async () => {
|
timeouts.push(setTimeout(async () => {
|
||||||
(metricsRegistry.getSingleMetric(BuzzerActivatedMetrics.RESULT_NOTIFICATION_FATE_UNKNOWN) as Counter).inc({ door: config.door }, 1);
|
getMetricFromRegistry<Counter>(metricsRegistry, BuzzerActivatedMetrics.RESULT_NOTIFICATION_FATE_UNKNOWN)
|
||||||
(metricsRegistry.getSingleMetric(BuzzerActivatedMetrics.DIAL_THROUGH) as Counter).inc({ door: config.door }, 1);
|
.inc({ door: config.door }, 1);
|
||||||
|
|
||||||
|
getMetricFromRegistry<Counter>(metricsRegistry, BuzzerActivatedMetrics.DIAL_THROUGH)
|
||||||
|
.inc({ door: config.door }, 1);
|
||||||
|
|
||||||
const twiml = dialFallbackTwiml(config);
|
const twiml = dialFallbackTwiml(config);
|
||||||
console.error(
|
console.error(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user