Skip to content

Commit 6e93bfe

Browse files
authored
Update resource mapping (#134)
* Update resource mapping code to match the 'new' specification for GCP + collector exporter. * Fixes GKE resource to be container to align w/ logs.
1 parent 42ffc5c commit 6e93bfe

File tree

7 files changed

+342
-142
lines changed

7 files changed

+342
-142
lines changed

exporters/metrics/src/main/java/com/google/cloud/opentelemetry/metric/AggregateByLabelMetricTimeSeriesBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import static com.google.cloud.opentelemetry.metric.MetricTranslator.mapInterval;
2020
import static com.google.cloud.opentelemetry.metric.MetricTranslator.mapMetric;
2121
import static com.google.cloud.opentelemetry.metric.MetricTranslator.mapMetricDescriptor;
22-
import static com.google.cloud.opentelemetry.metric.MetricTranslator.mapResource;
22+
import static com.google.cloud.opentelemetry.metric.ResourceTranslator.mapResource;
2323

2424
import com.google.api.MetricDescriptor;
2525
import com.google.cloud.opentelemetry.metric.MetricExporter.MetricWithLabels;
@@ -106,7 +106,7 @@ private TimeSeries.Builder makeTimeSeriesHeader(
106106
return TimeSeries.newBuilder()
107107
.setMetric(mapMetric(attributes, descriptor.getType()))
108108
.setMetricKind(descriptor.getMetricKind())
109-
.setResource(mapResource(metric.getResource(), projectId));
109+
.setResource(mapResource(metric.getResource()));
110110
}
111111

112112
@Override

exporters/metrics/src/main/java/com/google/cloud/opentelemetry/metric/MetricTranslator.java

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import com.google.api.LabelDescriptor;
2222
import com.google.api.Metric;
2323
import com.google.api.MetricDescriptor;
24-
import com.google.api.MonitoredResource;
2524
import com.google.common.collect.ImmutableSet;
2625
import com.google.monitoring.v3.DroppedLabels;
2726
import com.google.monitoring.v3.SpanContext;
@@ -36,7 +35,6 @@
3635
import io.opentelemetry.sdk.metrics.data.MetricData;
3736
import io.opentelemetry.sdk.metrics.data.MetricDataType;
3837
import io.opentelemetry.sdk.metrics.data.SumData;
39-
import io.opentelemetry.sdk.resources.Resource;
4038
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
4139
import java.util.Map;
4240
import java.util.Set;
@@ -229,42 +227,6 @@ static TimeInterval mapInterval(
229227
return TimeInterval.newBuilder().setStartTime(startTime).setEndTime(endTime).build();
230228
}
231229

232-
static MonitoredResource mapResource(Resource resource, String projectId) {
233-
// First, we try to map to known GCP resources
234-
Attributes attributes = resource.getAttributes();
235-
236-
// GCE: https://cloud.google.com/monitoring/api/resources#tag_gce_instance
237-
String provider = attributes.get(ResourceAttributes.CLOUD_PROVIDER);
238-
if (ResourceAttributes.CloudProviderValues.GCP.equals(provider)) {
239-
String namespace = attributes.get(ResourceAttributes.K8S_NAMESPACE_NAME);
240-
if (namespace != null) {
241-
return MonitoredResource.newBuilder()
242-
.setType("gke_container")
243-
.putAllLabels(
244-
gkeMap.entrySet().stream()
245-
.collect(
246-
Collectors.toMap(
247-
e -> (String) e.getKey(), e -> attributes.get(e.getValue()))))
248-
.build();
249-
}
250-
return MonitoredResource.newBuilder()
251-
.setType("gce_instance")
252-
.putAllLabels(
253-
gceMap.entrySet().stream()
254-
.collect(
255-
Collectors.toMap(
256-
e -> (String) e.getKey(), e -> attributes.get(e.getValue()))))
257-
.build();
258-
}
259-
260-
// If none of the standard resource types apply, use the "global" resource:
261-
// https://cloud.google.com/monitoring/api/resources#tag_global
262-
return MonitoredResource.newBuilder()
263-
.setType(DEFAULT_RESOURCE_TYPE)
264-
.putLabels(RESOURCE_PROJECT_ID_LABEL, projectId)
265-
.build();
266-
}
267-
268230
private static Timestamp mapTimestamp(long epochNanos) {
269231
return Timestamp.newBuilder()
270232
.setSeconds(epochNanos / NANO_PER_SECOND)
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright 2022 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.opentelemetry.metric;
17+
18+
import com.google.api.MonitoredResource;
19+
import io.opentelemetry.api.common.AttributeKey;
20+
import io.opentelemetry.api.common.Attributes;
21+
import io.opentelemetry.sdk.resources.Resource;
22+
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
23+
import java.util.List;
24+
import java.util.Optional;
25+
26+
/** Translates from OpenTelemetry Resource into Google Cloud Monitoring's MonitoredResource. */
27+
public class ResourceTranslator {
28+
private ResourceTranslator() {}
29+
30+
@com.google.auto.value.AutoValue
31+
public abstract static class AttributeMapping {
32+
/** The label name used in GCP's MonitoredResource. */
33+
public abstract String getLabelName();
34+
/** The list of OTEL keys that can be used for this resource label, in priority order. */
35+
public abstract java.util.List<AttributeKey<?>> getOtelKeys();
36+
/** A fallback value to set the resource. */
37+
public abstract Optional<String> fallbackLiteral();
38+
39+
public void fill(Resource resource, MonitoredResource.Builder builder) {
40+
for (AttributeKey<?> key : getOtelKeys()) {
41+
Object value = resource.getAttribute(key);
42+
if (value != null) {
43+
builder.putLabels(getLabelName(), value.toString());
44+
return;
45+
}
46+
}
47+
fallbackLiteral().ifPresent(value -> builder.putLabels(getLabelName(), value));
48+
}
49+
50+
public static AttributeMapping create(String labelName, AttributeKey<?> otelKey) {
51+
return new AutoValue_ResourceTranslator_AttributeMapping(
52+
labelName, java.util.Collections.singletonList(otelKey), Optional.empty());
53+
}
54+
55+
public static AttributeMapping create(
56+
String labelName, java.util.List<AttributeKey<?>> otelKeys) {
57+
return new AutoValue_ResourceTranslator_AttributeMapping(
58+
labelName, otelKeys, Optional.empty());
59+
}
60+
61+
public static AttributeMapping create(
62+
String labelName, java.util.List<AttributeKey<?>> otelKeys, String fallbackLiteral) {
63+
return new AutoValue_ResourceTranslator_AttributeMapping(
64+
labelName, otelKeys, Optional.of(fallbackLiteral));
65+
}
66+
}
67+
68+
private static List<AttributeMapping> GCE_INSTANCE_LABELS =
69+
java.util.Arrays.asList(
70+
AttributeMapping.create("zone", ResourceAttributes.CLOUD_AVAILABILITY_ZONE),
71+
AttributeMapping.create("instance_id", ResourceAttributes.HOST_ID));
72+
private static List<AttributeMapping> K8S_CONTAINER_LABELS =
73+
java.util.Arrays.asList(
74+
AttributeMapping.create("location", ResourceAttributes.CLOUD_AVAILABILITY_ZONE),
75+
AttributeMapping.create("cluster_name", ResourceAttributes.K8S_CLUSTER_NAME),
76+
AttributeMapping.create("namespace_name", ResourceAttributes.K8S_NAMESPACE_NAME),
77+
AttributeMapping.create("container_name", ResourceAttributes.K8S_CONTAINER_NAME),
78+
AttributeMapping.create("pod_name", ResourceAttributes.K8S_POD_NAME));
79+
private static List<AttributeMapping> AWS_EC2_INSTANCE_LABELS =
80+
java.util.Arrays.asList(
81+
AttributeMapping.create("instance_id", ResourceAttributes.HOST_ID),
82+
AttributeMapping.create("region", ResourceAttributes.CLOUD_AVAILABILITY_ZONE),
83+
AttributeMapping.create("aws_account", ResourceAttributes.CLOUD_ACCOUNT_ID));
84+
private static List<AttributeMapping> GENERIC_TASK_LABELS =
85+
java.util.Arrays.asList(
86+
AttributeMapping.create(
87+
"location",
88+
java.util.Arrays.asList(
89+
ResourceAttributes.CLOUD_AVAILABILITY_ZONE, ResourceAttributes.CLOUD_REGION),
90+
"global"),
91+
AttributeMapping.create("namespace", ResourceAttributes.SERVICE_NAMESPACE),
92+
AttributeMapping.create("job", ResourceAttributes.SERVICE_NAME),
93+
AttributeMapping.create("task_id", ResourceAttributes.SERVICE_INSTANCE_ID));
94+
95+
/** Converts a Java OpenTelemetyr SDK resoruce into a MonitoredResource from GCP. */
96+
public static MonitoredResource mapResource(Resource resource) {
97+
String platform = resource.getAttribute(ResourceAttributes.CLOUD_PLATFORM);
98+
if (platform == null) {
99+
return mapBase(resource, "generic_task", GENERIC_TASK_LABELS);
100+
}
101+
switch (platform) {
102+
case ResourceAttributes.CloudPlatformValues.GCP_COMPUTE_ENGINE:
103+
return mapBase(resource, "gce_instance", GCE_INSTANCE_LABELS);
104+
case ResourceAttributes.CloudPlatformValues.GCP_KUBERNETES_ENGINE:
105+
return mapBase(resource, "k8s_container", K8S_CONTAINER_LABELS);
106+
case ResourceAttributes.CloudPlatformValues.AWS_EC2:
107+
return mapBase(resource, "aws_ec2_instance", AWS_EC2_INSTANCE_LABELS);
108+
default:
109+
return mapBase(resource, "generic_task", GENERIC_TASK_LABELS);
110+
}
111+
}
112+
113+
private static MonitoredResource mapBase(
114+
Resource resource, String mrType, List<AttributeMapping> mappings) {
115+
MonitoredResource.Builder mr = MonitoredResource.newBuilder();
116+
mr.setType(mrType);
117+
io.opentelemetry.api.common.AttributesBuilder unused = Attributes.builder();
118+
for (AttributeMapping mapping : mappings) {
119+
mapping.fill(resource, mr);
120+
}
121+
return mr.build();
122+
}
123+
}

exporters/metrics/src/test/java/com/google/cloud/opentelemetry/metric/FakeData.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ public class FakeData {
5757

5858
static final Attributes someGceAttributes =
5959
Attributes.builder()
60+
.put(
61+
ResourceAttributes.CLOUD_PLATFORM,
62+
ResourceAttributes.CloudPlatformValues.GCP_COMPUTE_ENGINE)
6063
.put(ResourceAttributes.CLOUD_ACCOUNT_ID, aProjectId)
6164
.put(ResourceAttributes.HOST_ID, aHostId)
6265
.put(ResourceAttributes.CLOUD_AVAILABILITY_ZONE, aCloudZone)

exporters/metrics/src/test/java/com/google/cloud/opentelemetry/metric/MetricExporterTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ public void testExportSucceeds() {
175175
.setResource(
176176
MonitoredResource.newBuilder()
177177
.setType("gce_instance")
178-
.putLabels("project_id", aProjectId)
179178
.putLabels("instance_id", aHostId)
180179
.putLabels("zone", aCloudZone)
181180
.build())
@@ -277,7 +276,6 @@ public void testExportWithHistogram_Succeeds() {
277276
.setResource(
278277
MonitoredResource.newBuilder()
279278
.setType("gce_instance")
280-
.putLabels("project_id", aProjectId)
281279
.putLabels("instance_id", aHostId)
282280
.putLabels("zone", aCloudZone)
283281
.build())

exporters/metrics/src/test/java/com/google/cloud/opentelemetry/metric/MetricTranslatorTest.java

Lines changed: 0 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,17 @@
3737
import com.google.api.Metric.Builder;
3838
import com.google.api.MetricDescriptor;
3939
import com.google.api.MetricDescriptor.MetricKind;
40-
import com.google.api.MonitoredResource;
4140
import com.google.common.collect.ImmutableList;
4241
import com.google.monitoring.v3.DroppedLabels;
4342
import com.google.monitoring.v3.SpanContext;
4443
import com.google.protobuf.InvalidProtocolBufferException;
45-
import io.opentelemetry.api.common.AttributeKey;
4644
import io.opentelemetry.api.common.Attributes;
47-
import io.opentelemetry.api.common.AttributesBuilder;
4845
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
4946
import io.opentelemetry.sdk.metrics.data.DoubleHistogramData;
5047
import io.opentelemetry.sdk.metrics.data.DoubleSumData;
5148
import io.opentelemetry.sdk.metrics.data.DoubleSummaryData;
5249
import io.opentelemetry.sdk.metrics.data.LongSumData;
5350
import io.opentelemetry.sdk.metrics.data.MetricData;
54-
import io.opentelemetry.sdk.resources.Resource;
55-
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
56-
import java.util.Map;
57-
import java.util.stream.Collectors;
58-
import java.util.stream.Stream;
5951
import org.junit.Test;
6052
import org.junit.runner.RunWith;
6153
import org.junit.runners.JUnit4;
@@ -240,98 +232,6 @@ public void testMapLabelWithPeriodInNameSucceeds() {
240232
assertEquals(expectedLabel, actualLabel);
241233
}
242234

243-
@Test
244-
public void testMapResourcesWithGCEResource() {
245-
Map<AttributeKey<String>, String> testAttributes =
246-
Stream.of(
247-
new Object[][] {
248-
{ResourceAttributes.CLOUD_PROVIDER, ResourceAttributes.CloudProviderValues.GCP},
249-
{ResourceAttributes.CLOUD_ACCOUNT_ID, "GCE-pid"},
250-
{ResourceAttributes.CLOUD_AVAILABILITY_ZONE, "country-region-zone"},
251-
{ResourceAttributes.CLOUD_REGION, "country-region"},
252-
{ResourceAttributes.HOST_ID, "GCE-instance-id"},
253-
{ResourceAttributes.HOST_NAME, "GCE-instance-name"},
254-
{ResourceAttributes.HOST_TYPE, "GCE-instance-type"}
255-
})
256-
.collect(
257-
Collectors.toMap(data -> (AttributeKey<String>) data[0], data -> (String) data[1]));
258-
AttributesBuilder attrBuilder = Attributes.builder();
259-
testAttributes.forEach(
260-
(key, value) -> {
261-
attrBuilder.put(key, value);
262-
});
263-
Attributes attributes = attrBuilder.build();
264-
265-
MonitoredResource monitoredResource =
266-
MetricTranslator.mapResource(Resource.create(attributes), "GCE_pid");
267-
268-
assertEquals("gce_instance", monitoredResource.getType());
269-
270-
Map<String, String> monitoredResourceMap = monitoredResource.getLabelsMap();
271-
assertEquals(3, monitoredResourceMap.size());
272-
273-
Map<String, String> expectedMappings =
274-
Stream.of(
275-
new Object[][] {
276-
{"instance_id", "GCE-instance-id"},
277-
{"zone", "country-region-zone"},
278-
{"project_id", "GCE-pid"}
279-
})
280-
.collect(Collectors.toMap(data -> (String) data[0], data -> (String) data[1]));
281-
expectedMappings.forEach(
282-
(key, value) -> {
283-
assertEquals(value, monitoredResourceMap.get(key));
284-
});
285-
}
286-
287-
@Test
288-
public void testMapResourcesWithGKEResource() {
289-
Map<AttributeKey<String>, String> testAttributes =
290-
Stream.of(
291-
new Object[][] {
292-
{ResourceAttributes.CLOUD_PROVIDER, ResourceAttributes.CloudProviderValues.GCP},
293-
{ResourceAttributes.CLOUD_ACCOUNT_ID, "GCE-pid"},
294-
{ResourceAttributes.CLOUD_AVAILABILITY_ZONE, "country-region-zone"},
295-
{ResourceAttributes.CLOUD_REGION, "country-region"},
296-
{ResourceAttributes.HOST_ID, "GCE-instance-id"},
297-
{ResourceAttributes.HOST_NAME, "GCE-instance-name"},
298-
{ResourceAttributes.HOST_TYPE, "GCE-instance-type"},
299-
{ResourceAttributes.K8S_CLUSTER_NAME, "GKE-cluster-name"},
300-
{ResourceAttributes.K8S_NAMESPACE_NAME, "GKE-testNameSpace"},
301-
{ResourceAttributes.K8S_POD_NAME, "GKE-testHostName"},
302-
{ResourceAttributes.K8S_CONTAINER_NAME, "GKE-testContainerName"}
303-
})
304-
.collect(
305-
Collectors.toMap(data -> (AttributeKey<String>) data[0], data -> (String) data[1]));
306-
AttributesBuilder attrBuilder = Attributes.builder();
307-
testAttributes.forEach(attrBuilder::put);
308-
Attributes attributes = attrBuilder.build();
309-
310-
MonitoredResource monitoredResource =
311-
MetricTranslator.mapResource(Resource.create(attributes), "GCE_pid");
312-
313-
assertEquals("gke_container", monitoredResource.getType());
314-
Map<String, String> monitoredResourceMap = monitoredResource.getLabelsMap();
315-
assertEquals(7, monitoredResourceMap.size());
316-
317-
Map<String, String> expectedMappings =
318-
Stream.of(
319-
new Object[][] {
320-
{"instance_id", "GCE-instance-id"},
321-
{"zone", "country-region-zone"},
322-
{"project_id", "GCE-pid"},
323-
{"cluster_name", "GKE-cluster-name"},
324-
{"pod_id", "GKE-testHostName"},
325-
{"container_name", "GKE-testContainerName"},
326-
{"namespace_id", "GKE-testNameSpace"}
327-
})
328-
.collect(Collectors.toMap(data -> (String) data[0], data -> (String) data[1]));
329-
expectedMappings.forEach(
330-
(key, value) -> {
331-
assertEquals(value, monitoredResourceMap.get(key));
332-
});
333-
}
334-
335235
@Test
336236
public void testMapDistribution() {
337237
Distribution result =

0 commit comments

Comments
 (0)