Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ endif::[]
* Add S3 bucket and key name to OTel attributes {pull}1790[#1790]
* Implement partial transaction support in AWS lambda {pull}1784[#1784]
* Add instrumentation for redis.asyncio {pull}1807[#1807]
* Add support for urllib3 v2.0.1+ {pull}1822[#1822]

[float]
===== Bug fixes
Expand Down
7 changes: 6 additions & 1 deletion elasticapm/instrumentation/packages/urllib3.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ def update_headers(args, kwargs, instance, transaction, trace_parent):
:param trace_parent: the TraceParent object
:return: an (args, kwargs) tuple
"""
if len(args) >= 4 and args[3]:
from urllib3._version import __version__ as urllib3_version

if urllib3_version.startswith("2") and len(args) >= 5 and args[4]:
headers = args[4].copy()
args = tuple(itertools.chain((args[:4]), (headers,), args[5:]))
elif len(args) >= 4 and args[3]:
Comment on lines +64 to +69
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was initially surprised to see version dependent code here since urllib3 2.0 is supposed to be mostly compatible with urllib3 1.26. Looking more closely, I understand now that you need to set trace headers irrelevant of the way they were set by the caller (arg, kwarg, instance parameter). Since urllib3 2.0 added a body field, it moved headers from 4th positional parameter to 5th positional parameter, and you have to detect this. cc @sethmlarson

Is my understanding right? Should you update the docstring to mention urllib3 2.0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you understood correctly. I probably should throw a comment in there for better documentation.

headers = args[3].copy()
args = tuple(itertools.chain((args[:3]), (headers,), args[4:]))
elif "headers" in kwargs and kwargs["headers"]:
Expand Down
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ packages = find:
include_package_data = true
zip_safe = false
install_requires =
urllib3<2.0
urllib3!=2.0.0,<3.0.0
certifi
wrapt>=1.14.1
test_suite=tests
Expand Down Expand Up @@ -71,7 +71,7 @@ tests_require =
py-cpuinfo==3.3.0
statistics==1.0.3.5

urllib3<2.0
urllib3!=2.0.0,<3.0.0
certifi
Jinja2
Logbook
Expand Down
2 changes: 1 addition & 1 deletion tests/client/client_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ def test_check_server_version(elasticapm_client):
indirect=["elasticapm_client"],
)
def test_user_agent(elasticapm_client, expected):
assert elasticapm_client.get_user_agent() == "apm-agent-python/unknown (myapp{})".format(expected)
assert elasticapm_client.get_user_agent() == "apm-agent-python/{} (myapp{})".format(elasticapm.VERSION, expected)


def test_label_without_client():
Expand Down
7 changes: 6 additions & 1 deletion tests/client/exception_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,12 @@ def test_message_event(elasticapm_client):
assert "stacktrace" in event["log"]
# check that only frames from `tests` module are not marked as library frames
for frame in event["log"]["stacktrace"]:
assert frame["library_frame"] or frame["module"].startswith(("tests", "__main__")), (
# vscode's python extension runs tests using libraries from the
# extension's dir, which results in false positives here. This is where
# runpy, _pydevd, and debugpy come from.
assert frame["library_frame"] or frame["module"].startswith(
("tests", "__main__", "runpy", "_pydevd", "debugpy")
), (
frame["module"],
frame["abs_path"],
)
Expand Down
6 changes: 5 additions & 1 deletion tests/instrumentation/urllib3_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import pytest
import urllib3
from urllib3._version import __version__ as urllib3_version

from elasticapm.conf import constants
from elasticapm.conf.constants import SPAN, TRANSACTION
Expand Down Expand Up @@ -273,7 +274,10 @@ def test_instance_headers_are_respected(
headers={"instance": "true"} if instance_headers else None,
)
if header_arg:
args = ("GET", url, None, {"args": "true"})
if urllib3_version.startswith("2"):
args = ("GET", url, None, None, {"args": "true"})
else:
args = ("GET", url, None, {"args": "true"})
else:
args = ("GET", url)
if header_kwarg:
Expand Down
2 changes: 1 addition & 1 deletion tests/requirements/reqs-base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jsonschema==3.2.0 ; python_version == '3.6'
jsonschema==4.17.3 ; python_version > '3.6'


urllib3<2.0
urllib3!=2.0.0,<3.0.0
certifi
Logbook
mock
Expand Down