-
Notifications
You must be signed in to change notification settings - Fork 936
Fix OTLP marshaling for empty string attributes #8014
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8014 +/- ##
============================================
+ Coverage 90.14% 90.19% +0.04%
- Complexity 7476 7479 +3
============================================
Files 834 834
Lines 22540 22539 -1
Branches 2236 2236
============================================
+ Hits 20319 20329 +10
+ Misses 1516 1509 -7
+ Partials 705 701 -4 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| .build(), | ||
| KeyValue.newBuilder() | ||
| .setKey("empty_string") | ||
| .setValue(AnyValue.newBuilder().setStringValue("").build()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC correctly, this is the only new difference resulting from this PR. The other test cases are demonstrating existing logic.
What's the current behavior of empty string? Presumably it would just be some sort of null / emtpy value, like AnyValue.newBuilder().build()?
What's the spec language that leads to the conclusion that empty strings should be included vs. omitted? I see this line that empty values are meaningful and must be passed to exporters, but should the OTLP exporter serialize those or omit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC correctly, this is the only new difference resulting from this PR. The other test cases are demonstrating existing logic.
👍
What's the current behavior of empty string? Presumably it would just be some sort of null / emtpy value, like
AnyValue.newBuilder().build()?
👍
What's the spec language that leads to the conclusion that empty strings should be included vs. omitted? I see this line that empty values are meaningful and must be passed to exporters, but should the OTLP exporter serialize those or omit?
to me it follows from protobuf specification itself https://protobuf.dev/programming-guides/proto3/#oneof
If you set a oneof field to the default value (such as setting an int32 oneof field to 0), the “case” of that oneof field will be set, and the value will be serialized on the wire.
(sorry, should have linked to open-telemetry/opentelemetry-specification#4660 (comment) in case you hadn't seen that)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(sorry, should have linked to open-telemetry/opentelemetry-specification#4660 (comment) in case you hadn't seen that)
Ah got it! Makes sense.
| context.addSize(utf8Size); | ||
| return AnyValue.STRING_VALUE.getTagSize() | ||
| + CodedOutputStream.computeUInt32SizeNoTag(utf8Size) | ||
| + utf8Size; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the new implementations are performing the function of sizeStringWithContext, but without the if (value == null || value.isEmpty()) short circuiting:
public static int sizeStringWithContext(
ProtoFieldInfo field, @Nullable String value, MarshalerContext context) {
if (value == null || value.isEmpty()) {
return sizeBytes(field, 0);
}
if (context.marshalStringNoAllocation()) {
int utf8Size = context.getStringEncoder().getUtf8Size(value);
context.addSize(utf8Size);
return sizeBytes(field, utf8Size);
} else {
byte[] valueUtf8 = MarshalerUtil.toBytes(value);
context.addData(valueUtf8);
return sizeBytes(field, valueUtf8.length);
}
}
What about the if (context.marshalStringNoAllocation()) {} else {} block? Would it be better to include an overload of serializeStringWithContext / sizeStringWithContext which ignores the if (value == null || value.isEmpty()) short circuiting?
Based on https://protobuf.dev/programming-guides/proto3/#oneof