Skip to content

Fix bug preventing accurate reporting of dropped attribute count #7142

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

Merged
merged 2 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
* A map with a fixed capacity that drops attributes when the map gets full, and which truncates
* string and array string attribute values to the {@link #lengthLimit}.
*
* <p>WARNING: In order to reduce memory allocation, this class extends {@link HashMap} when it
* would be more appropriate to delegate. The problem with extending is that we don't enforce that
* all {@link HashMap} methods for reading / writing data conform to the configured attribute
* limits. Therefore, it's easy to accidentally call something like {@link Map#putAll(Map)} or
* {@link Map#put(Object, Object)} and bypass the restrictions (see <a
* href="https://github.com/open-telemetry/opentelemetry-java/issues/7135">#7135</a>). Callers MUST
* take care to only call methods from {@link AttributesMap}, and not {@link HashMap}.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
Expand All @@ -44,13 +52,18 @@ public static AttributesMap create(long capacity, int lengthLimit) {
return new AttributesMap(capacity, lengthLimit);
}

/** Add the attribute key value pair, applying capacity and length limits. */
public <T> void put(AttributeKey<T> key, T value) {
/**
* Add the attribute key value pair, applying capacity and length limits. Callers MUST ensure the
* {@code value} type matches the type required by {@code key}.
*/
@Override
@Nullable
public Object put(AttributeKey<?> key, Object value) {
totalAddedValues++;
if (size() >= capacity && !containsKey(key)) {
return;
return null;
}
super.put(key, AttributeUtil.applyAttributeLengthLimit(value, lengthLimit));
return super.put(key, AttributeUtil.applyAttributeLengthLimit(value, lengthLimit));
}

/** Get the total number of attributes added, including those dropped for capcity limits. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ public ReadWriteSpan recordException(Throwable exception) {
}

@Override
@SuppressWarnings("unchecked")
public ReadWriteSpan recordException(Throwable exception, Attributes additionalAttributes) {
if (exception == null) {
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,21 @@ void recordException_additionalAttributes() {
});
}

@Test
void recordException_SpanLimits() {
SdkSpan span = createTestSpan(SpanLimits.builder().setMaxNumberOfAttributes(2).build());
span.recordException(
new IllegalStateException("error"),
Attributes.builder().put("key1", "value").put("key2", "value").build());

List<EventData> events = span.toSpanData().getEvents();
assertThat(events.size()).isEqualTo(1);
EventData event = events.get(0);
assertThat(event.getAttributes().size()).isEqualTo(2);
assertThat(event.getTotalAttributeCount()).isEqualTo(5);
assertThat(event.getTotalAttributeCount() - event.getAttributes().size()).isPositive();
}

@Test
void badArgsIgnored() {
SdkSpan span = createTestRootSpan();
Expand Down Expand Up @@ -1519,7 +1534,9 @@ void testAsSpanData() {
Resource resource = this.resource;
Attributes attributes = TestUtils.generateRandomAttributes();
AttributesMap attributesWithCapacity = AttributesMap.create(32, Integer.MAX_VALUE);
attributes.forEach(attributesWithCapacity::put);
attributes.forEach(
(attributeKey, object) ->
attributesWithCapacity.put((AttributeKey<Object>) attributeKey, object));
Attributes event1Attributes = TestUtils.generateRandomAttributes();
Attributes event2Attributes = TestUtils.generateRandomAttributes();
SpanContext context =
Expand Down
Loading