UNPKG

@promster/metrics

Version:

Metrics utilities used by all other server integrations

385 lines (384 loc) 18 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); //#region \0rolldown/runtime.js var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion let es_toolkit_function = require("es-toolkit/function"); let prom_client = require("prom-client"); prom_client = __toESM(prom_client, 1); let merge_options = require("merge-options"); merge_options = __toESM(merge_options, 1); let _chainsafe_prometheus_gc_stats = require("@chainsafe/prometheus-gc-stats"); let node_url = require("node:url"); node_url = __toESM(node_url, 1); let url_value_parser = require("url-value-parser"); url_value_parser = __toESM(url_value_parser, 1); //#region src/environment/kubernetes.ts const isRunningInKubernetes = () => Boolean(process.env.KUBERNETES_SERVICE_HOST); //#endregion //#region src/environment/skip-metrics-in-environment.ts const skipMetricsInEnvironment = (options) => options.detectKubernetes === true && !isRunningInKubernetes(); //#endregion //#region src/client/client.ts const defaultRegister = prom_client.register; const configure = (0, es_toolkit_function.once)((options) => { if (!skipMetricsInEnvironment(options)) prom_client.collectDefaultMetrics(options); }); //#endregion //#region src/create-gc-metrics/create-gc-metrics.ts const asArray$2 = (maybeArray) => Array.isArray(maybeArray) ? maybeArray : [maybeArray]; const defaultOptions$4 = { getLabelValues: () => ({}), labels: [], metricPrefix: "", metricNames: { up: ["nodejs_up"] }, gcCollectionInterval: 6e3 }; const getMetrics$2 = (options) => ({ up: asArray$2(options.metricNames.up).map((nameOfUpMetric) => new prom_client.Gauge({ name: `${options.metricPrefix}${nameOfUpMetric}`, help: "1 = nodejs server is up, 0 = nodejs server is not up" })) }); const createGcMetrics = (options) => { const defaultedOptions = (0, merge_options.default)(defaultOptions$4, options); configure({ prefix: defaultedOptions.metricPrefix }); return getMetrics$2(defaultedOptions); }; createGcMetrics.defaultOptions = defaultOptions$4; //#endregion //#region src/create-gc-observer/create-gc-observer.ts const defaultOptions$3 = { disableGcMetrics: false }; const createGcObserver = (0, es_toolkit_function.once)((_metrics, options) => () => { (0, _chainsafe_prometheus_gc_stats.gcStats)(defaultRegister, { collectionInterval: options.gcCollectionInterval, prefix: options.metricPrefix }); }); createGcObserver.defaultOptions = defaultOptions$3; //#endregion //#region src/create-graphql-metrics/create-graphql-metrics.ts const defaultGraphQlPercentiles = [ .5, .9, .95, .98, .99 ]; const defaultLabels$1 = ["operation_name"]; const asArray$1 = (maybeArray) => Array.isArray(maybeArray) ? maybeArray : [maybeArray]; const shouldObserveGraphQlParseDurationAsHistogram = (options) => options.metricTypes.includes("graphQlParseDurationHistogram"); const shouldObserveGraphQlValidationDurationAsHistogram = (options) => options.metricTypes.includes("graphQlValidationDurationHistogram"); const shouldObserveGraphQlResolveFieldDurationAsHistogram = (options) => options.metricTypes.includes("graphQlResolveFieldDurationHistogram"); const shouldObserveGraphQlRequestDurationAsHistogram = (options) => options.metricTypes.includes("graphQlRequestDurationHistogram"); const shouldObserveGraphQlErrorsTotalAsCounter = (options) => options.metricTypes.includes("graphQlErrorsTotal"); const defaultOptions$2 = { getLabelValues: () => ({}), labels: [], metricPrefix: "", metricTypes: [ "graphQlParseDurationHistogram", "graphQlValidationDurationHistogram", "graphQlResolveFieldDurationHistogram", "graphQlRequestDurationHistogram", "graphQlErrorsTotal" ], metricNames: { graphQlParseDuration: ["graphql_parse_duration_seconds"], graphQlValidationDuration: ["graphql_validation_duration_seconds"], graphQlResolveFieldDuration: ["graphql_resolve_field_duration_seconds"], graphQlRequestDuration: ["graphql_request_duration_seconds"], graphQlErrorsTotal: ["graphql_errors_total"] }, metricPercentiles: { graphQlParseDuration: defaultGraphQlPercentiles, graphQlValidationDuration: defaultGraphQlPercentiles, graphQlResolveFieldDuration: defaultGraphQlPercentiles, graphQlRequestDuration: defaultGraphQlPercentiles, graphQlErrorsTotal: defaultGraphQlPercentiles } }; const getMetrics$1 = (options) => ({ graphQlParseDuration: shouldObserveGraphQlParseDurationAsHistogram(options) ? asArray$1(options.metricNames.graphQlParseDuration).map((nameOfGraphQlParseDuration) => new prom_client.Histogram({ name: `${options.metricPrefix}${nameOfGraphQlParseDuration}`, help: "The GraphQL request parse time in seconds.", labelNames: defaultLabels$1.concat(options.labels).toSorted(), buckets: options.metricPercentiles?.graphQlParseDuration || defaultGraphQlPercentiles })) : void 0, graphQlValidationDuration: shouldObserveGraphQlValidationDurationAsHistogram(options) ? asArray$1(options.metricNames.graphQlValidationDuration).map((nameOfGraphQlValidationDuration) => new prom_client.Histogram({ name: `${options.metricPrefix}${nameOfGraphQlValidationDuration}`, help: "The GraphQL request validation time in seconds.", labelNames: defaultLabels$1.concat(options.labels).toSorted(), buckets: options.metricPercentiles?.graphQlValidationDuration || defaultGraphQlPercentiles })) : void 0, graphQlResolveFieldDuration: shouldObserveGraphQlResolveFieldDurationAsHistogram(options) ? asArray$1(options.metricNames.graphQlResolveFieldDuration).map((nameOfGraphQlResolveFieldDuration) => new prom_client.Histogram({ name: `${options.metricPrefix}${nameOfGraphQlResolveFieldDuration}`, help: "The GraphQL field resolving time in seconds.", labelNames: defaultLabels$1.concat(["field_name"]).concat(options.labels).toSorted(), buckets: options.metricPercentiles?.graphQlResolveFieldDuration || defaultGraphQlPercentiles })) : void 0, graphQlRequestDuration: shouldObserveGraphQlRequestDurationAsHistogram(options) ? asArray$1(options.metricNames.graphQlRequestDuration).map((nameOfGraphQlRequestDuration) => new prom_client.Histogram({ name: `${options.metricPrefix}${nameOfGraphQlRequestDuration}`, help: "The GraphQL request duration time in seconds.", labelNames: defaultLabels$1.concat(options.labels).toSorted(), buckets: options.metricPercentiles?.graphQlRequestDuration || defaultGraphQlPercentiles })) : void 0, graphQlErrorsTotal: shouldObserveGraphQlErrorsTotalAsCounter(options) ? asArray$1(options.metricNames.graphQlErrorsTotal).map((nameOfGraphQlErrorsCount) => new prom_client.Counter({ name: `${options.metricPrefix}${nameOfGraphQlErrorsCount}`, help: "Count of errors while parsing, validating, or executing a GraphQL operation.", labelNames: defaultLabels$1.concat(["phase"]).concat(options.labels).toSorted() })) : void 0 }); const createGraphQlMetrics = (options) => { const defaultedOptions = (0, merge_options.default)(defaultOptions$2, options); configure({ prefix: defaultedOptions.metricPrefix }); return getMetrics$1(defaultedOptions); }; createGraphQlMetrics.defaultOptions = defaultOptions$2; //#endregion //#region src/create-http-metrics/create-http-metrics.ts const defaultHttpRequestDurationPercentileInSeconds = [ .5, .9, .95, .98, .99 ]; const defaultHttpRequestDurationInSeconds = [ .05, .1, .3, .5, .8, 1, 1.5, 2, 3, 10 ]; const defaultHttpContentLengthInBytes = [ 1e5, 2e5, 5e5, 1e6, 15e5, 2e6, 3e6, 5e6, 1e7 ]; const defaultLabels = [ "path", "status_code", "method" ]; const asArray = (maybeArray) => Array.isArray(maybeArray) ? maybeArray : [maybeArray]; const shouldObserveHttpRequestsAsSummary = (options) => options.metricTypes.includes("httpRequestsSummary"); const shouldObserveHttpRequestsAsHistogram = (options) => options.metricTypes.includes("httpRequestsHistogram"); const shouldObserveHttpRequestsAsCounter = (options) => options.metricTypes.includes("httpRequestsTotal"); const shouldObserveHttpContentLengthAsHistogram = (options) => options.metricTypes.includes("httpContentLengthHistogram"); const defaultOptions$1 = { getLabelValues: () => ({}), labels: [], metricPrefix: "", metricTypes: ["httpRequestsTotal", "httpRequestsHistogram"], metricNames: { httpRequestsTotal: ["http_requests_total"], httpRequestDurationPerPercentileInSeconds: ["http_request_duration_per_percentile_seconds"], httpRequestDurationInSeconds: ["http_request_duration_seconds"], httpRequestContentLengthInBytes: ["http_request_content_length_bytes"], httpResponseContentLengthInBytes: ["http_response_content_length_bytes"] }, metricBuckets: { httpRequestContentLengthInBytes: defaultHttpContentLengthInBytes, httpRequestDurationInSeconds: defaultHttpRequestDurationInSeconds }, metricPercentiles: { httpRequestDurationPerPercentileInSeconds: defaultHttpRequestDurationPercentileInSeconds, httpResponseContentLengthInBytes: defaultHttpContentLengthInBytes } }; const getMetrics = (options) => ({ httpRequestContentLengthInBytes: shouldObserveHttpContentLengthAsHistogram(options) ? asArray(options.metricNames.httpRequestContentLengthInBytes).map((nameOfHttpContentLengthMetric) => new prom_client.Histogram({ name: `${options.metricPrefix}${nameOfHttpContentLengthMetric}`, help: "The HTTP request content length in bytes.", labelNames: defaultLabels.concat(options.labels).toSorted(), buckets: options.metricBuckets?.httpRequestContentLengthInBytes || defaultHttpContentLengthInBytes })) : void 0, httpResponseContentLengthInBytes: shouldObserveHttpContentLengthAsHistogram(options) ? asArray(options.metricNames.httpResponseContentLengthInBytes).map((nameOfHttpContentLengthMetric) => new prom_client.Histogram({ name: `${options.metricPrefix}${nameOfHttpContentLengthMetric}`, help: "The HTTP response content length in bytes.", labelNames: defaultLabels.concat(options.labels).toSorted(), buckets: options.metricBuckets?.httpResponseContentLengthInBytes || defaultHttpContentLengthInBytes })) : void 0 }); const getHttpRequestLatencyMetricsInSeconds = (options) => ({ httpRequestDurationPerPercentileInSeconds: shouldObserveHttpRequestsAsSummary(options) ? asArray(options.metricNames.httpRequestDurationPerPercentileInSeconds).map((nameOfHttpRequestDurationPerPercentileInSeconds) => new prom_client.Summary({ name: `${options.metricPrefix}${nameOfHttpRequestDurationPerPercentileInSeconds}`, help: "The HTTP request latencies in seconds.", labelNames: defaultLabels.concat(options.labels).toSorted(), percentiles: options.metricPercentiles?.httpRequestDurationPerPercentileInSeconds || defaultHttpRequestDurationPercentileInSeconds })) : void 0, httpRequestDurationInSeconds: shouldObserveHttpRequestsAsHistogram(options) ? asArray(options.metricNames.httpRequestDurationInSeconds).map((nameOfHttpRequestDurationInSecondsMetric) => new prom_client.Histogram({ name: `${options.metricPrefix}${nameOfHttpRequestDurationInSecondsMetric}`, help: "The HTTP request latencies in seconds.", labelNames: defaultLabels.concat(options.labels).toSorted(), buckets: options.metricBuckets?.httpRequestDurationInSeconds || defaultHttpRequestDurationInSeconds })) : void 0 }); const getHttpRequestCounterMetric = (options) => ({ httpRequestsTotal: shouldObserveHttpRequestsAsCounter(options) ? asArray(options.metricNames.httpRequestsTotal).map((nameOfHttpRequestsTotalMetric) => new prom_client.Counter({ name: `${options.metricPrefix}${nameOfHttpRequestsTotalMetric}`, help: "The total HTTP requests.", labelNames: defaultLabels.concat(options.labels).toSorted() })) : void 0 }); const createHttpMetrics = (options) => { const defaultedOptions = (0, merge_options.default)(defaultOptions$1, options); configure({ prefix: defaultedOptions.metricPrefix }); const metrics = getMetrics(defaultedOptions); const httpRequestLatencyMetricsInSeconds = getHttpRequestLatencyMetricsInSeconds(defaultedOptions); const httpRequestCounterMetric = getHttpRequestCounterMetric(defaultedOptions); return Object.assign({}, metrics, httpRequestLatencyMetricsInSeconds, httpRequestCounterMetric); }; createHttpMetrics.defaultOptions = defaultOptions$1; //#endregion //#region src/end-measurement-from/end-measurement-from.ts const NS_PER_SEC = 1e9; function endMeasurementFrom(start) { const [seconds, nanoseconds] = process.hrtime(start); return { durationS: (seconds * NS_PER_SEC + nanoseconds) / NS_PER_SEC }; } //#endregion //#region src/sort-labels/sort-labels.ts function sortLabels(unsortedLabels) { return Object.keys(unsortedLabels).toSorted((a, b) => { if (a < b) return -1; if (a > b) return 1; return 0; }).reduce((sortedLabels, labelName) => { sortedLabels[labelName] = unsortedLabels[labelName]; return sortedLabels; }, {}); } //#endregion //#region src/create-request-recorder/create-request-recorder.ts const defaultOptions = { detectKubernetes: false }; function isTiming(timing) { return !Array.isArray(timing); } const createRequestRecorder = (metrics, options = defaultOptions) => { const shouldSkipMetricsByEnvironment = skipMetricsInEnvironment((0, merge_options.default)(defaultOptions, options)); return (timing, recordingOptions) => { const durationS = isTiming(timing) ? timing.end().value().seconds : endMeasurementFrom(timing).durationS; const labels = sortLabels(recordingOptions.labels); if (!shouldSkipMetricsByEnvironment && durationS !== void 0) { if (metrics.httpRequestDurationInSeconds) for (const httpRequestDurationInSecondsMetricType of metrics.httpRequestDurationInSeconds) httpRequestDurationInSecondsMetricType.observe(labels, durationS); } if (!shouldSkipMetricsByEnvironment && durationS !== void 0) { if (metrics.httpRequestDurationPerPercentileInSeconds) for (const httpRequestDurationPerPercentileInSecondsMetricType of metrics.httpRequestDurationPerPercentileInSeconds) httpRequestDurationPerPercentileInSecondsMetricType.observe(labels, durationS); } if (!shouldSkipMetricsByEnvironment && durationS !== void 0) { if (metrics.httpRequestsTotal) for (const httpRequestsTotalMetricType of metrics.httpRequestsTotal) httpRequestsTotalMetricType.inc(labels); } if (recordingOptions.requestContentLength) { if (metrics.httpRequestContentLengthInBytes) for (const httpRequestContentLengthInBytesMetricType of metrics.httpRequestContentLengthInBytes) httpRequestContentLengthInBytesMetricType.observe(labels, recordingOptions.requestContentLength); } if (recordingOptions.responseContentLength) { if (metrics.httpResponseContentLengthInBytes) for (const httpResponseContentLengthInBytesMetricType of metrics.httpResponseContentLengthInBytes) httpResponseContentLengthInBytesMetricType.observe(labels, recordingOptions.responseContentLength); } }; }; createRequestRecorder.defaultOptions = defaultOptions; //#endregion //#region src/normalizers/method/method.ts const normalizeMethod = (method) => method.toLowerCase(); //#endregion //#region src/normalizers/path/path.ts const urlValueParser = new url_value_parser.default(); const normalizePath = (path) => { const parsedPathname = node_url.default.parse(path).pathname; if (!parsedPathname) return ""; return urlValueParser.replacePathValues(parsedPathname, "#val"); }; //#endregion //#region src/normalizers/status-code/status-code.ts const normalizeStatusCode = (statusCode) => statusCode; //#endregion //#region src/normalizers/index.ts const defaultNormalizers = { normalizeStatusCode, normalizePath, normalizeMethod }; //#endregion //#region src/summary/summary.ts const getSummary = async () => defaultRegister.metrics(); const getContentType = () => defaultRegister.contentType; //#endregion //#region src/timing/timing.ts var Timing = class Timing { static NS_PER_SEC = BigInt(1e9); #startTime; #endTime; constructor() { this.reset(); } value() { const startTime = this.#startTime; const endTime = this.#endTime; if (!endTime || !startTime) return { seconds: void 0 }; return { seconds: Number(endTime - startTime) / Number(Timing.NS_PER_SEC) }; } reset() { this.#startTime = process.hrtime.bigint(); this.#endTime = void 0; return this; } end() { this.#endTime = process.hrtime.bigint(); return this; } }; const timing = { start() { return new Timing(); } }; //#endregion Object.defineProperty(exports, "Prometheus", { enumerable: true, get: function() { return prom_client; } }); exports.createGcMetrics = createGcMetrics; exports.createGcObserver = createGcObserver; exports.createGraphQlMetrics = createGraphQlMetrics; exports.createHttpMetrics = createHttpMetrics; exports.createRequestRecorder = createRequestRecorder; exports.defaultNormalizers = defaultNormalizers; exports.defaultRegister = defaultRegister; exports.endMeasurementFrom = endMeasurementFrom; exports.getContentType = getContentType; exports.getSummary = getSummary; exports.isRunningInKubernetes = isRunningInKubernetes; exports.normalizeMethod = normalizeMethod; exports.normalizePath = normalizePath; exports.normalizeStatusCode = normalizeStatusCode; exports.skipMetricsInEnvironment = skipMetricsInEnvironment; exports.sortLabels = sortLabels; exports.timing = timing; //# sourceMappingURL=index.cjs.map