UNPKG

apitally

Version:

Simple API monitoring & analytics for REST APIs built with Express, Fastify, NestJS, AdonisJS, Hono, H3, Elysia, Hapi, and Koa.

203 lines 9.23 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var plugin_exports = {}; __export(plugin_exports, { default: () => apitallyPlugin, setConsumer: () => setConsumer }); module.exports = __toCommonJS(plugin_exports); var import_node_async_hooks = require("node:async_hooks"); var import_node_perf_hooks = require("node:perf_hooks"); var import_client = require("../common/client.js"); var import_consumerRegistry = require("../common/consumerRegistry.js"); var import_requestLogger = require("../common/requestLogger.js"); var import_loggers = require("../loggers/index.js"); var import_utils = require("./utils.js"); const START_TIME_SYMBOL = Symbol("apitally.startTime"); const REQUEST_BODY_SYMBOL = Symbol("apitally.requestBody"); const REQUEST_SIZE_SYMBOL = Symbol("apitally.requestSize"); const RESPONSE_BODY_SYMBOL = Symbol("apitally.responseBody"); const RESPONSE_SIZE_SYMBOL = Symbol("apitally.responseSize"); const LOGS_SYMBOL = Symbol("apitally.logs"); function apitallyPlugin(config) { const client = new import_client.ApitallyClient(config); const logsContext = new import_node_async_hooks.AsyncLocalStorage(); let pinoLoggerPatched = false; if (client.requestLogger.enabled && client.requestLogger.config.captureLogs) { (0, import_loggers.patchConsole)(logsContext); (0, import_loggers.patchWinston)(logsContext); } return { name: "apitally", register: /* @__PURE__ */ __name(async function(server) { server.ext("onPostStart", async () => { if ("logger" in server && client.requestLogger.enabled && client.requestLogger.config.captureLogs) { pinoLoggerPatched = await (0, import_loggers.patchPinoLogger)(server.logger, logsContext); } }); server.ext("onPostStart", () => { const appInfo = (0, import_utils.getAppInfo)(server, config.appVersion); client.setStartupData(appInfo); client.startSync(); }); server.ext("onPreStop", async () => { await client.handleShutdown(); }); server.events.on("request", (_, event) => { if (!pinoLoggerPatched && client.requestLogger.enabled && client.requestLogger.config.captureLogs && event.channel === "app") { (0, import_loggers.handleHapiRequestEvent)(event, logsContext); } }); server.ext("onRequest", async (request, h) => { if (!client.isEnabled() || request.method.toUpperCase() === "OPTIONS") { return h.continue; } request[START_TIME_SYMBOL] = import_node_perf_hooks.performance.now(); const lifecycle = request._lifecycle.bind(request); const logs = []; request[LOGS_SYMBOL] = logs; request._lifecycle = () => logsContext.run(logs, lifecycle); const captureRequestBody = client.requestLogger.enabled && client.requestLogger.config.logRequestBody && client.requestLogger.isSupportedContentType(request.headers["content-type"]); const chunks = []; let size = 0; request.events.on("peek", (chunk, encoding) => { if (captureRequestBody) { chunks.push(Buffer.from(chunk, encoding)); } size += Buffer.byteLength(chunk, encoding); }); request.events.once("finish", () => { if (chunks.length > 0) { request[REQUEST_BODY_SYMBOL] = Buffer.concat(chunks); } request[REQUEST_SIZE_SYMBOL] = size; }); return h.continue; }); server.ext("onPreResponse", async (request, h) => { if (!client.isEnabled() || request.method.toUpperCase() === "OPTIONS" || (0, import_utils.isBoom)(request.response)) { return h.continue; } const captureResponseBody = client.requestLogger.enabled && client.requestLogger.config.logResponseBody && client.requestLogger.isSupportedContentType(request.response.contentType); const chunks = []; let size = 0; request.response.events.on("peek", (chunk, encoding) => { if (captureResponseBody) { chunks.push(Buffer.from(chunk, encoding)); } size += Buffer.byteLength(chunk, encoding); }); request.response.events.once("finish", () => { if (chunks.length > 0) { request[RESPONSE_BODY_SYMBOL] = Buffer.concat(chunks); } request[RESPONSE_SIZE_SYMBOL] = size; }); return h.continue; }); server.ext("onPostResponse", async (request, h) => { var _a, _b, _c; if (!client.isEnabled() || request.method.toUpperCase() === "OPTIONS") { return h.continue; } const startTime = request[START_TIME_SYMBOL]; const responseTime = startTime ? import_node_perf_hooks.performance.now() - startTime : 0; const timestamp = (Date.now() - responseTime) / 1e3; const requestBody = request[REQUEST_BODY_SYMBOL]; const requestSize = request[REQUEST_SIZE_SYMBOL]; const response = request.response; let statusCode; let responseHeaders; let responseBody = request[RESPONSE_BODY_SYMBOL]; let responseSize = request[RESPONSE_SIZE_SYMBOL]; const error = response._error instanceof Error ? response._error : void 0; if ((0, import_utils.isBoom)(response)) { statusCode = ((_a = response.output) == null ? void 0 : _a.statusCode) ?? 500; responseHeaders = ((_b = response.output) == null ? void 0 : _b.headers) ?? {}; } else { statusCode = response.statusCode ?? 200; responseHeaders = response.headers ?? {}; } if (!responseBody && response._payload && client.requestLogger.enabled && client.requestLogger.config.logResponseBody && ((0, import_utils.isBoom)(response) || client.requestLogger.isSupportedContentType(response.contentType))) { const responsePayload = response._payload; if (responsePayload && responsePayload._data && responsePayload._encoding && typeof responsePayload._data === "string" && typeof responsePayload._encoding === "string") { responseBody = Buffer.from(responsePayload._data, responsePayload._encoding); } } if (!responseSize && responseBody) { responseSize = responseBody.length; } const consumer = request.apitallyConsumer ? (0, import_consumerRegistry.consumerFromStringOrObject)(request.apitallyConsumer) : null; client.consumerRegistry.addOrUpdateConsumer(consumer); if (request.route.path) { client.requestCounter.addRequest({ consumer: consumer == null ? void 0 : consumer.identifier, method: request.method, path: request.route.path, statusCode, responseTime, requestSize, responseSize }); if (statusCode === 500 && error) { client.serverErrorCounter.addServerError({ consumer: consumer == null ? void 0 : consumer.identifier, method: request.method, path: request.route.path, type: error.name, msg: error.message, traceback: error.stack || "" }); } } if (client.requestLogger.enabled) { const logs = request[LOGS_SYMBOL]; client.requestLogger.logRequest({ timestamp, consumer: consumer == null ? void 0 : consumer.identifier, method: request.method, path: request.route.path, url: ((_c = request.url) == null ? void 0 : _c.href) || "", headers: (0, import_requestLogger.convertHeaders)(request.headers), size: requestSize, body: requestBody }, { statusCode, responseTime: responseTime / 1e3, headers: (0, import_requestLogger.convertHeaders)(responseHeaders), size: responseSize, body: responseBody }, error, logs); } return h.continue; }); }, "register") }; } __name(apitallyPlugin, "apitallyPlugin"); function setConsumer(request, consumer) { request.apitallyConsumer = consumer || void 0; } __name(setConsumer, "setConsumer"); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { setConsumer }); //# sourceMappingURL=plugin.cjs.map