Implement dropped attribute count in logs (#4697)

* Implement dropped attribute count in logs

* PR feedback

* PR feedback
This commit is contained in:
jack-berg 2022-09-16 10:49:06 -05:00 committed by GitHub
parent 591aff6c15
commit a50ceb3959
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 56 additions and 18 deletions

View File

@ -50,8 +50,7 @@ final class LogMarshaler extends MarshalerWithSize {
MarshalerUtil.toBytes(logData.getSeverityText()), MarshalerUtil.toBytes(logData.getSeverityText()),
anyValueMarshaler, anyValueMarshaler,
attributeMarshalers, attributeMarshalers,
// TODO (trask) implement droppedAttributesCount in LogRecord logData.getTotalAttributeCount() - logData.getAttributes().size(),
0,
spanContext.getTraceFlags(), spanContext.getTraceFlags(),
spanContext.getTraceId().equals(INVALID_TRACE_ID) ? null : spanContext.getTraceId(), spanContext.getTraceId().equals(INVALID_TRACE_ID) ? null : spanContext.getTraceId(),
spanContext.getSpanId().equals(INVALID_SPAN_ID) ? null : spanContext.getSpanId()); spanContext.getSpanId().equals(INVALID_SPAN_ID) ? null : spanContext.getSpanId());

View File

@ -69,6 +69,7 @@ class LogsRequestMarshalerTest {
SpanContext.create( SpanContext.create(
TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault())) TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault()))
.setAttributes(Attributes.of(AttributeKey.booleanKey("key"), true)) .setAttributes(Attributes.of(AttributeKey.booleanKey("key"), true))
.setTotalAttributeCount(2)
.setEpoch(12345, TimeUnit.NANOSECONDS) .setEpoch(12345, TimeUnit.NANOSECONDS)
.build())); .build()));
@ -111,6 +112,7 @@ class LogsRequestMarshalerTest {
SpanContext.create( SpanContext.create(
TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault())) TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault()))
.setAttributes(Attributes.of(AttributeKey.booleanKey("key"), true)) .setAttributes(Attributes.of(AttributeKey.booleanKey("key"), true))
.setTotalAttributeCount(2)
.setEpoch(12345, TimeUnit.NANOSECONDS) .setEpoch(12345, TimeUnit.NANOSECONDS)
.build())); .build()));
@ -124,6 +126,7 @@ class LogsRequestMarshalerTest {
.setKey("key") .setKey("key")
.setValue(AnyValue.newBuilder().setBoolValue(true).build()) .setValue(AnyValue.newBuilder().setBoolValue(true).build())
.build()); .build());
assertThat(logRecord.getDroppedAttributesCount()).isEqualTo(1);
assertThat(logRecord.getTimeUnixNano()).isEqualTo(12345); assertThat(logRecord.getTimeUnixNano()).isEqualTo(12345);
} }
@ -148,6 +151,7 @@ class LogsRequestMarshalerTest {
.isEqualTo(Severity.UNDEFINED_SEVERITY_NUMBER.getSeverityNumber()); .isEqualTo(Severity.UNDEFINED_SEVERITY_NUMBER.getSeverityNumber());
assertThat(logRecord.getBody()).isEqualTo(AnyValue.newBuilder().setStringValue("").build()); assertThat(logRecord.getBody()).isEqualTo(AnyValue.newBuilder().setStringValue("").build());
assertThat(logRecord.getAttributesList()).isEmpty(); assertThat(logRecord.getAttributesList()).isEmpty();
assertThat(logRecord.getDroppedAttributesCount()).isZero();
assertThat(logRecord.getTimeUnixNano()).isEqualTo(12345); assertThat(logRecord.getTimeUnixNano()).isEqualTo(12345);
} }

View File

@ -163,4 +163,17 @@ public class LogDataAssert extends AbstractAssert<LogDataAssert, LogData> {
// implementations. // implementations.
return actual.getAttributes().asMap().equals(attributes.asMap()); return actual.getAttributes().asMap().equals(attributes.asMap());
} }
/** Asserts the log has the given total attributes. */
public LogDataAssert hasTotalAttributeCount(int totalAttributeCount) {
isNotNull();
if (actual.getTotalAttributeCount() != totalAttributeCount) {
failWithActualExpectedAndMessage(
actual.getTotalAttributeCount(),
totalAttributeCount,
"Expected log to have recorded <%s> total attributes but did not",
totalAttributeCount);
}
return this;
}
} }

View File

@ -31,7 +31,8 @@ public abstract class TestLogData implements LogData {
.setSpanContext(SpanContext.getInvalid()) .setSpanContext(SpanContext.getInvalid())
.setSeverity(Severity.UNDEFINED_SEVERITY_NUMBER) .setSeverity(Severity.UNDEFINED_SEVERITY_NUMBER)
.setBody("") .setBody("")
.setAttributes(Attributes.empty()); .setAttributes(Attributes.empty())
.setTotalAttributeCount(0);
} }
TestLogData() {} TestLogData() {}
@ -86,5 +87,8 @@ public abstract class TestLogData implements LogData {
/** Set the attributes. */ /** Set the attributes. */
public abstract Builder setAttributes(Attributes attributes); public abstract Builder setAttributes(Attributes attributes);
/** Set the total attribute count. */
public abstract Builder setTotalAttributeCount(int totalAttributeCount);
} }
} }

