UNPKG

@talks.converse/js-monitoring

Version:

Express monitoring middleware with Datadog and Prometheus backends, plus conditional logging.

126 lines (107 loc) 4.84 kB
const { loggingRequest } = require('./lib/logging'); /** * Setup monitoring (Datadog + Prometheus) and request logging. * * Usage: * const setupMonitoring = require('js-monitoring'); * setupMonitoring(app, { isEnableDatadog: true, isEnablePrometheus: true, appName: 'myapp' }); * * If options are omitted, values fall back to environment variables: * - ENABLE_DATADOG, ENABLE_PROMETHEUS, PROMETHEUS_METRICS_PATH, APP_NAME * * @param {import('express').Application} app * @param {Object} [opts] * @param {boolean} [opts.isEnableDatadog] * @param {boolean} [opts.isEnablePrometheus] * @param {string} [opts.prometheusPath] * @param {string} [opts.appName] */ /** * Main entry point to set up monitoring and logging on the Express app. */ module.exports = (app, opts = {}) => { const datadog = require('./lib/datadog'); const prometheus = require('./lib/prometheus'); const helper = require('./lib/helper'); const appName = helper.normalizeName(process.env.APP_NAME); const isEnableDatadog = opts.isEnableDatadog ?? (process.env.ENABLE_DATADOG === 'true'); const isEnablePrometheus = opts.isEnablePrometheus ?? (process.env.ENABLE_PROMETHEUS === 'true'); const prometheusPath = opts.prometheusPath ?? (process.env.PROMETHEUS_METRICS_PATH || '/metrics'); const isGroupingStatusCode = opts.isGroupingStatusCode ?? (process.env.GROUPING_STATUS_CODE === 'true'); /** * Register the /metrics endpoint for Prometheus scraping if enabled. */ if (isEnablePrometheus && typeof prometheus.registerMetricsRoute === 'function') { prometheus.registerMetricsRoute(app, prometheusPath); } /** * Middleware that measures request latency and handles tagging for monitoring and logging. */ app.use((req, res, next) => { const start = Date.now(); /** * Runs when a request is completed to record metrics and log data. */ res.on('finish', () => { const latency = Date.now() - start; const method = req.method; const statusCode = res.statusCode; const statusCodeGroup = helper.getStatusGroup(statusCode); const endpointPath = helper.getEndpointPath(req); const body = req.body; const query = req.query; const responseMessage = res.locals.responseMessage || ''; const monitoring_tags = {} /** * Checks if the endpoint is in the default exclude list. */ const isDefaultExcluded = (endpoint) => helper.DEFAULT_EXCLUDES.some((re) => re.test(endpoint || '')); if (isGroupingStatusCode){ monitoring_tags.status_code_group = statusCodeGroup } else { monitoring_tags.status_code = statusCode } /** * Handles sending metrics to Datadog. */ if (isEnableDatadog && typeof datadog.monitor === 'function') { const datadogEndpointRules = datadog.buildDatadogMonitoringEndpointsConfig ? datadog.buildDatadogMonitoringEndpointsConfig() : { include: [], exclude: [] }; const endpointTagValue = helper.isEndpointProcessed(endpointPath, datadogEndpointRules) && !isDefaultExcluded(endpointPath) ? endpointPath : '_others'; monitoring_tags.endpoint = endpointTagValue datadog.monitor(appName, latency, monitoring_tags); } /** * Handles sending metrics to Prometheus. */ if (isEnablePrometheus && typeof prometheus.monitor === 'function') { const prometheusEndpointRules = prometheus.buildPrometheusMonitoringEndpointsConfig ? prometheus.buildPrometheusMonitoringEndpointsConfig() : { include: [], exclude: [] }; const endpointTagValue = helper.isEndpointProcessed(endpointPath, prometheusEndpointRules) && !isDefaultExcluded(endpointPath) ? endpointPath : '_others'; monitoring_tags.endpoint = endpointTagValue prometheus.monitor(appName, latency, monitoring_tags); } /** * Handles logging with its own include/exclude rules. */ loggingRequest({ endpoint: endpointPath, method: method, status_code: statusCode, req_query: query, req_body: body, response: responseMessage, latencyMs: latency, }); }); next(); }); };