Skip to content

Commit 63bc533

Browse files
fix(otel): Exporter creating Monitored Resource with task_id for Cloud Run (#14923)
When inside a Cloud Run environment, the `MonitoredResource` in a `CreateTimeSeriesRequest` to the Cloud Monitoring API does not include the necessary fields for the `generic_task` resource type, and is rejected. Should follow the well-tested Golang implementation where the `faas.instance` OTel Resource Attribute is mapped to `MonitoredResource` `task_id`. As the `service.namespace` OTel Resource Attribute is not set by the Resource Detector from within Cloud Run, it should be mapped as an empty string, rather than being left absent. https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/blob/8da0f42dab085c916987891419461d583a2aa96e/internal/resourcemapping/resourcemapping.go#L153
1 parent a8c2e9d commit 63bc533

File tree

2 files changed

+40
-11
lines changed

2 files changed

+40
-11
lines changed

google/cloud/opentelemetry/internal/monitored_resource.cc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ struct AsStringVisitor {
5252

5353
struct OTelKeyMatch {
5454
std::vector<std::string> otel_keys;
55-
absl::optional<std::string> fallback = absl::nullopt;
55+
// NOLINTNEXTLINE(readability-redundant-member-init)
56+
std::string fallback = {};
5657
};
5758

5859
class MonitoredResourceProvider {
@@ -72,8 +73,8 @@ class MonitoredResourceProvider {
7273
[](auto const& key, auto const& attr) { return key == attr.first; });
7374
if (found != oks.end()) {
7475
mr.labels[kv.first] = AsString(attributes.at(*found));
75-
} else if (kv.second.fallback) {
76-
mr.labels[kv.first] = *kv.second.fallback;
76+
} else {
77+
mr.labels[kv.first] = kv.second.fallback;
7778
}
7879
}
7980
return mr;
@@ -162,8 +163,8 @@ MonitoredResourceProvider GenericTask() {
162163
{"location",
163164
{{sc::kCloudAvailabilityZone, sc::kCloudRegion}, "global"}},
164165
{"namespace", {{sc::kServiceNamespace}}},
165-
{"job", {{sc::kServiceName}}},
166-
{"task_id", {{sc::kServiceInstanceId}}},
166+
{"job", {{sc::kServiceName, sc::kFaasName}}},
167+
{"task_id", {{sc::kServiceInstanceId, sc::kFaasInstance}}},
167168
});
168169
}
169170

@@ -174,7 +175,7 @@ MonitoredResourceProvider GenericNode() {
174175
{"location",
175176
{{sc::kCloudAvailabilityZone, sc::kCloudRegion}, "global"}},
176177
{"namespace", {{sc::kServiceNamespace}}},
177-
{"node_id", {{sc::kHostId}}},
178+
{"node_id", {{sc::kHostId, sc::kHostName}}},
178179
});
179180
}
180181

google/cloud/opentelemetry/internal/monitored_resource_test.cc

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,11 @@ TEST(MonitoredResource, GenericTaskFaas) {
283283

284284
auto mr = ToMonitoredResource(attributes);
285285
EXPECT_EQ(mr.type, "generic_task");
286-
EXPECT_THAT(mr.labels,
287-
UnorderedElementsAre(Pair("location", test.expected_location)));
286+
EXPECT_THAT(mr.labels, UnorderedElementsAre(
287+
Pair("location", test.expected_location),
288+
// Verify fallback to empty string.
289+
Pair("namespace", ""), Pair("job", "faas-name"),
290+
Pair("task_id", "faas-instance")));
288291
}
289292
}
290293

@@ -320,18 +323,18 @@ TEST(MonitoredResource, GenericTaskService) {
320323
}
321324

322325
TEST(MonitoredResource, GenericNode) {
323-
struct TestCase {
326+
struct LocationTestCase {
324327
absl::optional<std::string> zone;
325328
absl::optional<std::string> region;
326329
std::string expected_location;
327330
};
328-
auto tests = std::vector<TestCase>{
331+
auto location_tests = std::vector<LocationTestCase>{
329332
{"us-central1-a", "us-central1", "us-central1-a"},
330333
{"us-central1-a", absl::nullopt, "us-central1-a"},
331334
{absl::nullopt, "us-central1", "us-central1"},
332335
{absl::nullopt, absl::nullopt, "global"},
333336
};
334-
for (auto const& test : tests) {
337+
for (auto const& test : location_tests) {
335338
auto attributes = opentelemetry::sdk::resource::ResourceAttributes{
336339
{sc::kServiceNamespace, "test-namespace"},
337340
{sc::kHostId, "test-instance"},
@@ -346,6 +349,31 @@ TEST(MonitoredResource, GenericNode) {
346349
Pair("namespace", "test-namespace"),
347350
Pair("node_id", "test-instance")));
348351
}
352+
353+
struct NodeIDTestCase {
354+
absl::optional<std::string> host_id;
355+
std::string expected_node_id;
356+
};
357+
auto node_id_tests = std::vector<NodeIDTestCase>{
358+
{"test-instance", "test-instance"},
359+
{absl::nullopt, "test-name"},
360+
};
361+
for (auto const& test : node_id_tests) {
362+
auto attributes = opentelemetry::sdk::resource::ResourceAttributes{
363+
{sc::kCloudAvailabilityZone, "us-central1-a"},
364+
{sc::kCloudRegion, "us-central1"},
365+
{sc::kServiceNamespace, "test-namespace"},
366+
{sc::kHostName, "test-name"},
367+
};
368+
if (test.host_id) attributes[sc::kHostId] = *test.host_id;
369+
370+
auto mr = ToMonitoredResource(attributes);
371+
EXPECT_EQ(mr.type, "generic_node");
372+
EXPECT_THAT(mr.labels,
373+
UnorderedElementsAre(Pair("location", "us-central1-a"),
374+
Pair("namespace", "test-namespace"),
375+
Pair("node_id", test.expected_node_id)));
376+
}
349377
}
350378

351379
} // namespace

0 commit comments

Comments
 (0)