@promster/metrics
Version:
Metrics utilities used by all other server integrations
385 lines (384 loc) • 18 kB
JavaScript
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