Skip to content

Commit e566384

Browse files
committed
Simplify attributes for botocore instrumentation
1 parent 28c1331 commit e566384

File tree

3 files changed

+140
-187
lines changed

3 files changed

+140
-187
lines changed

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

+44-1
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@
5151
from wrapt import wrap_function_wrapper
5252

5353
from opentelemetry.instrumentation.boto.version import __version__
54-
from opentelemetry.instrumentation.botocore import add_span_arg_tags, unwrap
5554
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
55+
from opentelemetry.instrumentation.utils import unwrap
5656
from opentelemetry.sdk.trace import Resource
5757
from opentelemetry.trace import SpanKind, get_tracer
5858

@@ -204,3 +204,46 @@ def _patched_auth_request(self, original_func, instance, args, kwargs):
204204
args,
205205
kwargs,
206206
)
207+
208+
209+
def add_span_arg_tags(span, endpoint_name, args, args_names, args_traced):
210+
def truncate_arg_value(value, max_len=1024):
211+
"""Truncate values which are bytes and greater than `max_len`.
212+
Useful for parameters like "Body" in `put_object` operations.
213+
"""
214+
if isinstance(value, bytes) and len(value) > max_len:
215+
return b"..."
216+
217+
return value
218+
219+
def flatten_dict(dict_, sep=".", prefix=""):
220+
"""
221+
Returns a normalized dict of depth 1 with keys in order of embedding
222+
"""
223+
# adapted from https://stackoverflow.com/a/19647596
224+
return (
225+
{
226+
prefix + sep + k if prefix else k: v
227+
for kk, vv in dict_.items()
228+
for k, v in flatten_dict(vv, sep, kk).items()
229+
}
230+
if isinstance(dict_, dict)
231+
else {prefix: dict_}
232+
)
233+
234+
if not span.is_recording():
235+
return
236+
237+
if endpoint_name not in {"kms", "sts"}:
238+
tags = dict(
239+
(name, value)
240+
for (name, value) in zip(args_names, args)
241+
if name in args_traced
242+
)
243+
tags = flatten_dict(tags)
244+
for key, value in {
245+
k: truncate_arg_value(v)
246+
for k, v in tags.items()
247+
if k not in {"s3": ["params.Body"]}.get(endpoint_name, [])
248+
}.items():
249+
span.set_attribute(key, value)

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

+9-118
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,21 @@
5858

5959
from opentelemetry.instrumentation.botocore.version import __version__
6060
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
61+
from opentelemetry.instrumentation.utils import unwrap
6162
from opentelemetry.sdk.trace import Resource
6263
from opentelemetry.trace import SpanKind, get_tracer
6364

6465
logger = logging.getLogger(__name__)
6566

6667

