Skip to content

Commit

Permalink
Merge b28d706 into 51b3c59
Browse files Browse the repository at this point in the history
  • Loading branch information
aabmass authored Nov 29, 2022
2 parents 51b3c59 + b28d706 commit b347659
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 68 deletions.
76 changes: 36 additions & 40 deletions packages/opentelemetry-cloud-monitoring-exporter/src/monitoring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import {
PushMetricExporter,
ResourceMetrics,
InstrumentDescriptor,
MetricData,
} from '@opentelemetry/sdk-metrics';
import {
ExportResult,
Expand Down Expand Up @@ -63,14 +63,16 @@ export class MetricExporter implements PushMetricExporter {
private readonly _metricPrefix: string;
private readonly _displayNamePrefix: string;
private readonly _auth: GoogleAuth;
private readonly _startTime = new Date().toISOString();

static readonly DEFAULT_DISPLAY_NAME_PREFIX: string = 'OpenTelemetry';
static readonly CUSTOM_OPENTELEMETRY_DOMAIN: string =
'custom.googleapis.com/opentelemetry';

private registeredInstrumentDescriptors: Map<string, InstrumentDescriptor> =
new Map();
/**
* Set of OTel metric names that have already had their metric descriptors successfully
* created
*/
private createdMetricDescriptors: Set<string> = new Set();

private _monitoring: monitoring_v3.Monitoring;

Expand Down Expand Up @@ -150,9 +152,7 @@ export class MetricExporter implements PushMetricExporter {
const timeSeries: TimeSeries[] = [];
for (const scopeMetric of resourceMetrics.scopeMetrics) {
for (const metric of scopeMetric.metrics) {
const isRegistered = await this._registerMetricDescriptor(
metric.descriptor
);
const isRegistered = await this._registerMetricDescriptor(metric);
if (isRegistered) {
timeSeries.push(
...createTimeSeries(metric, resource, this._metricPrefix)
Expand Down Expand Up @@ -186,65 +186,61 @@ export class MetricExporter implements PushMetricExporter {
/**
* Returns true if the given metricDescriptor is successfully registered to
* Google Cloud Monitoring, or the exact same metric has already been
* registered. Returns false otherwise.
* @param instrumentDescriptor The OpenTelemetry MetricDescriptor.
* registered. Returns false otherwise and should be skipped.
*
* @param metric The OpenTelemetry MetricData.
*/
private async _registerMetricDescriptor(
instrumentDescriptor: InstrumentDescriptor
) {
const existingInstrumentDescriptor =
this.registeredInstrumentDescriptors.get(instrumentDescriptor.name);
metric: MetricData
): Promise<boolean> {
const isDescriptorCreated = this.createdMetricDescriptors.has(
metric.descriptor.name
);

if (existingInstrumentDescriptor) {
if (existingInstrumentDescriptor === instrumentDescriptor) {
// Ignore descriptors that are already registered.
return true;
} else {
diag.warn(
'A different metric with the same name is already registered: %s',
existingInstrumentDescriptor
);
return false;
}
if (isDescriptorCreated) {
return true;
}

try {
await this._createMetricDescriptor(instrumentDescriptor);
this.registeredInstrumentDescriptors.set(
instrumentDescriptor.name,
instrumentDescriptor
);
const res = await this._createMetricDescriptor(metric);
if (res) {
this.createdMetricDescriptors.add(metric.descriptor.name);
return true;
} catch (e) {
const err = asError(e);
diag.error('Error creating metric descriptor: %s', err.message);
return false;
}
return false;
}

/**
* Calls CreateMetricDescriptor in the GCM API for the given InstrumentDescriptor
* @param instrumentDescriptor The OpenTelemetry InstrumentDescriptor.
* @param metric The OpenTelemetry MetricData.
* @returns whether or not the descriptor was successfully created
*/
private async _createMetricDescriptor(
instrumentDescriptor: InstrumentDescriptor
) {
private async _createMetricDescriptor(metric: MetricData): Promise<boolean> {
const authClient = await this._authorize();
const descriptor = transformMetricDescriptor(
instrumentDescriptor,
metric,
this._metricPrefix,
this._displayNamePrefix
);
if (!descriptor) {
diag.info(
'MetricData with name "%s" contained no points, skipping.',
metric.descriptor.name
);
return false;
}

try {
await this._monitoring.projects.metricDescriptors.create({
const res = await this._monitoring.projects.metricDescriptors.create({
name: `projects/${this._projectId}`,
requestBody: descriptor,
auth: authClient,
});
diag.debug('sent metric descriptor', descriptor);
return true;
} catch (e) {
const err = asError(e);
diag.error('Failed to create metric descriptor: %s', err.message);
return false;
}
}

Expand Down
72 changes: 44 additions & 28 deletions packages/opentelemetry-cloud-monitoring-exporter/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import {
InstrumentDescriptor,
InstrumentType,
Histogram,
MetricData,
DataPoint,
Expand All @@ -39,21 +38,34 @@ const OPENTELEMETRY_TASK = 'opentelemetry_task';
const OPENTELEMETRY_TASK_DESCRIPTION = 'OpenTelemetry task identifier';
export const OPENTELEMETRY_TASK_VALUE_DEFAULT = generateDefaultTaskValue();

/**
*
* @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
*/
export function transformMetricDescriptor(
instrumentDescriptor: InstrumentDescriptor,
metric: MetricData,
metricPrefix: string,
displayNamePrefix: string
): MetricDescriptor {
): MetricDescriptor | null {
// If no data points
if (!metric.dataPoints[0]) {
return null;
}

const {
descriptor: {name, description, unit},
} = metric;

return {
type: transformMetricType(metricPrefix, instrumentDescriptor.name),
description: instrumentDescriptor.description,
displayName: transformDisplayName(
displayNamePrefix,
instrumentDescriptor.name
),
metricKind: transformMetricKind(instrumentDescriptor.type),
valueType: transformValueType(instrumentDescriptor.valueType),
unit: instrumentDescriptor.unit,
type: transformMetricType(metricPrefix, name),
description,
displayName: transformDisplayName(displayNamePrefix, name),
metricKind: transformMetricKind(metric),
valueType: transformValueType(metric),
unit,
labels: [
{
key: OPENTELEMETRY_TASK,
Expand All @@ -74,30 +86,34 @@ function transformDisplayName(displayNamePrefix: string, name: string): string {
}

/** Transforms a OpenTelemetry instrument type to a GCM MetricKind. */
function transformMetricKind(instrumentType: InstrumentType): MetricKind {
switch (instrumentType) {
case InstrumentType.COUNTER:
case InstrumentType.OBSERVABLE_COUNTER:
case InstrumentType.HISTOGRAM:
return MetricKind.CUMULATIVE;
case InstrumentType.UP_DOWN_COUNTER:
case InstrumentType.OBSERVABLE_GAUGE:
case InstrumentType.OBSERVABLE_UP_DOWN_COUNTER:
function transformMetricKind(metric: MetricData): MetricKind {
switch (metric.dataPointType) {
case DataPointType.SUM:
return metric.isMonotonic ? MetricKind.CUMULATIVE : MetricKind.GAUGE;
case DataPointType.GAUGE:
return MetricKind.GAUGE;
case DataPointType.HISTOGRAM:
return MetricKind.CUMULATIVE;
default:
exhaust(instrumentType);
diag.info('Encountered unexpected instrumentType=%s', instrumentType);
exhaust(metric);
diag.info(
'Encountered unexpected data point type %s',
(metric as MetricData).dataPointType
);
return MetricKind.UNSPECIFIED;
}
}

/** Transforms a OpenTelemetry ValueType to a GCM ValueType. */
function transformValueType(valueType: OTValueType): ValueType {
function transformValueType(metric: MetricData): ValueType {
const {valueType} = metric.descriptor;
if (valueType === OTValueType.DOUBLE) {
return ValueType.DOUBLE;
} else if (valueType === OTValueType.INT) {
return ValueType.INT64;
} else {
exhaust(valueType);
diag.info('Encountered unexpected value type %s', valueType);
return ValueType.VALUE_TYPE_UNSPECIFIED;
}
}
Expand All @@ -106,13 +122,13 @@ function transformValueType(valueType: OTValueType): ValueType {
* Converts metric's timeseries to a TimeSeries, so that metric can be
* uploaded to GCM.
*/
export function createTimeSeries<TMetricData extends MetricData>(
metric: TMetricData,
export function createTimeSeries(
metric: MetricData,
resource: MonitoredResource,
metricPrefix: string
): TimeSeries[] {
const metricKind = transformMetricKind(metric.descriptor.type);
const valueType = transformValueType(metric.descriptor.valueType);
const metricKind = transformMetricKind(metric);
const valueType = transformValueType(metric);

return transformPoints(metric, metricPrefix).map(({point, metric}) => ({
metric,
Expand Down

0 comments on commit b347659

Please sign in to comment.