init
This commit is contained in:
195
node_modules/@opentelemetry/exporter-prometheus/build/src/PrometheusExporter.js
generated
vendored
Normal file
195
node_modules/@opentelemetry/exporter-prometheus/build/src/PrometheusExporter.js
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
"use strict";
|
||||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.PrometheusExporter = void 0;
|
||||
const api_1 = require("@opentelemetry/api");
|
||||
const core_1 = require("@opentelemetry/core");
|
||||
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
|
||||
const http_1 = require("http");
|
||||
const PrometheusSerializer_1 = require("./PrometheusSerializer");
|
||||
/** Node.js v8.x compat */
|
||||
const url_1 = require("url");
|
||||
class PrometheusExporter extends sdk_metrics_1.MetricReader {
|
||||
static DEFAULT_OPTIONS = {
|
||||
host: undefined,
|
||||
port: 9464,
|
||||
endpoint: '/metrics',
|
||||
prefix: '',
|
||||
appendTimestamp: false,
|
||||
withResourceConstantLabels: undefined,
|
||||
withoutTargetInfo: false,
|
||||
};
|
||||
_host;
|
||||
_port;
|
||||
_baseUrl;
|
||||
_endpoint;
|
||||
_server;
|
||||
_prefix;
|
||||
_appendTimestamp;
|
||||
_serializer;
|
||||
_startServerPromise;
|
||||
// This will be required when histogram is implemented. Leaving here so it is not forgotten
|
||||
// Histogram cannot have a attribute named 'le'
|
||||
// private static readonly RESERVED_HISTOGRAM_LABEL = 'le';
|
||||
/**
|
||||
* Constructor
|
||||
* @param config Exporter configuration
|
||||
* @param callback Callback to be called after a server was started
|
||||
*/
|
||||
constructor(config = {}, callback = () => { }) {
|
||||
super({
|
||||
aggregationSelector: _instrumentType => {
|
||||
return {
|
||||
type: sdk_metrics_1.AggregationType.DEFAULT,
|
||||
};
|
||||
},
|
||||
aggregationTemporalitySelector: _instrumentType => sdk_metrics_1.AggregationTemporality.CUMULATIVE,
|
||||
metricProducers: config.metricProducers,
|
||||
});
|
||||
this._host =
|
||||
config.host ||
|
||||
process.env.OTEL_EXPORTER_PROMETHEUS_HOST ||
|
||||
PrometheusExporter.DEFAULT_OPTIONS.host;
|
||||
this._port =
|
||||
config.port ||
|
||||
Number(process.env.OTEL_EXPORTER_PROMETHEUS_PORT) ||
|
||||
PrometheusExporter.DEFAULT_OPTIONS.port;
|
||||
this._prefix = config.prefix || PrometheusExporter.DEFAULT_OPTIONS.prefix;
|
||||
this._appendTimestamp =
|
||||
typeof config.appendTimestamp === 'boolean'
|
||||
? config.appendTimestamp
|
||||
: PrometheusExporter.DEFAULT_OPTIONS.appendTimestamp;
|
||||
const _withResourceConstantLabels = config.withResourceConstantLabels ||
|
||||
PrometheusExporter.DEFAULT_OPTIONS.withResourceConstantLabels;
|
||||
const _withoutTargetInfo = config.withoutTargetInfo ||
|
||||
PrometheusExporter.DEFAULT_OPTIONS.withoutTargetInfo;
|
||||
// unref to prevent prometheus exporter from holding the process open on exit
|
||||
this._server = (0, http_1.createServer)(this._requestHandler).unref();
|
||||
this._serializer = new PrometheusSerializer_1.PrometheusSerializer(this._prefix, this._appendTimestamp, _withResourceConstantLabels, _withoutTargetInfo);
|
||||
this._baseUrl = `http://${this._host}:${this._port}/`;
|
||||
this._endpoint = (config.endpoint || PrometheusExporter.DEFAULT_OPTIONS.endpoint).replace(/^([^/])/, '/$1');
|
||||
if (config.preventServerStart !== true) {
|
||||
this.startServer().then(callback, err => {
|
||||
api_1.diag.error(err);
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
else if (callback) {
|
||||
// Do not invoke callback immediately to avoid zalgo problem.
|
||||
queueMicrotask(callback);
|
||||
}
|
||||
}
|
||||
async onForceFlush() {
|
||||
/** do nothing */
|
||||
}
|
||||
/**
|
||||
* Shuts down the export server and clears the registry
|
||||
*/
|
||||
onShutdown() {
|
||||
return this.stopServer();
|
||||
}
|
||||
/**
|
||||
* Stops the Prometheus export server
|
||||
*/
|
||||
stopServer() {
|
||||
if (!this._server) {
|
||||
api_1.diag.debug('Prometheus stopServer() was called but server was never started.');
|
||||
return Promise.resolve();
|
||||
}
|
||||
else {
|
||||
return new Promise(resolve => {
|
||||
this._server.close(err => {
|
||||
if (!err) {
|
||||
api_1.diag.debug('Prometheus exporter was stopped');
|
||||
}
|
||||
else {
|
||||
if (err.code !==
|
||||
'ERR_SERVER_NOT_RUNNING') {
|
||||
(0, core_1.globalErrorHandler)(err);
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Starts the Prometheus export server
|
||||
*/
|
||||
startServer() {
|
||||
this._startServerPromise ??= new Promise((resolve, reject) => {
|
||||
this._server.once('error', reject);
|
||||
this._server.listen({
|
||||
port: this._port,
|
||||
host: this._host,
|
||||
}, () => {
|
||||
api_1.diag.debug(`Prometheus exporter server started: ${this._host}:${this._port}/${this._endpoint}`);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
return this._startServerPromise;
|
||||
}
|
||||
/**
|
||||
* Request handler that responds with the current state of metrics
|
||||
* @param _request Incoming HTTP request of server instance
|
||||
* @param response HTTP response object used to response to request
|
||||
*/
|
||||
getMetricsRequestHandler(_request, response) {
|
||||
this._exportMetrics(response);
|
||||
}
|
||||
/**
|
||||
* Request handler used by http library to respond to incoming requests
|
||||
* for the current state of metrics by the Prometheus backend.
|
||||
*
|
||||
* @param request Incoming HTTP request to export server
|
||||
* @param response HTTP response object used to respond to request
|
||||
*/
|
||||
_requestHandler = (request, response) => {
|
||||
if (request.url != null &&
|
||||
new url_1.URL(request.url, this._baseUrl).pathname === this._endpoint) {
|
||||
this._exportMetrics(response);
|
||||
}
|
||||
else {
|
||||
this._notFound(response);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Responds to incoming message with current state of all metrics.
|
||||
*/
|
||||
_exportMetrics = (response) => {
|
||||
response.statusCode = 200;
|
||||
response.setHeader('content-type', 'text/plain');
|
||||
this.collect().then(collectionResult => {
|
||||
const { resourceMetrics, errors } = collectionResult;
|
||||
if (errors.length) {
|
||||
api_1.diag.error('PrometheusExporter: metrics collection errors', ...errors);
|
||||
}
|
||||
response.end(this._serializer.serialize(resourceMetrics));
|
||||
}, err => {
|
||||
response.end(`# failed to export metrics: ${err}`);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Responds with 404 status code to all requests that do not match the configured endpoint.
|
||||
*/
|
||||
_notFound = (response) => {
|
||||
response.statusCode = 404;
|
||||
response.end();
|
||||
};
|
||||
}
|
||||
exports.PrometheusExporter = PrometheusExporter;
|
||||
//# sourceMappingURL=PrometheusExporter.js.map
|
||||
264
node_modules/@opentelemetry/exporter-prometheus/build/src/PrometheusSerializer.js
generated
vendored
Normal file
264
node_modules/@opentelemetry/exporter-prometheus/build/src/PrometheusSerializer.js
generated
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
"use strict";
|
||||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.PrometheusSerializer = void 0;
|
||||
const api_1 = require("@opentelemetry/api");
|
||||
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
|
||||
const core_1 = require("@opentelemetry/core");
|
||||
function escapeString(str) {
|
||||
return str.replace(/\\/g, '\\\\').replace(/\n/g, '\\n');
|
||||
}
|
||||
/**
|
||||
* String Attribute values are converted directly to Prometheus attribute values.
|
||||
* Non-string values are represented as JSON-encoded strings.
|
||||
*
|
||||
* `undefined` is converted to an empty string.
|
||||
*/
|
||||
function escapeAttributeValue(str = '') {
|
||||
if (typeof str !== 'string') {
|
||||
str = JSON.stringify(str);
|
||||
}
|
||||
return escapeString(str).replace(/"/g, '\\"');
|
||||
}
|
||||
const invalidCharacterRegex = /[^a-z0-9_]/gi;
|
||||
const multipleUnderscoreRegex = /_{2,}/g;
|
||||
/**
|
||||
* Ensures metric names are valid Prometheus metric names by removing
|
||||
* characters allowed by OpenTelemetry but disallowed by Prometheus.
|
||||
*
|
||||
* https://prometheus.io/docs/concepts/data_model/#metric-names-and-attributes
|
||||
*
|
||||
* 1. Names must match `[a-zA-Z_:][a-zA-Z0-9_:]*`
|
||||
*
|
||||
* 2. Colons are reserved for user defined recording rules.
|
||||
* They should not be used by exporters or direct instrumentation.
|
||||
*
|
||||
* OpenTelemetry metric names are already validated in the Meter when they are created,
|
||||
* and they match the format `[a-zA-Z][a-zA-Z0-9_.\-]*` which is very close to a valid
|
||||
* prometheus metric name, so we only need to strip characters valid in OpenTelemetry
|
||||
* but not valid in prometheus and replace them with '_'.
|
||||
*
|
||||
* @param name name to be sanitized
|
||||
*/
|
||||
function sanitizePrometheusMetricName(name) {
|
||||
// replace all invalid characters with '_'
|
||||
return name
|
||||
.replace(invalidCharacterRegex, '_')
|
||||
.replace(multipleUnderscoreRegex, '_');
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* Helper method which assists in enforcing the naming conventions for metric
|
||||
* names in Prometheus
|
||||
* @param name the name of the metric
|
||||
* @param type the kind of metric
|
||||
* @returns string
|
||||
*/
|
||||
function enforcePrometheusNamingConvention(name, data) {
|
||||
// Prometheus requires that metrics of the Counter kind have "_total" suffix
|
||||
if (!name.endsWith('_total') &&
|
||||
data.dataPointType === sdk_metrics_1.DataPointType.SUM &&
|
||||
data.isMonotonic) {
|
||||
name = name + '_total';
|
||||
}
|
||||
return name;
|
||||
}
|
||||
function valueString(value) {
|
||||
if (value === Infinity) {
|
||||
return '+Inf';
|
||||
}
|
||||
else if (value === -Infinity) {
|
||||
return '-Inf';
|
||||
}
|
||||
else {
|
||||
// Handle finite numbers and NaN.
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
||||
function toPrometheusType(metricData) {
|
||||
switch (metricData.dataPointType) {
|
||||
case sdk_metrics_1.DataPointType.SUM:
|
||||
if (metricData.isMonotonic) {
|
||||
return 'counter';
|
||||
}
|
||||
return 'gauge';
|
||||
case sdk_metrics_1.DataPointType.GAUGE:
|
||||
return 'gauge';
|
||||
case sdk_metrics_1.DataPointType.HISTOGRAM:
|
||||
return 'histogram';
|
||||
default:
|
||||
return 'untyped';
|
||||
}
|
||||
}
|
||||
function stringify(metricName, attributes, value, timestamp, additionalAttributes) {
|
||||
let hasAttribute = false;
|
||||
let attributesStr = '';
|
||||
for (const [key, val] of Object.entries(attributes)) {
|
||||
const sanitizedAttributeName = sanitizePrometheusMetricName(key);
|
||||
hasAttribute = true;
|
||||
attributesStr += `${attributesStr.length > 0 ? ',' : ''}${sanitizedAttributeName}="${escapeAttributeValue(val)}"`;
|
||||
}
|
||||
if (additionalAttributes) {
|
||||
for (const [key, val] of Object.entries(additionalAttributes)) {
|
||||
const sanitizedAttributeName = sanitizePrometheusMetricName(key);
|
||||
hasAttribute = true;
|
||||
attributesStr += `${attributesStr.length > 0 ? ',' : ''}${sanitizedAttributeName}="${escapeAttributeValue(val)}"`;
|
||||
}
|
||||
}
|
||||
if (hasAttribute) {
|
||||
metricName += `{${attributesStr}}`;
|
||||
}
|
||||
return `${metricName} ${valueString(value)}${timestamp !== undefined ? ' ' + String(timestamp) : ''}\n`;
|
||||
}
|
||||
const NO_REGISTERED_METRICS = '# no registered metrics';
|
||||
class PrometheusSerializer {
|
||||
_prefix;
|
||||
_appendTimestamp;
|
||||
_additionalAttributes;
|
||||
_withResourceConstantLabels;
|
||||
_withoutTargetInfo;
|
||||
constructor(prefix, appendTimestamp = false, withResourceConstantLabels, withoutTargetInfo) {
|
||||
if (prefix) {
|
||||
this._prefix = prefix + '_';
|
||||
}
|
||||
this._appendTimestamp = appendTimestamp;
|
||||
this._withResourceConstantLabels = withResourceConstantLabels;
|
||||
this._withoutTargetInfo = !!withoutTargetInfo;
|
||||
}
|
||||
serialize(resourceMetrics) {
|
||||
let str = '';
|
||||
this._additionalAttributes = this._filterResourceConstantLabels(resourceMetrics.resource.attributes, this._withResourceConstantLabels);
|
||||
for (const scopeMetrics of resourceMetrics.scopeMetrics) {
|
||||
str += this._serializeScopeMetrics(scopeMetrics);
|
||||
}
|
||||
if (str === '') {
|
||||
str += NO_REGISTERED_METRICS;
|
||||
}
|
||||
return this._serializeResource(resourceMetrics.resource) + str;
|
||||
}
|
||||
_filterResourceConstantLabels(attributes, pattern) {
|
||||
if (pattern) {
|
||||
const filteredAttributes = {};
|
||||
for (const [key, value] of Object.entries(attributes)) {
|
||||
if (key.match(pattern)) {
|
||||
filteredAttributes[key] = value;
|
||||
}
|
||||
}
|
||||
return filteredAttributes;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_serializeScopeMetrics(scopeMetrics) {
|
||||
let str = '';
|
||||
for (const metric of scopeMetrics.metrics) {
|
||||
str += this._serializeMetricData(metric) + '\n';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
_serializeMetricData(metricData) {
|
||||
let name = sanitizePrometheusMetricName(escapeString(metricData.descriptor.name));
|
||||
if (this._prefix) {
|
||||
name = `${this._prefix}${name}`;
|
||||
}
|
||||
const dataPointType = metricData.dataPointType;
|
||||
name = enforcePrometheusNamingConvention(name, metricData);
|
||||
const help = `# HELP ${name} ${escapeString(metricData.descriptor.description || 'description missing')}`;
|
||||
const unit = metricData.descriptor.unit
|
||||
? `\n# UNIT ${name} ${escapeString(metricData.descriptor.unit)}`
|
||||
: '';
|
||||
const type = `# TYPE ${name} ${toPrometheusType(metricData)}`;
|
||||
let results = '';
|
||||
switch (dataPointType) {
|
||||
case sdk_metrics_1.DataPointType.SUM:
|
||||
case sdk_metrics_1.DataPointType.GAUGE: {
|
||||
results = metricData.dataPoints
|
||||
.map(it => this._serializeSingularDataPoint(name, metricData, it))
|
||||
.join('');
|
||||
break;
|
||||
}
|
||||
case sdk_metrics_1.DataPointType.HISTOGRAM: {
|
||||
results = metricData.dataPoints
|
||||
.map(it => this._serializeHistogramDataPoint(name, metricData, it))
|
||||
.join('');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
api_1.diag.error(`Unrecognizable DataPointType: ${dataPointType} for metric "${name}"`);
|
||||
}
|
||||
}
|
||||
return `${help}${unit}\n${type}\n${results}`.trim();
|
||||
}
|
||||
_serializeSingularDataPoint(name, data, dataPoint) {
|
||||
let results = '';
|
||||
name = enforcePrometheusNamingConvention(name, data);
|
||||
const { value, attributes } = dataPoint;
|
||||
const timestamp = (0, core_1.hrTimeToMilliseconds)(dataPoint.endTime);
|
||||
results += stringify(name, attributes, value, this._appendTimestamp ? timestamp : undefined, this._additionalAttributes);
|
||||
return results;
|
||||
}
|
||||
_serializeHistogramDataPoint(name, data, dataPoint) {
|
||||
let results = '';
|
||||
name = enforcePrometheusNamingConvention(name, data);
|
||||
const attributes = dataPoint.attributes;
|
||||
const histogram = dataPoint.value;
|
||||
const timestamp = (0, core_1.hrTimeToMilliseconds)(dataPoint.endTime);
|
||||
/** Histogram["bucket"] is not typed with `number` */
|
||||
for (const key of ['count', 'sum']) {
|
||||
const value = histogram[key];
|
||||
if (value != null)
|
||||
results += stringify(name + '_' + key, attributes, value, this._appendTimestamp ? timestamp : undefined, this._additionalAttributes);
|
||||
}
|
||||
let cumulativeSum = 0;
|
||||
const countEntries = histogram.buckets.counts.entries();
|
||||
let infiniteBoundaryDefined = false;
|
||||
for (const [idx, val] of countEntries) {
|
||||
cumulativeSum += val;
|
||||
const upperBound = histogram.buckets.boundaries[idx];
|
||||
/** HistogramAggregator is producing different boundary output -
|
||||
* in one case not including infinity values, in other -
|
||||
* full, e.g. [0, 100] and [0, 100, Infinity]
|
||||
* we should consider that in export, if Infinity is defined, use it
|
||||
* as boundary
|
||||
*/
|
||||
if (upperBound === undefined && infiniteBoundaryDefined) {
|
||||
break;
|
||||
}
|
||||
if (upperBound === Infinity) {
|
||||
infiniteBoundaryDefined = true;
|
||||
}
|
||||
results += stringify(name + '_bucket', attributes, cumulativeSum, this._appendTimestamp ? timestamp : undefined, Object.assign({}, this._additionalAttributes ?? {}, {
|
||||
le: upperBound === undefined || upperBound === Infinity
|
||||
? '+Inf'
|
||||
: String(upperBound),
|
||||
}));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
_serializeResource(resource) {
|
||||
if (this._withoutTargetInfo === true) {
|
||||
return '';
|
||||
}
|
||||
const name = 'target_info';
|
||||
const help = `# HELP ${name} Target metadata`;
|
||||
const type = `# TYPE ${name} gauge`;
|
||||
const results = stringify(name, resource.attributes, 1).trim();
|
||||
return `${help}\n${type}\n${results}\n`;
|
||||
}
|
||||
}
|
||||
exports.PrometheusSerializer = PrometheusSerializer;
|
||||
//# sourceMappingURL=PrometheusSerializer.js.map
|
||||
23
node_modules/@opentelemetry/exporter-prometheus/build/src/index.js
generated
vendored
Normal file
23
node_modules/@opentelemetry/exporter-prometheus/build/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.PrometheusSerializer = exports.PrometheusExporter = void 0;
|
||||
var PrometheusExporter_1 = require("./PrometheusExporter");
|
||||
Object.defineProperty(exports, "PrometheusExporter", { enumerable: true, get: function () { return PrometheusExporter_1.PrometheusExporter; } });
|
||||
var PrometheusSerializer_1 = require("./PrometheusSerializer");
|
||||
Object.defineProperty(exports, "PrometheusSerializer", { enumerable: true, get: function () { return PrometheusSerializer_1.PrometheusSerializer; } });
|
||||
//# sourceMappingURL=index.js.map
|
||||
Reference in New Issue
Block a user