Clarify that AttributesBuilder.put allows nulls (#7271)

This commit is contained in:
Tyler Benson 2025-04-16 12:08:03 -04:00 committed by GitHub
parent 14e2fefe7a
commit d13f04d084
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 44 additions and 25 deletions

View File

@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
import javax.annotation.Nullable;
class ArrayBackedAttributesBuilder implements AttributesBuilder { class ArrayBackedAttributesBuilder implements AttributesBuilder {
private final List<Object> data; private final List<Object> data;
@ -37,7 +38,7 @@ class ArrayBackedAttributesBuilder implements AttributesBuilder {
} }
@Override @Override
public <T> AttributesBuilder put(AttributeKey<T> key, T value) { public <T> AttributesBuilder put(AttributeKey<T> key, @Nullable T value) {
if (key == null || key.getKey().isEmpty() || value == null) { if (key == null || key.getKey().isEmpty() || value == null) {
return this; return this;
} }

View File

@ -18,6 +18,7 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
import javax.annotation.Nullable;
/** A builder of {@link Attributes} supporting an arbitrary number of key-value pairs. */ /** A builder of {@link Attributes} supporting an arbitrary number of key-value pairs. */
public interface AttributesBuilder { public interface AttributesBuilder {
@ -35,18 +36,22 @@ public interface AttributesBuilder {
// version. // version.
<T> AttributesBuilder put(AttributeKey<Long> key, int value); <T> AttributesBuilder put(AttributeKey<Long> key, int value);
/** Puts a {@link AttributeKey} with associated value into this. */ /**
<T> AttributesBuilder put(AttributeKey<T> key, T value); * Puts an {@link AttributeKey} with an associated value into this if the value is non-null.
* Providing a null value does not remove or unset previously set values.
*/
<T> AttributesBuilder put(AttributeKey<T> key, @Nullable T value);
/** /**
* Puts a String attribute into this. * Puts a String attribute into this if the value is non-null. Providing a null value does not
* remove or unset previously set values.
* *
* <p>Note: It is strongly recommended to use {@link #put(AttributeKey, Object)}, and pre-allocate * <p>Note: It is strongly recommended to use {@link #put(AttributeKey, Object)}, and pre-allocate
* your keys, if possible. * your keys, if possible.
* *
* @return this Builder * @return this Builder
*/ */
default AttributesBuilder put(String key, String value) { default AttributesBuilder put(String key, @Nullable String value) {
return put(stringKey(key), value); return put(stringKey(key), value);
} }

View File

@ -10,6 +10,7 @@ import io.opentelemetry.api.common.Value;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import java.time.Instant; import java.time.Instant;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
class DefaultLogger implements Logger { class DefaultLogger implements Logger {
@ -77,7 +78,7 @@ class DefaultLogger implements Logger {
} }
@Override @Override
public <T> LogRecordBuilder setAttribute(AttributeKey<T> key, T value) { public <T> LogRecordBuilder setAttribute(AttributeKey<T> key, @Nullable T value) {
return this; return this;
} }

View File

@ -16,6 +16,7 @@ import io.opentelemetry.api.common.Value;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import java.time.Instant; import java.time.Instant;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
/** /**
* Used to construct and emit log records from a {@link Logger}. * Used to construct and emit log records from a {@link Logger}.
@ -107,16 +108,20 @@ public interface LogRecordBuilder {
* Sets an attribute on the {@code LogRecord}. If the {@code LogRecord} previously contained a * Sets an attribute on the {@code LogRecord}. If the {@code LogRecord} previously contained a
* mapping for the key, the old value is replaced by the specified value. * mapping for the key, the old value is replaced by the specified value.
* *
* <p>Note: Providing a null value is a no-op and will not remove previously set values.
*
* @param key the key for this attribute. * @param key the key for this attribute.
* @param value the value for this attribute. * @param value the value for this attribute.
* @return this. * @return this.
*/ */
<T> LogRecordBuilder setAttribute(AttributeKey<T> key, T value); <T> LogRecordBuilder setAttribute(AttributeKey<T> key, @Nullable T value);
/** /**
* Sets a String attribute on the {@code LogRecord}. If the {@code LogRecord} previously contained * Sets a String attribute on the {@code LogRecord}. If the {@code LogRecord} previously contained
* a mapping for the key, the old value is replaced by the specified value. * a mapping for the key, the old value is replaced by the specified value.
* *
* <p>Note: Providing a null value is a no-op and will not remove previously set values.
*
* <p>Note: It is strongly recommended to use {@link #setAttribute(AttributeKey, Object)}, and * <p>Note: It is strongly recommended to use {@link #setAttribute(AttributeKey, Object)}, and
* pre-allocate your keys, if possible. * pre-allocate your keys, if possible.
* *
@ -125,7 +130,7 @@ public interface LogRecordBuilder {
* @return this. * @return this.
* @since 1.48.0 * @since 1.48.0
*/ */
default LogRecordBuilder setAttribute(String key, String value) { default LogRecordBuilder setAttribute(String key, @Nullable String value) {
return setAttribute(stringKey(key), value); return setAttribute(stringKey(key), value);
} }

View File

@ -8,6 +8,7 @@ package io.opentelemetry.api.trace;
import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.Attributes;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
@ -42,7 +43,7 @@ final class PropagatedSpan implements Span {
} }
@Override @Override
public Span setAttribute(String key, String value) { public Span setAttribute(String key, @Nullable String value) {
return this; return this;
} }
@ -62,7 +63,7 @@ final class PropagatedSpan implements Span {
} }
@Override @Override
public <T> Span setAttribute(AttributeKey<T> key, T value) { public <T> Span setAttribute(AttributeKey<T> key, @Nullable T value) {
return this; return this;
} }

View File

@ -88,7 +88,9 @@ public interface Span extends ImplicitContextKeyed {
* Sets an attribute to the {@code Span}. If the {@code Span} previously contained a mapping for * Sets an attribute to the {@code Span}. If the {@code Span} previously contained a mapping for
* the key, the old value is replaced by the specified value. * the key, the old value is replaced by the specified value.
* *
* <p>Empty String "" and null are valid attribute {@code value}, but not valid keys. * <p>Empty String "" and null are valid attribute {@code value}s, but not valid keys.
*
* <p>Note: Providing a null value is a no-op and will not remove previously set values.
* *
* <p>Note: It is strongly recommended to use {@link #setAttribute(AttributeKey, Object)}, and * <p>Note: It is strongly recommended to use {@link #setAttribute(AttributeKey, Object)}, and
* pre-allocate your keys, if possible. * pre-allocate your keys, if possible.
@ -97,7 +99,7 @@ public interface Span extends ImplicitContextKeyed {
* @param value the value for this attribute. * @param value the value for this attribute.
* @return this. * @return this.
*/ */
default Span setAttribute(String key, String value) { default Span setAttribute(String key, @Nullable String value) {
return setAttribute(AttributeKey.stringKey(key), value); return setAttribute(AttributeKey.stringKey(key), value);
} }
@ -150,13 +152,13 @@ public interface Span extends ImplicitContextKeyed {
* Sets an attribute to the {@code Span}. If the {@code Span} previously contained a mapping for * Sets an attribute to the {@code Span}. If the {@code Span} previously contained a mapping for
* the key, the old value is replaced by the specified value. * the key, the old value is replaced by the specified value.
* *
* <p>Note: the behavior of null values is undefined, and hence strongly discouraged. * <p>Note: Providing a null value is a no-op.
* *
* @param key the key for this attribute. * @param key the key for this attribute.
* @param value the value for this attribute. * @param value the value for this attribute.
* @return this. * @return this.
*/ */
<T> Span setAttribute(AttributeKey<T> key, T value); <T> Span setAttribute(AttributeKey<T> key, @Nullable T value);
/** /**
* Sets an attribute to the {@code Span}. If the {@code Span} previously contained a mapping for * Sets an attribute to the {@code Span}. If the {@code Span} previously contained a mapping for

View File

@ -26,6 +26,7 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** Unit tests for {@link Attributes}s. */ /** Unit tests for {@link Attributes}s. */
@ -565,7 +566,7 @@ class AttributesTest {
} }
@Override @Override
public <T> AttributesBuilder put(AttributeKey<T> key, T value) { public <T> AttributesBuilder put(AttributeKey<T> key, @Nullable T value) {
return null; return null;
} }

View File

@ -13,6 +13,7 @@ import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import java.time.Instant; import java.time.Instant;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
class ExtendedDefaultLogger implements ExtendedLogger { class ExtendedDefaultLogger implements ExtendedLogger {
@ -51,7 +52,7 @@ class ExtendedDefaultLogger implements ExtendedLogger {
} }
@Override @Override
public <T> ExtendedLogRecordBuilder setAttribute(AttributeKey<T> key, T value) { public <T> ExtendedLogRecordBuilder setAttribute(AttributeKey<T> key, @Nullable T value) {
return this; return this;
} }

View File

@ -15,6 +15,7 @@ import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
import java.time.Instant; import java.time.Instant;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
/** Extended {@link LogRecordBuilder} with experimental APIs. */ /** Extended {@link LogRecordBuilder} with experimental APIs. */
public interface ExtendedLogRecordBuilder extends LogRecordBuilder { public interface ExtendedLogRecordBuilder extends LogRecordBuilder {
@ -110,7 +111,7 @@ public interface ExtendedLogRecordBuilder extends LogRecordBuilder {
* attribute APIs. * attribute APIs.
*/ */
@Override @Override
<T> ExtendedLogRecordBuilder setAttribute(AttributeKey<T> key, T value); <T> ExtendedLogRecordBuilder setAttribute(AttributeKey<T> key, @Nullable T value);
/** /**
* Set an attribute. * Set an attribute.

View File

@ -15,6 +15,7 @@ import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
import java.time.Instant; import java.time.Instant;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
/** /**
* Delegates <i>all</i> {@link Span} methods to some underlying Span via {@link * Delegates <i>all</i> {@link Span} methods to some underlying Span via {@link
@ -52,12 +53,12 @@ interface DelegatingSpan extends Span {
} }
@Override @Override
default <T> Span setAttribute(AttributeKey<T> key, T value) { default <T> Span setAttribute(AttributeKey<T> key, @Nullable T value) {
return getDelegate().setAttribute(key, value); return getDelegate().setAttribute(key, value);
} }
@Override @Override
default Span setAttribute(String key, String value) { default Span setAttribute(String key, @Nullable String value) {
return getDelegate().setAttribute(key, value); return getDelegate().setAttribute(key, value);
} }

View File

@ -37,7 +37,7 @@ import io.opentelemetry.api.trace.StatusCode;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull; import javax.annotation.Nullable;
final class OpenTelemetryNoRecordEventsSpanImpl extends Span final class OpenTelemetryNoRecordEventsSpanImpl extends Span
implements io.opentelemetry.api.trace.Span { implements io.opentelemetry.api.trace.Span {
@ -110,7 +110,7 @@ final class OpenTelemetryNoRecordEventsSpanImpl extends Span
} }
@Override @Override
public io.opentelemetry.api.trace.Span setAttribute(String key, @Nonnull String value) { public io.opentelemetry.api.trace.Span setAttribute(String key, @Nullable String value) {
return this; return this;
} }
@ -130,7 +130,7 @@ final class OpenTelemetryNoRecordEventsSpanImpl extends Span
} }
@Override @Override
public <T> io.opentelemetry.api.trace.Span setAttribute(AttributeKey<T> key, @Nonnull T value) { public <T> io.opentelemetry.api.trace.Span setAttribute(AttributeKey<T> key, @Nullable T value) {
return this; return this;
} }

View File

@ -117,7 +117,7 @@ final class ExtendedSdkLogRecordBuilder extends SdkLogRecordBuilder
} }
@Override @Override
public <T> ExtendedSdkLogRecordBuilder setAttribute(AttributeKey<T> key, T value) { public <T> ExtendedSdkLogRecordBuilder setAttribute(AttributeKey<T> key, @Nullable T value) {
if (key == null || key.getKey().isEmpty() || value == null) { if (key == null || key.getKey().isEmpty() || value == null) {
return this; return this;
} }

View File

@ -95,7 +95,7 @@ class SdkLogRecordBuilder implements LogRecordBuilder {
} }
@Override @Override
public <T> SdkLogRecordBuilder setAttribute(AttributeKey<T> key, T value) { public <T> SdkLogRecordBuilder setAttribute(AttributeKey<T> key, @Nullable T value) {
if (key == null || key.getKey().isEmpty() || value == null) { if (key == null || key.getKey().isEmpty() || value == null) {
return this; return this;
} }

View File

@ -314,7 +314,7 @@ final class SdkSpan implements ReadWriteSpan {
} }
@Override @Override
public <T> ReadWriteSpan setAttribute(AttributeKey<T> key, T value) { public <T> ReadWriteSpan setAttribute(AttributeKey<T> key, @Nullable T value) {
if (key == null || key.getKey().isEmpty() || value == null) { if (key == null || key.getKey().isEmpty() || value == null) {
return this; return this;
} }