From 80f45cce0eaeade4e8b20635e7fe6a5dc8b2f401 Mon Sep 17 00:00:00 2001 From: Martin Dimitrov Date: Sun, 9 Feb 2025 19:55:17 -0800 Subject: [PATCH] log to loki --- bun.lockb | Bin 413960 -> 435256 bytes packages/doorman-api/.env.example | 7 +- packages/doorman-api/package.json | 4 +- .../doorman-api/src/common/DoormanHandler.ts | 108 ++++++++++++++++-- .../src/common/DoormanHandlerContext.ts | 3 + packages/doorman-client/.env.example | 7 +- packages/doorman-client/package.json | 4 +- 7 files changed, 117 insertions(+), 16 deletions(-) diff --git a/bun.lockb b/bun.lockb index ec7491bed9eeda5730b166a5af102336dc35c861..79d6fb36059a6f384bdf8eade0a0977251ef314e 100755 GIT binary patch delta 35520 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 Ja)7%5(8ToE07!ftuv#F+_;LZ81Tt~~ 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 = ( @@ -12,12 +15,15 @@ export type DoormanLambda = event: Parameters>[1], callback: Parameters>[2], metricsRegistry: Registry, + logger: Logger, ) => void; export enum CommonMetrics { RUNTIME = "FunctionRuntime", BLOCKED_REQUEST = "BlockedRequest", HTTP_CLIENT_ERROR = "HttpClientError", + LOKI_ERROR = "LokiError", + LOKI_LOG_AFTER_CLOSE = "LokiLogAfterClose", }; export function getMetricFromRegistry(metricsRegistry: Registry, metric: string): T { @@ -34,6 +40,7 @@ export function withMetrics handler: DoormanLambda ): ServerlessFunctionSignature { return async (context, event, callback) => { + console.log("[CommonHandler] created loki logger"); console.log("[CommonHandler] clearing old metrics"); register.clear(); @@ -63,6 +70,80 @@ export function withMetrics labelNames: [ "ErrorCode" ], })); + metricsRegistry.registerMetric(new Counter({ + name: CommonMetrics.LOKI_ERROR, + help: "Error connecting to loki for logs", + })); + + metricsRegistry.registerMetric(new Counter({ + name: CommonMetrics.LOKI_LOG_AFTER_CLOSE, + help: "Tried to write logs after log stream closed", + })); + + // Create a Winston logger + const logger = createLogger({ + level: 'info', + format: format.json(), + transports: [ + new LokiTransport({ + host: context.LOKI_URL, + labels: { + service: "doorman", + stage: context.STAGE, + handler: functionName, + }, + basicAuth: `${context.LOKI_USER}:${context.LOKI_PW}`, + interval: 500, + gracefulShutdown: true, + clearOnError: true, + batching: true, + onConnectionError: (error) => { + console.log("in error block"); + console.error(error); + getMetricFromRegistry(metricsRegistry, CommonMetrics.LOKI_ERROR).inc(1); + }, + }), + new transports.Console(), // Add other transports if needed + ], + }); + + const orignalLog = { + log: console.log, + error: console.error, + warn: console.warn, + info: console.info, + } + + // Override the base console log with winston + console.log = function (...args) { + if (logger.writable) { + return logger.info.apply(logger, [...args]); + } + getMetricFromRegistry(metricsRegistry, CommonMetrics.LOKI_LOG_AFTER_CLOSE).inc(1); + return orignalLog.log(...args); + }; + console.error = function (...args) { + if (logger.writable) { + return logger.error.apply(logger, [...args]); + } + getMetricFromRegistry(metricsRegistry, CommonMetrics.LOKI_LOG_AFTER_CLOSE).inc(1); + return orignalLog.error(...args); + }; + console.warn = function (...args) { + if (logger.writable) { + return logger.warn.apply(logger, [...args]); + } + getMetricFromRegistry(metricsRegistry, CommonMetrics.LOKI_LOG_AFTER_CLOSE).inc(1); + return orignalLog.warn(...args); + }; + console.info = function (...args) { + if (logger.writable) { + return logger.info.apply(logger, [...args]); + } + getMetricFromRegistry(metricsRegistry, CommonMetrics.LOKI_LOG_AFTER_CLOSE).inc(1); + return orignalLog.info(...args); + }; + const summaryTimer = getMetricFromRegistry(metricsRegistry, CommonMetrics.RUNTIME).startTimer(); const startTime = Date.now(); @@ -82,7 +163,7 @@ export function withMetrics response.setStatusCode(200); callbackResult = [null, response]; } else { - await handler(context, event, tempCallback, metricsRegistry); + await handler(context, event, tempCallback, metricsRegistry, logger); } if (!callbackResult) { @@ -112,26 +193,29 @@ export function withMetrics console.log(`[CommonHandler] there is ${remainingTime} ms left to send metrics`); let metricsTimeout = setTimeout(() => { - console.log("[CommonHandler] cutting it too close, abandoning metrics"); callback(...result); }, remainingTime - 250); summaryTimer(); - console.log("[CommonHandler] attempting to push metrics..."); + try { - await pushGateway.push({ - jobName: functionName, - groupings: { - stage: context.STAGE, - }, + logger.end(async () => { + await pushGateway.push({ + jobName: functionName, + groupings: { + stage: context.STAGE, + }, + }); + console.log("[CommonHandler] pushed metrics successfully"); + clearTimeout(metricsTimeout); + callback(...result); }); - console.log("[CommonHandler] pushed metrics successfully"); + } catch (e: any) { console.log("[CommonHandler] failed to push metrics, quietly discarding them", e); + clearTimeout(metricsTimeout); + callback(...result); } - - clearTimeout(metricsTimeout); - callback(...result); }; }; diff --git a/packages/doorman-api/src/common/DoormanHandlerContext.ts b/packages/doorman-api/src/common/DoormanHandlerContext.ts index 259bfdb..2d0588b 100644 --- a/packages/doorman-api/src/common/DoormanHandlerContext.ts +++ b/packages/doorman-api/src/common/DoormanHandlerContext.ts @@ -10,4 +10,7 @@ export interface DoormanLambdaContext extends EnvironmentVariables { PUSHGATEWAY_USER: string; PUSHGATEWAY_PW: string; STAGE: string; + LOKI_URL: string; + LOKI_USER: string; + LOKI_PW: string; }; diff --git a/packages/doorman-client/.env.example b/packages/doorman-client/.env.example index 3d98815..10a3eae 100644 --- a/packages/doorman-client/.env.example +++ b/packages/doorman-client/.env.example @@ -8,4 +8,9 @@ AUTH_TOKEN= PUSHGATEWAY_URL=https://metrics.chromart.cc STAGE=prod PUSHGATEWAY_USER=doorman -PUSHGATEWAY_PW=doormanmetrics \ No newline at end of file +PUSHGATEWAY_PW=doormanmetrics + +# logs +LOKI_URL=https://logs.chromart.cc +LOKI_USER=doorman +LOKI_PW=doormanlogs diff --git a/packages/doorman-client/package.json b/packages/doorman-client/package.json index 850016d..6e4f891 100644 --- a/packages/doorman-client/package.json +++ b/packages/doorman-client/package.json @@ -11,12 +11,14 @@ "deploy": "twilio-run deploy --load-system-env --env .env.example --service-name buzzer --environment=prod --override-existing-project" }, "dependencies": { + "@twilio-labs/serverless-runtime-types": "^3.0.0", "@twilio/runtime-handler": "1.3.0", "node-fetch": "^2.7.0", "prom-client": "^15.1.3", "prometheus-remote-write": "^0.5.0", "twilio": "^3.84.1", - "@twilio-labs/serverless-runtime-types": "^3.0.0" + "winston": "^3.17.0", + "winston-loki": "^6.1.3" }, "devDependencies": { "@types/bun": "latest",