@@ -76,6 +76,38 @@ def response_hook(span, service_name, operation_name, result):
76
76
)
77
77
ec2 = self.session.create_client("ec2", region_name="us-west-2")
78
78
ec2.describe_instances()
79
+
80
+ Extensions
81
+ ----------
82
+
83
+ The instrumentation supports creating extensions for AWS services for enriching what is collected. We have extensions
84
+ for the following AWS services:
85
+
86
+ - Bedrock Runtime
87
+ - DynamoDB
88
+ - Lambda
89
+ - SNS
90
+ - SQS
91
+
92
+ Bedrock Runtime
93
+ ***************
94
+
95
+ This extension implements the GenAI semantic conventions for the following API calls:
96
+
97
+ - Converse
98
+ - ConverseStream
99
+ - InvokeModel
100
+ - InvokeModelWithResponseStream
101
+
102
+ For the Converse and ConverseStream APIs tracing, events and metrics are implemented.
103
+
104
+ For the InvokeModel and InvokeModelWithResponseStream APIs tracing, events and metrics implemented only for a subset of
105
+ the available models, namely:
106
+ - Amazon Titan models
107
+ - Amazon Nova models
108
+ - Anthropic Claude
109
+
110
+ There is no support for tool calls with Amazon Models for the InvokeModel and InvokeModelWithResponseStream APIs.
79
111
"""
80
112
81
113
import logging
@@ -104,6 +136,7 @@ def response_hook(span, service_name, operation_name, result):
104
136
suppress_http_instrumentation ,
105
137
unwrap ,
106
138
)
139
+ from opentelemetry .metrics import Instrument , Meter , get_meter
107
140
from opentelemetry .propagators .aws .aws_xray_propagator import AwsXRayPropagator
108
141
from opentelemetry .semconv .trace import SpanAttributes
109
142
from opentelemetry .trace import get_tracer
@@ -134,6 +167,10 @@ def _instrument(self, **kwargs):
134
167
self ._tracers = {}
135
168
# event_loggers are lazy initialized per-extension in _get_event_logger
136
169
self ._event_loggers = {}
170
+ # meters are lazy initialized per-extension in _get_meter
171
+ self ._meters = {}
172
+ # metrics are lazy initialized per-extension in _get_metrics
173
+ self ._metrics : Dict [str , Dict [str , Instrument ]] = {}
137
174
138
175
self .request_hook = kwargs .get ("request_hook" )
139
176
self .response_hook = kwargs .get ("response_hook" )
@@ -144,6 +181,7 @@ def _instrument(self, **kwargs):
144
181
145
182
self .tracer_provider = kwargs .get ("tracer_provider" )
146
183
self .event_logger_provider = kwargs .get ("event_logger_provider" )
184
+ self .meter_provider = kwargs .get ("meter_provider" )
147
185
148
186
wrap_function_wrapper (
149
187
"botocore.client" ,
@@ -201,6 +239,38 @@ def _get_event_logger(self, extension: _AwsSdkExtension):
201
239
202
240
return self ._event_loggers [instrumentation_name ]
203
241
242
+ def _get_meter (self , extension : _AwsSdkExtension ):
243
+ """This is a multiplexer in order to have a meter per extension"""
244
+
245
+ instrumentation_name = self ._get_instrumentation_name (extension )
246
+ meter = self ._meters .get (instrumentation_name )
247
+ if meter :
248
+ return meter
249
+
250
+ schema_version = extension .meter_schema_version ()
251
+ self ._meters [instrumentation_name ] = get_meter (
252
+ instrumentation_name ,
253
+ "" ,
254
+ schema_url = f"https://opentelemetry.io/schemas/{ schema_version } " ,
255
+ meter_provider = self .meter_provider ,
256
+ )
257
+
258
+ return self ._meters [instrumentation_name ]
259
+
260
+ def _get_metrics (
261
+ self , extension : _AwsSdkExtension , meter : Meter
262
+ ) -> Dict [str , Instrument ]:
263
+ """This is a multiplexer for lazy initialization of metrics required by extensions"""
264
+ instrumentation_name = self ._get_instrumentation_name (extension )
265
+ metrics = self ._metrics .get (instrumentation_name )
266
+ if metrics is not None :
267
+ return metrics
268
+
269
+ self ._metrics .setdefault (instrumentation_name , {})
270
+ metrics = self ._metrics [instrumentation_name ]
271
+ _safe_invoke (extension .setup_metrics , meter , metrics )
272
+ return metrics
273
+
204
274
def _uninstrument (self , ** kwargs ):
205
275
unwrap (BaseClient , "_make_api_call" )
206
276
unwrap (Endpoint , "prepare_request" )
@@ -244,8 +314,11 @@ def _patched_api_call(self, original_func, instance, args, kwargs):
244
314
245
315
tracer = self ._get_tracer (extension )
246
316
event_logger = self ._get_event_logger (extension )
317
+ meter = self ._get_meter (extension )
318
+ metrics = self ._get_metrics (extension , meter )
247
319
instrumentor_ctx = _BotocoreInstrumentorContext (
248
- event_logger = event_logger
320
+ event_logger = event_logger ,
321
+ metrics = metrics ,
249
322
)
250
323
with tracer .start_as_current_span (
251
324
call_context .span_name ,
0 commit comments