"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_base_1 = require("@opentelemetry/sdk-metrics-base"); const http_1 = require("http"); const PrometheusSerializer_1 = require("./PrometheusSerializer"); /** Node.js v8.x compat */ const url_1 = require("url"); const NO_REGISTERED_METRICS = '# no registered metrics'; class PrometheusExporter extends sdk_metrics_base_1.MetricReader { // 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(); /** * 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 */ this._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. */ this._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); } let result = this._serializer.serialize(resourceMetrics); if (result === '') { result = NO_REGISTERED_METRICS; } response.end(result); }, err => { response.end(`# failed to export metrics: ${err}`); }); }; /** * Responds with 404 status code to all requests that do not match the configured endpoint. */ this._notFound = (response) => { response.statusCode = 404; response.end(); }; 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; // 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); 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) .catch(err => api_1.diag.error(err)); } else if (callback) { callback(); } } selectAggregationTemporality() { return sdk_metrics_base_1.AggregationTemporality.CUMULATIVE; } 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() { return new Promise(resolve => { this._server.listen({ port: this._port, host: this._host, }, () => { api_1.diag.debug(`Prometheus exporter server started: ${this._host}:${this._port}/${this._endpoint}`); resolve(); }); }); } /** * Request handler that responds with the current state of metrics * @param request Incoming HTTP request of server instance * @param response HTTP response objet used to response to request */ getMetricsRequestHandler(_request, response) { this._exportMetrics(response); } } exports.PrometheusExporter = PrometheusExporter; PrometheusExporter.DEFAULT_OPTIONS = { host: undefined, port: 9464, endpoint: '/metrics', prefix: '', appendTimestamp: true, }; //# sourceMappingURL=PrometheusExporter.js.map