6768
class BotocoreInstrumentor(BaseInstrumentor):
68-
"""A instrumentor for Botocore
69+
"""A instrumentor for Botocore.
6970
7071
See `BaseInstrumentor`
7172
"""
7273

7374
def _instrument(self, **kwargs):
7475

75-
# FIXME should the tracer provider be accessed via Configuration
76-
# instead?
7776
# pylint: disable=attribute-defined-outside-init
7877
self._tracer = get_tracer(
7978
__name__, __version__, kwargs.get("tracer_provider")
@@ -90,45 +89,18 @@ def _uninstrument(self, **kwargs):
9089

9190
def _patched_api_call(self, original_func, instance, args, kwargs):
9291

93-
endpoint_name = deep_getattr(instance, "_endpoint._endpoint_prefix")
92+
# pylint: disable=protected-access
93+
service_name = instance._service_model.service_name
94+
operation_name, _ = args
9495

9596
with self._tracer.start_as_current_span(
96-
"{}.command".format(endpoint_name), kind=SpanKind.CONSUMER,
97+
"{}".format(service_name), kind=SpanKind.CLIENT,
9798
) as span:
9899

99-
operation = None
100-
if args and span.is_recording():
101-
operation = args[0]
102-
span.resource = Resource(
103-
attributes={
104-
"endpoint": endpoint_name,
105-
"operation": operation.lower(),
106-
}
107-
)
108-
109-
else:
110-
span.resource = Resource(
111-
attributes={"endpoint": endpoint_name}
112-
)
113-
114-
add_span_arg_tags(
115-
span,
116-
endpoint_name,
117-
args,
118-
("action", "params", "path", "verb"),
119-
{"params", "path", "verb"},
120-
)
121-
122100
if span.is_recording():
123-
region_name = deep_getattr(instance, "meta.region_name")
124-
125-
meta = {
126-
"aws.agent": "botocore",
127-
"aws.operation": operation,
128-
"aws.region": region_name,
129-
}
130-
for key, value in meta.items():
131-
span.set_attribute(key, value)
101+
span.set_attribute("aws.operation", operation_name)
102+
span.set_attribute("aws.region", instance.meta.region_name)
103+
span.set_attribute("aws.service", service_name)
132104

133105
result = original_func(*args, **kwargs)
134106

@@ -137,86 +109,5 @@ def _patched_api_call(self, original_func, instance, args, kwargs):
137109
"http.status_code",
138110
result["ResponseMetadata"]["HTTPStatusCode"],
139111
)
140-
span.set_attribute(
141-
"retry_attempts",
142-
result["ResponseMetadata"]["RetryAttempts"],
143-
)
144112

145113
return result
146-
147-
148-
def unwrap(obj, attr):
149-
function = getattr(obj, attr, None)
150-
if (
151-
function
152-
and isinstance(function, ObjectProxy)
153-
and hasattr(function, "__wrapped__")
154-
):
155-
setattr(obj, attr, function.__wrapped__)
156-
157-
158-
def add_span_arg_tags(span, endpoint_name, args, args_names, args_traced):
159-
def truncate_arg_value(value, max_len=1024):
160-
"""Truncate values which are bytes and greater than `max_len`.
161-
Useful for parameters like "Body" in `put_object` operations.
162-
"""
163-
if isinstance(value, bytes) and len(value) > max_len:
164-
return b"..."
165-
166-
return value
167-
168-
def flatten_dict(dict_, sep=".", prefix=""):
169-
"""
170-
Returns a normalized dict of depth 1 with keys in order of embedding
171-
"""
172-
# adapted from https://stackoverflow.com/a/19647596
173-
return (
174-
{
175-
prefix + sep + k if prefix else k: v
176-
for kk, vv in dict_.items()
177-
for k, v in flatten_dict(vv, sep, kk).items()
178-
}
179-
if isinstance(dict_, dict)
180-
else {prefix: dict_}
181-
)
182-
183-
if not span.is_recording():
184-
return
185-
186-
if endpoint_name not in {"kms", "sts"}:
187-
tags = dict(
188-
(name, value)
189-
for (name, value) in zip(args_names, args)
190-
if name in args_traced
191-
)
192-
tags = flatten_dict(tags)
193-
for key, value in {
194-
k: truncate_arg_value(v)
195-
for k, v in tags.items()
196-
if k not in {"s3": ["params.Body"]}.get(endpoint_name, [])
197-
}.items():
198-
span.set_attribute(key, value)
199-
200-
201-
def deep_getattr(obj, attr_string, default=None):
202-
"""
203-
Returns the attribute of ``obj`` at the dotted path given by
204-
``attr_string``, if no such attribute is reachable, returns ``default``.
205-
206-
>>> deep_getattr(cass, "cluster")
207-
<cassandra.cluster.Cluster object at 0xa20c350
208-
209-
>>> deep_getattr(cass, "cluster.metadata.partitioner")
210-
u"org.apache.cassandra.dht.Murmur3Partitioner"
211-
212-
>>> deep_getattr(cass, "i.dont.exist", default="default")
213-
"default"
214-
"""
215-
attrs = attr_string.split(".")
216-
for attr in attrs:
217-
try:
218-
obj = getattr(obj, attr)
219-
except AttributeError:
220-
return default
221-
222-
return obj

0 commit comments

Comments
 (0)