Skip to content

Commit 0ff1032

Browse files
authored
botocore: add genai metrics for bedrock extensions (open-telemetry#3326)
This add genai metrics regarding the operation duration and the tokens used to botocore bedrock extension.
1 parent 81eaea5 commit 0ff1032

File tree

8 files changed

+534
-18
lines changed

8 files changed

+534
-18
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2525
([#3275](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3275))
2626
- `opentelemetry-instrumentation-botocore` Add support for GenAI tool events
2727
([#3302](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3302))
28+
- `opentelemetry-instrumentation-botocore` Add support for GenAI metrics
29+
([#3326](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3326))
2830
- `opentelemetry-instrumentation` make it simpler to initialize auto-instrumentation programmatically
2931
([#3273](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3273))
3032
- Add `opentelemetry-instrumentation-vertexai>=2.0b0` to `opentelemetry-bootstrap`

instrumentation/opentelemetry-instrumentation-botocore/pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ classifiers = [
2626
"Programming Language :: Python :: 3.13",
2727
]
2828
dependencies = [
29-
"opentelemetry-api ~= 1.12",
29+
"opentelemetry-api ~= 1.30",
3030
"opentelemetry-instrumentation == 0.52b0.dev",
3131
"opentelemetry-semantic-conventions == 0.52b0.dev",
3232
"opentelemetry-propagator-aws-xray ~= 1.0",

instrumentation/opentelemetry-instrumentation-botocore/src/opentelemetry/instrumentation/botocore/__init__.py

+74-1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,38 @@ def response_hook(span, service_name, operation_name, result):
7676
)
7777
ec2 = self.session.create_client("ec2", region_name="us-west-2")
7878
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.
79111
"""
80112

81113
import logging
@@ -104,6 +136,7 @@ def response_hook(span, service_name, operation_name, result):
104136
suppress_http_instrumentation,
105137
unwrap,
106138
)
139+
from opentelemetry.metrics import Instrument, Meter, get_meter
107140
from opentelemetry.propagators.aws.aws_xray_propagator import AwsXRayPropagator
108141
from opentelemetry.semconv.trace import SpanAttributes
109142
from opentelemetry.trace import get_tracer
@@ -134,6 +167,10 @@ def _instrument(self, **kwargs):
134167
self._tracers = {}
135168
# event_loggers are lazy initialized per-extension in _get_event_logger
136169
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]] = {}
137174

138175
self.request_hook = kwargs.get("request_hook")
139176
self.response_hook = kwargs.get("response_hook")
@@ -144,6 +181,7 @@ def _instrument(self, **kwargs):
144181

145182
self.tracer_provider = kwargs.get("tracer_provider")
146183
self.event_logger_provider = kwargs.get("event_logger_provider")
184+
self.meter_provider = kwargs.get("meter_provider")
147185

148186
wrap_function_wrapper(
149187
"botocore.client",
@@ -201,6 +239,38 @@ def _get_event_logger(self, extension: _AwsSdkExtension):
201239

202240
return self._event_loggers[instrumentation_name]
203241

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+
204274
def _uninstrument(self, **kwargs):
205275
unwrap(BaseClient, "_make_api_call")
206276
unwrap(Endpoint, "prepare_request")
@@ -244,8 +314,11 @@ def _patched_api_call(self, original_func, instance, args, kwargs):
244314

245315
tracer = self._get_tracer(extension)
246316
event_logger = self._get_event_logger(extension)
317+
meter = self._get_meter(extension)
318+
metrics = self._get_metrics(extension, meter)
247319
instrumentor_ctx = _BotocoreInstrumentorContext(
248-
event_logger=event_logger
320+
event_logger=event_logger,
321+
metrics=metrics,
249322
)
250323
with tracer.start_as_current_span(
251324
call_context.span_name,

0 commit comments

Comments
 (0)