View File

@ -55,6 +55,7 @@ public class LogAssertionsTest {
.setSeverityText("info") .setSeverityText("info")
.setBody("message") .setBody("message")
.setAttributes(ATTRIBUTES) .setAttributes(ATTRIBUTES)
.setTotalAttributeCount(999)
.build(); .build();
@Test @Test
@ -109,7 +110,8 @@ public class LogAssertionsTest {
attributeEntry("colors", "red", "blue"), attributeEntry("colors", "red", "blue"),
attributeEntry("conditions", false, true), attributeEntry("conditions", false, true),
attributeEntry("scores", 0L, 1L), attributeEntry("scores", 0L, 1L),
attributeEntry("coins", 0.01, 0.05, 0.1))); attributeEntry("coins", 0.01, 0.05, 0.1)))
.hasTotalAttributeCount(999);
} }
@Test @Test
@ -179,5 +181,7 @@ public class LogAssertionsTest {
AttributeKey.stringKey("bear"), AttributeKey.stringKey("bear"),
value -> assertThat(value).hasSize(2)))) value -> assertThat(value).hasSize(2))))
.isInstanceOf(AssertionError.class); .isInstanceOf(AssertionError.class);
assertThatThrownBy(() -> assertThat(LOG_DATA).hasTotalAttributeCount(11))
.isInstanceOf(AssertionError.class);
} }
} }

View File

@ -31,7 +31,8 @@ abstract class SdkLogData implements LogData {
Severity severity, Severity severity,
@Nullable String severityText, @Nullable String severityText,
Body body, Body body,
Attributes attributes) { Attributes attributes,
int totalAttributeCount) {
return new AutoValue_SdkLogData( return new AutoValue_SdkLogData(
resource, resource,
instrumentationScopeInfo, instrumentationScopeInfo,
@ -40,6 +41,7 @@ abstract class SdkLogData implements LogData {
severity, severity,
severityText, severityText,
body, body,
attributes); attributes,
totalAttributeCount);
} }
} }

View File

@ -115,7 +115,8 @@ class SdkReadWriteLogRecord implements ReadWriteLogRecord {
severity, severity,
severityText, severityText,
body, body,
getImmutableAttributes()); getImmutableAttributes(),
attributes == null ? 0 : attributes.getTotalAddedValues());
} }
} }
} }

View File

@ -8,6 +8,7 @@ package io.opentelemetry.sdk.logs.data;
import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.logs.LogLimits;
import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.resources.Resource;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@ -44,4 +45,13 @@ public interface LogData {
/** Returns the attributes for this log, or {@link Attributes#empty()} if unset. */ /** Returns the attributes for this log, or {@link Attributes#empty()} if unset. */
Attributes getAttributes(); Attributes getAttributes();
/**
* Returns the total number of attributes that were recorded on this log.
*
* <p>This number may be larger than the number of attributes that are attached to this log, if
* the total number recorded was greater than the configured maximum value. See {@link
* LogLimits#getMaxNumberOfAttributes()}.
*/
int getTotalAttributeCount();
} }

View File

@ -17,6 +17,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.internal.StringUtils; import io.opentelemetry.api.internal.StringUtils;
@ -104,20 +105,20 @@ class SdkLogEmitterTest {
() -> LogLimits.builder().setMaxNumberOfAttributes(maxNumberOfAttrs).build()) () -> LogLimits.builder().setMaxNumberOfAttributes(maxNumberOfAttrs).build())
.build(); .build();
AttributesBuilder attributesBuilder = Attributes.builder(); LogRecordBuilder builder = logEmitterProvider.get("test").logRecordBuilder();
AttributesBuilder expectedAttributes = Attributes.builder();
for (int i = 0; i < 2 * maxNumberOfAttrs; i++) { for (int i = 0; i < 2 * maxNumberOfAttrs; i++) {
attributesBuilder.put("key" + i, i); AttributeKey<Long> key = AttributeKey.longKey("key" + i);
builder.setAttribute(key, (long) i);
if (i < maxNumberOfAttrs) {
expectedAttributes.put(key, (long) i);
} }
}
builder.emit();
logEmitterProvider assertThat(seenLog.get().toLogData())
.get("test") .hasAttributes(expectedAttributes.build())
.logRecordBuilder() .hasTotalAttributeCount(maxNumberOfAttrs * 2);
.setAllAttributes(attributesBuilder.build())
.emit();
// NOTE: cannot guarantee which attributes are retained, only that there are no more that the
// max
assertThat(seenLog.get().toLogData().getAttributes()).hasSize(maxNumberOfAttrs);
} }
@Test @Test