@jpg-store/opentelemetry-cloud-monitoring-exporter
Version:
OpenTelemetry Google Cloud Monitoring Exporter allows the user to send collected metrics to Google Cloud Monitoring.
198 lines • 7.89 kB
JavaScript
;
// Copyright 2020 Google LLC
//
// 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
//
// http://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._TEST_ONLY = exports.createTimeSeries = exports.transformMetricDescriptor = void 0;
const sdk_metrics_1 = require("@opentelemetry/sdk-metrics");
const api_1 = require("@opentelemetry/api");
const types_1 = require("./types");
const path = require("path");
const precise_date_1 = require("@google-cloud/precise-date");
/**
*
* @param metric the MetricData to create a descriptor for
* @param metricPrefix prefix to add to metric names
* @param displayNamePrefix prefix to add to display name in the descriptor
* @returns the GCM MetricDescriptor or null if the MetricData was empty
*/
function transformMetricDescriptor(metric, metricPrefix) {
const { descriptor: { name, description, unit }, } = metric;
return {
type: transformMetricType(metricPrefix, name),
description,
displayName: name,
metricKind: transformMetricKind(metric),
valueType: transformValueType(metric),
unit,
labels: transformLabelDescriptors(metric),
};
}
exports.transformMetricDescriptor = transformMetricDescriptor;
function transformLabelDescriptors(metric) {
if (metric.dataPoints.length === 0) {
return [];
}
const attrs = metric.dataPoints[0].attributes;
return Object.keys(attrs).map(key => ({
key: normalizeLabelKey(key),
description: '',
}));
}
/** Transforms Metric type. */
function transformMetricType(metricPrefix, name) {
return path.posix.join(metricPrefix, name);
}
/** Transforms a OpenTelemetry instrument type to a GCM MetricKind. */
function transformMetricKind(metric) {
switch (metric.dataPointType) {
case sdk_metrics_1.DataPointType.SUM:
return metric.isMonotonic ? types_1.MetricKind.CUMULATIVE : types_1.MetricKind.GAUGE;
case sdk_metrics_1.DataPointType.GAUGE:
return types_1.MetricKind.GAUGE;
case sdk_metrics_1.DataPointType.HISTOGRAM:
return types_1.MetricKind.CUMULATIVE;
default:
exhaust(metric);
api_1.diag.info('Encountered unexpected data point type %s', metric.dataPointType);
return types_1.MetricKind.UNSPECIFIED;
}
}
/** Transforms a OpenTelemetry ValueType to a GCM ValueType. */
function transformValueType(metric) {
if (metric.dataPointType === sdk_metrics_1.DataPointType.HISTOGRAM) {
return types_1.ValueType.DISTRIBUTION;
}
const { valueType } = metric.descriptor;
if (valueType === api_1.ValueType.DOUBLE) {
return types_1.ValueType.DOUBLE;
}
else if (valueType === api_1.ValueType.INT) {
return types_1.ValueType.INT64;
}
else {
exhaust(valueType);
api_1.diag.info('Encountered unexpected value type %s', valueType);
return types_1.ValueType.VALUE_TYPE_UNSPECIFIED;
}
}
/**
* Converts metric's timeseries to a TimeSeries, so that metric can be
* uploaded to GCM.
*/
function createTimeSeries(metric, resource, metricPrefix) {
const metricKind = transformMetricKind(metric);
const valueType = transformValueType(metric);
return transformPoints(metric, metricPrefix).map(({ point, metric }) => ({
metric,
resource,
metricKind,
valueType,
points: [point],
}));
}
exports.createTimeSeries = createTimeSeries;
function transformMetric(point, instrumentDescriptor, metricPrefix) {
const type = transformMetricType(metricPrefix, instrumentDescriptor.name);
const labels = {};
Object.keys(point.attributes).forEach(key => {
labels[normalizeLabelKey(key)] = `${point.attributes[key]}`;
});
return { type, labels };
}
/**
* Transform timeseries's point, so that metric can be uploaded to GCM.
*/
function transformPoints(metric, metricPrefix) {
switch (metric.dataPointType) {
case sdk_metrics_1.DataPointType.SUM:
case sdk_metrics_1.DataPointType.GAUGE:
return metric.dataPoints.map(dataPoint => ({
metric: transformMetric(dataPoint, metric.descriptor, metricPrefix),
point: {
value: transformNumberValue(metric.descriptor.valueType, dataPoint.value),
interval: {
// Add start time for non-gauge points
...(metric.dataPointType === sdk_metrics_1.DataPointType.SUM && metric.isMonotonic
? {
startTime: new precise_date_1.PreciseDate(dataPoint.startTime).toISOString(),
}
: null),
endTime: new precise_date_1.PreciseDate(dataPoint.endTime).toISOString(),
},
},
}));
case sdk_metrics_1.DataPointType.HISTOGRAM:
return metric.dataPoints.map(dataPoint => ({
metric: transformMetric(dataPoint, metric.descriptor, metricPrefix),
point: {
value: transformHistogramValue(dataPoint.value),
interval: {
startTime: new precise_date_1.PreciseDate(dataPoint.startTime).toISOString(),
endTime: new precise_date_1.PreciseDate(dataPoint.endTime).toISOString(),
},
},
}));
default:
exhaust(metric);
api_1.diag.info('Encountered unexpected dataPointType=%s, dropping the point', metric.dataPointType);
break;
}
return [];
}
/** Transforms a OpenTelemetry Point's value to a GCM Point value. */
function transformNumberValue(valueType, value) {
if (valueType === api_1.ValueType.INT) {
return { int64Value: value.toString() };
}
else if (valueType === api_1.ValueType.DOUBLE) {
return { doubleValue: value };
}
exhaust(valueType);
throw Error(`unsupported value type: ${valueType}`);
}
function transformHistogramValue(value) {
return {
distributionValue: {
// sumOfSquaredDeviation param not aggregated
count: value.count.toString(),
mean: value.count && value.sum ? value.sum / value.count : 0,
bucketOptions: {
explicitBuckets: { bounds: value.buckets.boundaries },
},
bucketCounts: value.buckets.counts.map(count => count.toString()),
},
};
}
function normalizeLabelKey(key) {
// Replace characters which are not Letter or Decimal_Number unicode category with "_", see
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes
//
// Reimplementation of reference impl in Go:
// https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/blob/e955c204f4f2bfdc92ff0ad52786232b975efcc2/exporter/metric/metric.go#L595-L604
let sanitized = key.replace(/[^\p{Letter}\p{Decimal_Number}_]/gu, '_');
if (sanitized[0].match(/\p{Decimal_Number}/u)) {
sanitized = 'key_' + sanitized;
}
return sanitized;
}
/**
* Assert switch case is exhaustive
*/
function exhaust(switchValue) {
return switchValue;
}
exports._TEST_ONLY = {
normalizeLabelKey,
};
//# sourceMappingURL=transform.js.map