Skip to content

Commit 4071f5e

Browse files
feat: add support for resolving first available attribute (#168)
1 parent e6fb786 commit 4071f5e

4 files changed

Lines changed: 112 additions & 8 deletions

File tree

hypertrace-trace-enricher/hypertrace-trace-enricher/src/main/resources/configs/common/application.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ kafka.streams.config = {
2424
}
2525

2626
enricher {
27-
names = ["SpanTypeAttributeEnricher", "ApiStatusEnricher", "EndpointEnricher", "TransactionNameEnricher", "ApiBoundaryTypeAttributeEnricher", "ErrorsAndExceptionsEnricher", "BackendEntityEnricher", "HttpAttributeEnricher", "DefaultServiceEntityEnricher", "UserAgentSpanEnricher", "SpaceEnricher", "ExitCallsEnricher"]
27+
names = ["SpanTypeAttributeEnricher", "ApiStatusEnricher", "EndpointEnricher", "TransactionNameEnricher", "ApiBoundaryTypeAttributeEnricher", "ErrorsAndExceptionsEnricher", "BackendEntityEnricher", "HttpAttributeEnricher", "DefaultServiceEntityEnricher", "UserAgentSpanEnricher", "SpaceEnricher", "EntitySpanEnricher", "ExitCallsEnricher"]
2828

2929
clients = {
3030
entity.service.config = {

hypertrace-trace-enricher/trace-reader/build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ plugins {
66
}
77

88
dependencies {
9-
api("org.hypertrace.core.attribute.service:attribute-service-api:0.10.5")
10-
api("org.hypertrace.core.attribute.service:caching-attribute-service-client:0.10.5")
9+
api("org.hypertrace.core.attribute.service:attribute-service-api:0.11.0")
10+
api("org.hypertrace.core.attribute.service:caching-attribute-service-client:0.11.0")
1111
api("org.hypertrace.entity.service:entity-type-service-rx-client:0.6.0")
1212
api("org.hypertrace.entity.service:entity-data-service-rx-client:0.6.0")
1313
api("org.hypertrace.core.datamodel:data-model:0.1.14")
14-
implementation("org.hypertrace.core.attribute.service:attribute-projection-registry:0.10.5")
14+
implementation("org.hypertrace.core.attribute.service:attribute-projection-registry:0.11.0")
1515
implementation("org.hypertrace.core.grpcutils:grpc-client-rx-utils:0.4.0")
1616
implementation("org.hypertrace.core.grpcutils:grpc-context-utils:0.4.0")
1717
implementation("io.reactivex.rxjava3:rxjava:3.0.11")

hypertrace-trace-enricher/trace-reader/src/main/java/org/hypertrace/trace/reader/attributes/DefaultValueResolver.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient;
1111
import org.hypertrace.core.attribute.service.projection.AttributeProjection;
1212
import org.hypertrace.core.attribute.service.projection.AttributeProjectionRegistry;
13+
import org.hypertrace.core.attribute.service.v1.AttributeDefinition;
14+
import org.hypertrace.core.attribute.service.v1.AttributeDefinition.AttributeDefinitions;
1315
import org.hypertrace.core.attribute.service.v1.AttributeDefinition.SourceField;
1416
import org.hypertrace.core.attribute.service.v1.AttributeKind;
1517
import org.hypertrace.core.attribute.service.v1.AttributeMetadata;
1618
import org.hypertrace.core.attribute.service.v1.AttributeType;
1719
import org.hypertrace.core.attribute.service.v1.LiteralValue;
20+
import org.hypertrace.core.attribute.service.v1.LiteralValue.ValueCase;
1821
import org.hypertrace.core.attribute.service.v1.Projection;
1922
import org.hypertrace.core.attribute.service.v1.ProjectionExpression;
2023

@@ -36,28 +39,49 @@ public Single<LiteralValue> resolve(
3639
return this.buildError("Attribute definition not set");
3740
}
3841

39-
switch (attributeMetadata.getDefinition().getValueCase()) {
42+
return this.resolveDefinition(
43+
valueSource, attributeMetadata, attributeMetadata.getDefinition());
44+
}
45+
46+
private Single<LiteralValue> resolveDefinition(
47+
ValueSource valueSource,
48+
AttributeMetadata attributeMetadata,
49+
AttributeDefinition definition) {
50+
51+
switch (definition.getValueCase()) {
4052
case SOURCE_PATH:
4153
return this.resolveValue(
4254
valueSource,
4355
attributeMetadata.getScopeString(),
4456
attributeMetadata.getType(),
4557
attributeMetadata.getValueKind(),
46-
attributeMetadata.getDefinition().getSourcePath());
58+
definition.getSourcePath());
4759
case PROJECTION:
4860
return this.resolveProjection(
4961
valueSource, attributeMetadata.getDefinition().getProjection());
5062
case SOURCE_FIELD:
5163
return this.resolveField(
64+
valueSource, definition.getSourceField(), attributeMetadata.getValueKind());
65+
case FIRST_VALUE_PRESENT:
66+
return this.resolveFirstValuePresent(
5267
valueSource,
53-
attributeMetadata.getDefinition().getSourceField(),
54-
attributeMetadata.getValueKind());
68+
attributeMetadata,
69+
attributeMetadata.getDefinition().getFirstValuePresent());
5570
case VALUE_NOT_SET:
5671
default:
5772
return this.buildError("Unrecognized attribute definition");
5873
}
5974
}
6075

76+
private Maybe<LiteralValue> maybeResolveDefinition(
77+
ValueSource valueSource,
78+
AttributeMetadata attributeMetadata,
79+
AttributeDefinition definition) {
80+
return this.resolveDefinition(valueSource, attributeMetadata, definition)
81+
.filter(literalValue -> !literalValue.getValueCase().equals(ValueCase.VALUE_NOT_SET))
82+
.onErrorComplete();
83+
}
84+
6185
private Single<LiteralValue> resolveValue(
6286
ValueSource contextValueSource,
6387
String attributeScope,
@@ -107,6 +131,17 @@ private Single<LiteralValue> resolveField(
107131
.defaultIfEmpty(LiteralValue.getDefaultInstance());
108132
}
109133

134+
private Single<LiteralValue> resolveFirstValuePresent(
135+
ValueSource valueSource,
136+
AttributeMetadata attributeMetadata,
137+
AttributeDefinitions definitions) {
138+
139+
return Observable.fromIterable(definitions.getDefinitionsList())
140+
.concatMapMaybe(
141+
definition -> this.maybeResolveDefinition(valueSource, attributeMetadata, definition))
142+
.first(LiteralValue.getDefaultInstance());
143+
}
144+
110145
private Single<LiteralValue> resolveExpression(
111146
ValueSource valueSource, ProjectionExpression expression) {
112147

hypertrace-trace-enricher/trace-reader/src/test/java/org/hypertrace/trace/reader/attributes/DefaultValueResolverTest.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.hypertrace.trace.reader.attributes;
22

33
import static org.hypertrace.trace.reader.attributes.AvroUtil.buildAttributesWithKeyValue;
4+
import static org.hypertrace.trace.reader.attributes.AvroUtil.buildAttributesWithKeyValues;
45
import static org.hypertrace.trace.reader.attributes.AvroUtil.buildMetricsWithKeyValue;
56
import static org.hypertrace.trace.reader.attributes.AvroUtil.defaultedEventBuilder;
67
import static org.hypertrace.trace.reader.attributes.AvroUtil.defaultedStructuredTraceBuilder;
@@ -11,13 +12,16 @@
1112
import static org.mockito.Mockito.when;
1213

1314
import io.reactivex.rxjava3.core.Single;
15+
import java.util.Map;
1416
import org.hypertrace.core.attribute.service.cachingclient.CachingAttributeClient;
1517
import org.hypertrace.core.attribute.service.projection.AttributeProjectionRegistry;
1618
import org.hypertrace.core.attribute.service.v1.AttributeDefinition;
19+
import org.hypertrace.core.attribute.service.v1.AttributeDefinition.AttributeDefinitions;
1720
import org.hypertrace.core.attribute.service.v1.AttributeDefinition.SourceField;
1821
import org.hypertrace.core.attribute.service.v1.AttributeKind;
1922
import org.hypertrace.core.attribute.service.v1.AttributeMetadata;
2023
import org.hypertrace.core.attribute.service.v1.AttributeType;
24+
import org.hypertrace.core.attribute.service.v1.LiteralValue;
2125
import org.hypertrace.core.attribute.service.v1.Projection;
2226
import org.hypertrace.core.attribute.service.v1.ProjectionExpression;
2327
import org.hypertrace.core.attribute.service.v1.ProjectionOperator;
@@ -242,4 +246,69 @@ void resolveFields() {
242246
.resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadataEndTime)
243247
.blockingGet());
244248
}
249+
250+
@Test
251+
void resolvesFirstAvailableDefinition() {
252+
AttributeMetadata metadata =
253+
AttributeMetadata.newBuilder()
254+
.setScopeString("TEST_SCOPE")
255+
.setType(AttributeType.ATTRIBUTE)
256+
.setValueKind(AttributeKind.TYPE_INT64)
257+
.setDefinition(
258+
AttributeDefinition.newBuilder()
259+
.setFirstValuePresent(
260+
AttributeDefinitions.newBuilder()
261+
.addDefinitions( // Should error due to data type
262+
AttributeDefinition.newBuilder().setSourcePath("path.to.string"))
263+
.addDefinitions( // Should be empty and skipped
264+
AttributeDefinition.newBuilder().setSourcePath("non.existent"))
265+
.addDefinitions(
266+
AttributeDefinition.newBuilder().setSourcePath("path.to.int"))
267+
.addDefinitions( // Shouldn't be reached
268+
AttributeDefinition.newBuilder()
269+
.setSourceField(SourceField.SOURCE_FIELD_START_TIME))))
270+
.build();
271+
272+
Event span =
273+
defaultedEventBuilder()
274+
.setAttributes(
275+
buildAttributesWithKeyValues(Map.of("path.to.string", "foo", "path.to.int", "14")))
276+
.build();
277+
278+
assertEquals(
279+
longLiteral(14),
280+
this.resolver
281+
.resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadata)
282+
.blockingGet());
283+
}
284+
285+
@Test
286+
void resolvesEmptyIfNoDefinitionAvailable() {
287+
AttributeMetadata metadata =
288+
AttributeMetadata.newBuilder()
289+
.setScopeString("TEST_SCOPE")
290+
.setType(AttributeType.ATTRIBUTE)
291+
.setValueKind(AttributeKind.TYPE_INT64)
292+
.setDefinition(
293+
AttributeDefinition.newBuilder()
294+
.setFirstValuePresent(
295+
AttributeDefinitions.newBuilder()
296+
.addDefinitions( // Should error due to data type
297+
AttributeDefinition.newBuilder().setSourcePath("path.to.string"))
298+
.addDefinitions( // Should be empty and skipped
299+
AttributeDefinition.newBuilder().setSourcePath("non.existent"))))
300+
.build();
301+
302+
Event span =
303+
defaultedEventBuilder()
304+
.setAttributes(
305+
buildAttributesWithKeyValues(Map.of("path.to.string", "foo", "path.to.int", "14")))
306+
.build();
307+
308+
assertEquals(
309+
LiteralValue.getDefaultInstance(),
310+
this.resolver
311+
.resolve(ValueSourceFactory.forSpan(this.mockStructuredTrace, span), metadata)
312+
.blockingGet());
313+
}
245314
}

0 commit comments

Comments
 (0)