Skip to content

Commit 4c8e612

Browse files
committed
fix(otel): Monitored Resource with task_id for Cloud Run
Fixes #14925 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 51e05fe commit 4c8e612

File tree

2 files changed

+39
-11
lines changed

2 files changed

+39
-11
lines changed

google/cloud/opentelemetry/internal/monitored_resource.cc

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

5353
struct OTelKeyMatch {
5454
std::vector<std::string> otel_keys;
55-
absl::optional<std::string> fallback = absl::nullopt;
55+
std::string fallback;
5656
};
5757

5858
class MonitoredResourceProvider {
@@ -72,8 +72,8 @@ class MonitoredResourceProvider {
7272
[](auto const& key, auto const& attr) { return key == attr.first; });
7373
if (found != oks.end()) {
7474
mr.labels[kv.first] = AsString(attributes.at(*found));
75-
} else if (kv.second.fallback) {
76-
mr.labels[kv.first] = *kv.second.fallback;
75+
} else {
76+
mr.labels[kv.first] = kv.second.fallback;
7777
}
7878
}
7979
return mr;
@@ -162,8 +162,8 @@ MonitoredResourceProvider GenericTask() {
162162
{"location",
163163
{{sc::kCloudAvailabilityZone, sc::kCloudRegion}, "global"}},
164164
{"namespace", {{sc::kServiceNamespace}}},
165-
{"job", {{sc::kServiceName}}},
166-
{"task_id", {{sc::kServiceInstanceId}}},
165+
{"job", {{sc::kServiceName, sc::kFaasName}}},
166+
{"task_id", {{sc::kServiceInstanceId, sc::kFaasInstance}}},
167167
});
168168
}
169169

@@ -174,7 +174,7 @@ MonitoredResourceProvider GenericNode() {
174174
{"location",
175175
{{sc::kCloudAvailabilityZone, sc::kCloudRegion}, "global"}},
176176
{"namespace", {{sc::kServiceNamespace}}},
177-
{"node_id", {{sc::kHostId}}},
177+
{"node_id", {{sc::kHostId, sc::kHostName}}},
178178
});
179179
}
180180

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)