Skip to content

Commit 1feaefc

Browse files
committed
Implement genai events for bedrock (non-streaming)
1 parent 6daa5ff commit 1feaefc

File tree

13 files changed

+1309
-44
lines changed

13 files changed

+1309
-44
lines changed

instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts

+9
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,15 @@ testing {
147147
implementation("software.amazon.awssdk:bedrockruntime:2.25.63")
148148
}
149149
}
150+
151+
targets {
152+
all {
153+
testTask.configure {
154+
// TODO run tests both with and without genai message capture
155+
systemProperty("otel.instrumentation.genai.capture-message-content", "true")
156+
}
157+
}
158+
}
150159
}
151160
}
152161
}

instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/autoconfigure/AwsSdkSingletons.java

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public final class AwsSdkSingletons {
2424
.setMessagingReceiveInstrumentationEnabled(messagingReceiveInstrumentationEnabled())
2525
.setUseConfiguredPropagatorForMessaging(useMessagingPropagator())
2626
.setRecordIndividualHttpError(recordIndividualHttpError())
27+
.setGenaiCaptureMessageContent(genaiCaptureMessageContent())
2728
.build();
2829

2930
private static boolean hasAgentConfiguration() {
@@ -67,6 +68,11 @@ private static boolean recordIndividualHttpError() {
6768
"otel.instrumentation.aws-sdk.experimental-record-individual-http-error", false);
6869
}
6970

71+
private static boolean genaiCaptureMessageContent() {
72+
return getBoolean(
73+
"otel.instrumentation.genai.capture-message-content", false);
74+
}
75+
7076
private static boolean getBoolean(String name, boolean defaultValue) {
7177
if (HAS_INSTRUMENTATION_CONFIG) {
7278
return AgentInstrumentationConfig.get().getBoolean(name, defaultValue);

instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetry.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.opentelemetry.instrumentation.awssdk.v2_2;
77

88
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.api.logs.Logger;
910
import io.opentelemetry.context.propagation.TextMapPropagator;
1011
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
1112
import io.opentelemetry.instrumentation.awssdk.v2_2.internal.AwsSdkInstrumenterFactory;
@@ -56,10 +57,12 @@ public static AwsSdkTelemetryBuilder builder(OpenTelemetry openTelemetry) {
5657
private final Instrumenter<ExecutionAttributes, Response> producerInstrumenter;
5758
private final Instrumenter<ExecutionAttributes, Response> dynamoDbInstrumenter;
5859
private final Instrumenter<ExecutionAttributes, Response> bedrockRuntimeInstrumenter;
60+
private final Logger eventLogger;
5961
private final boolean captureExperimentalSpanAttributes;
6062
@Nullable private final TextMapPropagator messagingPropagator;
6163
private final boolean useXrayPropagator;
6264
private final boolean recordIndividualHttpError;
65+
private final boolean genAiCaptureMessageContent;
6366

6467
AwsSdkTelemetry(
6568
OpenTelemetry openTelemetry,
@@ -68,7 +71,8 @@ public static AwsSdkTelemetryBuilder builder(OpenTelemetry openTelemetry) {
6871
boolean useMessagingPropagator,
6972
boolean useXrayPropagator,
7073
boolean recordIndividualHttpError,
71-
boolean messagingReceiveInstrumentationEnabled) {
74+
boolean messagingReceiveInstrumentationEnabled,
75+
boolean genAiCaptureMessageContent) {
7276
this.useXrayPropagator = useXrayPropagator;
7377
this.messagingPropagator =
7478
useMessagingPropagator ? openTelemetry.getPropagators().getTextMapPropagator() : null;
@@ -88,8 +92,10 @@ public static AwsSdkTelemetryBuilder builder(OpenTelemetry openTelemetry) {
8892
this.producerInstrumenter = instrumenterFactory.producerInstrumenter();
8993
this.dynamoDbInstrumenter = instrumenterFactory.dynamoDbInstrumenter();
9094
this.bedrockRuntimeInstrumenter = instrumenterFactory.bedrockRuntimeInstrumenter();
95+
this.eventLogger = instrumenterFactory.eventLogger();
9196
this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes;
9297
this.recordIndividualHttpError = recordIndividualHttpError;
98+
this.genAiCaptureMessageContent = genAiCaptureMessageContent;
9399
}
94100

95101
/**
@@ -104,10 +110,12 @@ public ExecutionInterceptor newExecutionInterceptor() {
104110
producerInstrumenter,
105111
dynamoDbInstrumenter,
106112
bedrockRuntimeInstrumenter,
113+
eventLogger,
107114
captureExperimentalSpanAttributes,
108115
messagingPropagator,
109116
useXrayPropagator,
110-
recordIndividualHttpError);
117+
recordIndividualHttpError,
118+
genAiCaptureMessageContent);
111119
}
112120

113121
/**

instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AwsSdkTelemetryBuilder.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public final class AwsSdkTelemetryBuilder {
2424
private boolean recordIndividualHttpError;
2525
private boolean useXrayPropagator = true;
2626
private boolean messagingReceiveInstrumentationEnabled;
27+
private boolean genaiCaptureMessageContent;
2728

2829
AwsSdkTelemetryBuilder(OpenTelemetry openTelemetry) {
2930
this.openTelemetry = openTelemetry;
@@ -115,6 +116,18 @@ public AwsSdkTelemetryBuilder setMessagingReceiveInstrumentationEnabled(
115116
return this;
116117
}
117118

119+
/**
120+
* Set whether Generative AI events include full content of user and assistant messages.
121+
*
122+
* <p>Note that full content can have data privacy and size concerns and care should be taken when
123+
* enabling this.
124+
*/
125+
@CanIgnoreReturnValue
126+
public AwsSdkTelemetryBuilder setGenaiCaptureMessageContent(boolean genaiCaptureMessageContent) {
127+
this.genaiCaptureMessageContent = genaiCaptureMessageContent;
128+
return this;
129+
}
130+
118131
/**
119132
* Returns a new {@link AwsSdkTelemetry} with the settings of this {@link AwsSdkTelemetryBuilder}.
120133
*/
@@ -126,6 +139,7 @@ public AwsSdkTelemetry build() {
126139
useMessagingPropagator,
127140
useXrayPropagator,
128141
recordIndividualHttpError,
129-
messagingReceiveInstrumentationEnabled);
142+
messagingReceiveInstrumentationEnabled,
143+
genaiCaptureMessageContent);
130144
}
131145
}

instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/AwsSdkInstrumenterFactory.java

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import io.opentelemetry.api.OpenTelemetry;
1212
import io.opentelemetry.api.common.AttributesBuilder;
13+
import io.opentelemetry.api.logs.Logger;
1314
import io.opentelemetry.api.trace.Span;
1415
import io.opentelemetry.context.Context;
1516
import io.opentelemetry.context.propagation.TextMapPropagator;
@@ -236,6 +237,10 @@ public Instrumenter<ExecutionAttributes, Response> bedrockRuntimeInstrumenter()
236237
true);
237238
}
238239

240+
public Logger eventLogger() {
241+
return openTelemetry.getLogsBridge().get(INSTRUMENTATION_NAME);
242+
}
243+
239244
private static <REQUEST, RESPONSE> Instrumenter<REQUEST, RESPONSE> createInstrumenter(
240245
OpenTelemetry openTelemetry,
241246
SpanNameExtractor<REQUEST> spanNameExtractor,

instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAccess.java

+28
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package io.opentelemetry.instrumentation.awssdk.v2_2.internal;
77

8+
import io.opentelemetry.api.logs.Logger;
9+
import io.opentelemetry.context.Context;
810
import io.opentelemetry.javaagent.tooling.muzzle.NoMuzzle;
911
import java.util.List;
1012
import javax.annotation.Nullable;
@@ -37,6 +39,11 @@ static boolean isBedrockRuntimeRequest(SdkRequest request) {
3739
return enabled && BedrockRuntimeImpl.isBedrockRuntimeRequest(request);
3840
}
3941

42+
@NoMuzzle
43+
static boolean isBedrockRuntimeResponse(SdkResponse response) {
44+
return enabled && BedrockRuntimeImpl.isBedrockRuntimeResponse(response);
45+
}
46+
4047
@Nullable
4148
@NoMuzzle
4249
static String getModelId(SdkRequest request) {
@@ -84,4 +91,25 @@ static Long getUsageInputTokens(SdkResponse response) {
8491
static Long getUsageOutputTokens(SdkResponse response) {
8592
return enabled ? BedrockRuntimeImpl.getUsageOutputTokens(response) : null;
8693
}
94+
95+
@NoMuzzle
96+
static void recordRequestEvents(
97+
Context otelContext, Logger eventLogger, SdkRequest request, boolean captureMessageContent) {
98+
if (enabled) {
99+
BedrockRuntimeImpl.recordRequestEvents(
100+
otelContext, eventLogger, request, captureMessageContent);
101+
}
102+
}
103+
104+
@NoMuzzle
105+
static void recordResponseEvents(
106+
Context otelContext,
107+
Logger eventLogger,
108+
SdkResponse response,
109+
boolean captureMessageContent) {
110+
if (enabled) {
111+
BedrockRuntimeImpl.recordResponseEvents(
112+
otelContext, eventLogger, response, captureMessageContent);
113+
}
114+
}
87115
}

instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/BedrockRuntimeAttributesGetter.java

+17-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.opentelemetry.instrumentation.awssdk.v2_2.internal;
77

88
import static io.opentelemetry.instrumentation.awssdk.v2_2.internal.TracingExecutionInterceptor.SDK_REQUEST_ATTRIBUTE;
9+
import static java.util.Collections.emptyList;
910

1011
import io.opentelemetry.instrumentation.api.incubator.semconv.genai.GenAiAttributesGetter;
1112
import java.util.Arrays;
@@ -25,7 +26,7 @@ private static final class GenAiOperationNameIncubatingValues {
2526
private GenAiOperationNameIncubatingValues() {}
2627
}
2728

28-
private static final class GenAiSystemIncubatingValues {
29+
static final class GenAiSystemIncubatingValues {
2930
static final String AWS_BEDROCK = "aws.bedrock";
3031

3132
private GenAiSystemIncubatingValues() {}
@@ -116,10 +117,13 @@ public Double getRequestTopP(ExecutionAttributes executionAttributes) {
116117
@Nullable
117118
@Override
118119
public List<String> getResponseFinishReasons(
119-
ExecutionAttributes executionAttributes, Response response) {
120+
ExecutionAttributes executionAttributes, @Nullable Response response) {
121+
if (response == null) {
122+
return emptyList();
123+
}
120124
String stopReason = BedrockRuntimeAccess.getStopReason(response.getSdkResponse());
121125
if (stopReason == null) {
122-
return null;
126+
return emptyList();
123127
}
124128
return Arrays.asList(stopReason);
125129
}
@@ -138,13 +142,21 @@ public String getResponseModel(ExecutionAttributes executionAttributes, Response
138142

139143
@Nullable
140144
@Override
141-
public Long getUsageInputTokens(ExecutionAttributes executionAttributes, Response response) {
145+
public Long getUsageInputTokens(
146+
ExecutionAttributes executionAttributes, @Nullable Response response) {
147+
if (response == null) {
148+
return null;
149+
}
142150
return BedrockRuntimeAccess.getUsageInputTokens(response.getSdkResponse());
143151
}
144152

145153
@Nullable
146154
@Override
147-
public Long getUsageOutputTokens(ExecutionAttributes executionAttributes, Response response) {
155+
public Long getUsageOutputTokens(
156+
ExecutionAttributes executionAttributes, @Nullable Response response) {
157+
if (response == null) {
158+
return null;
159+
}
148160
return BedrockRuntimeAccess.getUsageOutputTokens(response.getSdkResponse());
149161
}
150162
}

0 commit comments

Comments
 (0)