-
Notifications
You must be signed in to change notification settings - Fork 682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
aiohttp client #421
aiohttp client #421
Conversation
2d8b1ec
to
ff58602
Compare
I think that async with session.get(...) as response:
# this code out of the span
... Currently, trace hooks track the life-time of async with session.get(...) as response:
# now ClientRequest is closed.
async for chunk in response.content:
# this code out of the span
... I tried to find a proper way to hook it, but class ClientResponseWithTracing(aiohttp.ClientResponse):
def _start_span(self):
ctx = tracer.start_as_current_span(self.url, kind=SpanKind.CLIENT)
span_exit = ctx.__exit__
span: Span = ctx.__enter__()
setattr(self, '_span', span)
setattr(self, '_exit_span', span_exit)
def _release_span(self):
if not hasattr(self, '_span'):
return
span: Span = getattr(self, '_span')
exc_val: Optional[BaseException] = self.content.exception()
if exc_val is not None:
add_exception_to_events(exc_val, span)
exc_type, exc_tb = type(exc_val), exc_val.__traceback__
span.set_status(Status(StatusCanonicalCode.INTERNAL, exc_type.__name__))
getattr(self, '_exit_span')(exc_type, exc_val, exc_tb)
else:
getattr(self, '_exit_span')(None, None, None)
delattr(self, '_span')
def close(self):
self._release_span()
super().close()
def release(self) -> Any:
self._release_span()
super().release()
async def on_request_end_hook(session, trace_config_ctx, params):
...
params.response._start_span() |
Calling This raises an interesting question: What should be included in the span itself? E.g. if the server returns a non-JSON response, but the client tries to parse it as JSON (e.g. calling |
I want to pay attention to the stream queries. We can receive status 200 and after that, the stream can suddenly close with I have been a bit hasty when passing any exception to if isinstance(exc_val, aiohttp.ClientError) or isinstance(exc_val, json.JSONDecodeError):
add_exception_to_events(exc_val, span)
exc_type, exc_tb = type(exc_val), exc_val.__traceback__
span.set_status(Status(StatusCanonicalCode.INTERNAL, exc_type.__name__))
getattr(self, '_exit_span')(exc_type, exc_val, exc_tb)
else:
getattr(self, '_exit_span')(None, None, None) |
Yes, you right. I tested with the default behavior when you call
Maybe OT-spec will state something about that in the future. |
ff58602
to
d8160cd
Compare
@joshuahlang is this ready for review? |
17f907f
to
0384670
Compare
There's a few open issues in the comments already, but does can be addressed in the formal review. I'll remove the |
cda2d24
to
958aa88
Compare
Ready now. Still an open need to update the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a couple minor discussions on response code standards, but looks good otherwise!
ext/opentelemetry-ext-aiohttp-client/src/opentelemetry/ext/aiohttp_client/__init__.py
Outdated
Show resolved
Hide resolved
ext/opentelemetry-ext-aiohttp-client/src/opentelemetry/ext/aiohttp_client/__init__.py
Outdated
Show resolved
Hide resolved
ext/opentelemetry-ext-aiohttp-client/tests/test_aiohttp_client_integration.py
Outdated
Show resolved
Hide resolved
) | ||
self.InMemoryExporter.clear() | ||
|
||
def test_url_filter_option(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe query parameters should be a part of the atttribute:
Full HTTP request URL in the form scheme://host[:port]/path?query[#fragment]. Usually the fragment is not transmitted over HTTP, but if it is known, it should be included nevertheless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the client wants to strip them for whatever reason, then they should be removed from the span completely. This is merely testing that a client-provided callback is actually called and used to process the URL before adding it to the span. In this case it simply removes all query params, but likely clients will use it to selectively remove API keys or PII from query params.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah ok. a little nervous that the test is one that causes behavior which violates the spec, but sounds good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the spec should be updated to account for this? Some things still pass secrets or PII in query strings. May be good to acknowledge that in the spec?
This is an interesting one. in that context, what does
Agree with @GoPavel that this is a good discussion for the spec. But today, HTTP instrumentation do not concern themselves with handling mismatched content types, or anything to do with processing the response, except for the timing. One major implementation philosophy of OpenTelemetry is to not interfere with the application runtime as much as possible. I think content type validation would be significant overhead we don't want to be involved in. |
35c0d76
to
d329d55
Compare
d329d55
to
311ba67
Compare
This module is only supported on Python3.5, which is the oldest supported by aiohttp.
311ba67
to
1d51ed2
Compare
@toumorokoshi Had some time this weekend to rebase this off the latest in OTel. |
@joshuahlang great! Looks like CI is still failing but seems unrelated to your changes, taking a look |
ext/opentelemetry-ext-aiohttp-client/src/opentelemetry/ext/aiohttp_client/version.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for coming back to this! I've added some minor comments but overall it looks great. I'm requesting changes because of the following missing files: CHANGELOG.md, LICENSE, MANIFEST.in.
Hopefully they'll be pretty quick to add, you can see an example here: https://github.com/open-telemetry/opentelemetry-python/tree/master/ext/opentelemetry-ext-requests
ext/opentelemetry-ext-aiohttp-client/src/opentelemetry/ext/aiohttp_client/__init__.py
Outdated
Show resolved
Hide resolved
ext/opentelemetry-ext-aiohttp-client/src/opentelemetry/ext/aiohttp_client/__init__.py
Outdated
Show resolved
Hide resolved
ext/opentelemetry-ext-aiohttp-client/tests/test_aiohttp_client_integration.py
Outdated
Show resolved
Hide resolved
568d522
to
cc3cdb6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for the record, we are not adding instrumentations (previously known as integrations) as children of BaseInstrumentor
. Nevertheless, this can be approved now and migrated later to an instrumentation if that is more convenient, I guess.
|
||
|
||
# TODO: refactor this code to some common utility | ||
def http_status_to_canonical_code(status: int) -> StatusCanonicalCode: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code looks very similar to this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does, hence the TODO
. Not sure where it could be shared from though. opentelemetry-sdk
perhaps? I'm not very familiar with the OTel project structure
elif callable(trace_config_ctx.span_name): | ||
request_span_name = str(trace_config_ctx.span_name(params)) | ||
else: | ||
request_span_name = str(trace_config_ctx.span_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it really necessary to have an argument that can be either a callable or a string? Is it feasible that the span_name
argument is only a string type argument and leave the processing of params
(if necessary) to the caller of on_request_start
who will have the responsibility of providing the span name to on_request_start
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on_request_start
is called by aiohttp
internally. aiohttp
doesn't know the caller's context (e.g. that a request has a sensitive value in the query string). The higher-level caller instrumenting aiohttp.client
doesn't have access to mutate the params
directly. params
is an internal construct used by aiohttp
for tracing.
ext/opentelemetry-ext-aiohttp-client/src/opentelemetry/ext/aiohttp_client/__init__.py
Outdated
Show resolved
Hide resolved
ext/opentelemetry-ext-aiohttp-client/tests/test_aiohttp_client_integration.py
Outdated
Show resolved
Hide resolved
14548cc
to
76600a2
Compare
dbdaff3
to
641cdbe
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like only a couple of unused imports left to cleanup in the tests, otherwise this is looking good. Thanks!
210ef13
to
579ce7a
Compare
@joshuahlang thanks so much for all the work! Exciting to get this in. |
Adding initial aiohttp client. This module is only supported on Python3.5, which is the oldest supported by aiohttp. Co-authored-by: Yusuke Tsutsumi <yusuke@tsutsumi.io>
* chore: update README * chore: update README * fix: review comments
Initial attempt at an aiohttp-client integration. Still needs some work to document the usage at a minimum.