|
| 1 | +import os |
| 2 | +import datetime |
1 | 3 | import asyncio
|
2 | 4 | import json
|
3 | 5 |
|
|
18 | 20 | )
|
19 | 21 |
|
20 | 22 | from sentry_sdk import capture_message, start_transaction
|
21 |
| -from sentry_sdk.integrations.aiohttp import AioHttpIntegration |
| 23 | +from sentry_sdk.integrations.aiohttp import AioHttpIntegration, create_trace_config |
| 24 | +from sentry_sdk.consts import SPANDATA |
22 | 25 | from tests.conftest import ApproxDict
|
23 | 26 |
|
24 | 27 |
|
@@ -633,6 +636,347 @@ async def handler(request):
|
633 | 636 | )
|
634 | 637 |
|
635 | 638 |
|
| 639 | +@pytest.mark.asyncio |
| 640 | +async def test_request_source_disabled( |
| 641 | + sentry_init, aiohttp_raw_server, aiohttp_client, capture_events |
| 642 | +): |
| 643 | + sentry_init( |
| 644 | + integrations=[AioHttpIntegration()], |
| 645 | + traces_sample_rate=1.0, |
| 646 | + enable_http_request_source=False, |
| 647 | + http_request_source_threshold_ms=0, |
| 648 | + ) |
| 649 | + |
| 650 | + # server for making span request |
| 651 | + async def handler(request): |
| 652 | + return web.Response(text="OK") |
| 653 | + |
| 654 | + raw_server = await aiohttp_raw_server(handler) |
| 655 | + |
| 656 | + async def hello(request): |
| 657 | + span_client = await aiohttp_client(raw_server) |
| 658 | + await span_client.get("/") |
| 659 | + return web.Response(text="hello") |
| 660 | + |
| 661 | + app = web.Application() |
| 662 | + app.router.add_get(r"/", hello) |
| 663 | + |
| 664 | + events = capture_events() |
| 665 | + |
| 666 | + client = await aiohttp_client(app) |
| 667 | + await client.get("/") |
| 668 | + |
| 669 | + (event,) = events |
| 670 | + |
| 671 | + span = event["spans"][-1] |
| 672 | + assert span["description"].startswith("GET") |
| 673 | + |
| 674 | + data = span.get("data", {}) |
| 675 | + |
| 676 | + assert SPANDATA.CODE_LINENO not in data |
| 677 | + assert SPANDATA.CODE_NAMESPACE not in data |
| 678 | + assert SPANDATA.CODE_FILEPATH not in data |
| 679 | + assert SPANDATA.CODE_FUNCTION not in data |
| 680 | + |
| 681 | + |
| 682 | +@pytest.mark.asyncio |
| 683 | +@pytest.mark.parametrize("enable_http_request_source", [None, True]) |
| 684 | +async def test_request_source_enabled( |
| 685 | + sentry_init, |
| 686 | + aiohttp_raw_server, |
| 687 | + aiohttp_client, |
| 688 | + capture_events, |
| 689 | + enable_http_request_source, |
| 690 | +): |
| 691 | + sentry_options = { |
| 692 | + "integrations": [AioHttpIntegration()], |
| 693 | + "traces_sample_rate": 1.0, |
| 694 | + "http_request_source_threshold_ms": 0, |
| 695 | + } |
| 696 | + if enable_http_request_source is not None: |
| 697 | + sentry_options["enable_http_request_source"] = enable_http_request_source |
| 698 | + |
| 699 | + sentry_init(**sentry_options) |
| 700 | + |
| 701 | + # server for making span request |
| 702 | + async def handler(request): |
| 703 | + return web.Response(text="OK") |
| 704 | + |
| 705 | + raw_server = await aiohttp_raw_server(handler) |
| 706 | + |
| 707 | + async def hello(request): |
| 708 | + span_client = await aiohttp_client(raw_server) |
| 709 | + await span_client.get("/") |
| 710 | + return web.Response(text="hello") |
| 711 | + |
| 712 | + app = web.Application() |
| 713 | + app.router.add_get(r"/", hello) |
| 714 | + |
| 715 | + events = capture_events() |
| 716 | + |
| 717 | + client = await aiohttp_client(app) |
| 718 | + await client.get("/") |
| 719 | + |
| 720 | + (event,) = events |
| 721 | + |
| 722 | + span = event["spans"][-1] |
| 723 | + assert span["description"].startswith("GET") |
| 724 | + |
| 725 | + data = span.get("data", {}) |
| 726 | + |
| 727 | + assert SPANDATA.CODE_LINENO in data |
| 728 | + assert SPANDATA.CODE_NAMESPACE in data |
| 729 | + assert SPANDATA.CODE_FILEPATH in data |
| 730 | + assert SPANDATA.CODE_FUNCTION in data |
| 731 | + |
| 732 | + |
| 733 | +@pytest.mark.asyncio |
| 734 | +async def test_request_source( |
| 735 | + sentry_init, aiohttp_raw_server, aiohttp_client, capture_events |
| 736 | +): |
| 737 | + sentry_init( |
| 738 | + integrations=[AioHttpIntegration()], |
| 739 | + traces_sample_rate=1.0, |
| 740 | + enable_http_request_source=True, |
| 741 | + http_request_source_threshold_ms=0, |
| 742 | + ) |
| 743 | + |
| 744 | + # server for making span request |
| 745 | + async def handler(request): |
| 746 | + return web.Response(text="OK") |
| 747 | + |
| 748 | + raw_server = await aiohttp_raw_server(handler) |
| 749 | + |
| 750 | + async def handler_with_outgoing_request(request): |
| 751 | + span_client = await aiohttp_client(raw_server) |
| 752 | + await span_client.get("/") |
| 753 | + return web.Response(text="hello") |
| 754 | + |
| 755 | + app = web.Application() |
| 756 | + app.router.add_get(r"/", handler_with_outgoing_request) |
| 757 | + |
| 758 | + events = capture_events() |
| 759 | + |
| 760 | + client = await aiohttp_client(app) |
| 761 | + await client.get("/") |
| 762 | + |
| 763 | + (event,) = events |
| 764 | + |
| 765 | + span = event["spans"][-1] |
| 766 | + assert span["description"].startswith("GET") |
| 767 | + |
| 768 | + data = span.get("data", {}) |
| 769 | + |
| 770 | + assert SPANDATA.CODE_LINENO in data |
| 771 | + assert SPANDATA.CODE_NAMESPACE in data |
| 772 | + assert SPANDATA.CODE_FILEPATH in data |
| 773 | + assert SPANDATA.CODE_FUNCTION in data |
| 774 | + |
| 775 | + assert type(data.get(SPANDATA.CODE_LINENO)) == int |
| 776 | + assert data.get(SPANDATA.CODE_LINENO) > 0 |
| 777 | + assert ( |
| 778 | + data.get(SPANDATA.CODE_NAMESPACE) == "tests.integrations.aiohttp.test_aiohttp" |
| 779 | + ) |
| 780 | + assert data.get(SPANDATA.CODE_FILEPATH).endswith( |
| 781 | + "tests/integrations/aiohttp/test_aiohttp.py" |
| 782 | + ) |
| 783 | + |
| 784 | + is_relative_path = data.get(SPANDATA.CODE_FILEPATH)[0] != os.sep |
| 785 | + assert is_relative_path |
| 786 | + |
| 787 | + assert data.get(SPANDATA.CODE_FUNCTION) == "handler_with_outgoing_request" |
| 788 | + |
| 789 | + |
| 790 | +@pytest.mark.asyncio |
| 791 | +async def test_request_source_with_module_in_search_path( |
| 792 | + sentry_init, aiohttp_raw_server, aiohttp_client, capture_events |
| 793 | +): |
| 794 | + """ |
| 795 | + Test that request source is relative to the path of the module it ran in |
| 796 | + """ |
| 797 | + sentry_init( |
| 798 | + integrations=[AioHttpIntegration()], |
| 799 | + traces_sample_rate=1.0, |
| 800 | + enable_http_request_source=True, |
| 801 | + http_request_source_threshold_ms=0, |
| 802 | + ) |
| 803 | + |
| 804 | + # server for making span request |
| 805 | + async def handler(request): |
| 806 | + return web.Response(text="OK") |
| 807 | + |
| 808 | + raw_server = await aiohttp_raw_server(handler) |
| 809 | + |
| 810 | + from aiohttp_helpers.helpers import get_request_with_client |
| 811 | + |
| 812 | + async def handler_with_outgoing_request(request): |
| 813 | + span_client = await aiohttp_client(raw_server) |
| 814 | + await get_request_with_client(span_client, "/") |
| 815 | + return web.Response(text="hello") |
| 816 | + |
| 817 | + app = web.Application() |
| 818 | + app.router.add_get(r"/", handler_with_outgoing_request) |
| 819 | + |
| 820 | + events = capture_events() |
| 821 | + |
| 822 | + client = await aiohttp_client(app) |
| 823 | + await client.get("/") |
| 824 | + |
| 825 | + (event,) = events |
| 826 | + |
| 827 | + span = event["spans"][-1] |
| 828 | + assert span["description"].startswith("GET") |
| 829 | + |
| 830 | + data = span.get("data", {}) |
| 831 | + |
| 832 | + assert SPANDATA.CODE_LINENO in data |
| 833 | + assert SPANDATA.CODE_NAMESPACE in data |
| 834 | + assert SPANDATA.CODE_FILEPATH in data |
| 835 | + assert SPANDATA.CODE_FUNCTION in data |
| 836 | + |
| 837 | + assert type(data.get(SPANDATA.CODE_LINENO)) == int |
| 838 | + assert data.get(SPANDATA.CODE_LINENO) > 0 |
| 839 | + assert data.get(SPANDATA.CODE_NAMESPACE) == "aiohttp_helpers.helpers" |
| 840 | + assert data.get(SPANDATA.CODE_FILEPATH) == "aiohttp_helpers/helpers.py" |
| 841 | + |
| 842 | + is_relative_path = data.get(SPANDATA.CODE_FILEPATH)[0] != os.sep |
| 843 | + assert is_relative_path |
| 844 | + |
| 845 | + assert data.get(SPANDATA.CODE_FUNCTION) == "get_request_with_client" |
| 846 | + |
| 847 | + |
| 848 | +@pytest.mark.asyncio |
| 849 | +async def test_no_request_source_if_duration_too_short( |
| 850 | + sentry_init, aiohttp_raw_server, aiohttp_client, capture_events |
| 851 | +): |
| 852 | + sentry_init( |
| 853 | + integrations=[AioHttpIntegration()], |
| 854 | + traces_sample_rate=1.0, |
| 855 | + enable_http_request_source=True, |
| 856 | + http_request_source_threshold_ms=100, |
| 857 | + ) |
| 858 | + |
| 859 | + # server for making span request |
| 860 | + async def handler(request): |
| 861 | + return web.Response(text="OK") |
| 862 | + |
| 863 | + raw_server = await aiohttp_raw_server(handler) |
| 864 | + |
| 865 | + async def handler_with_outgoing_request(request): |
| 866 | + span_client = await aiohttp_client(raw_server) |
| 867 | + await span_client.get("/") |
| 868 | + return web.Response(text="hello") |
| 869 | + |
| 870 | + app = web.Application() |
| 871 | + app.router.add_get(r"/", handler_with_outgoing_request) |
| 872 | + |
| 873 | + events = capture_events() |
| 874 | + |
| 875 | + def fake_create_trace_context(*args, **kwargs): |
| 876 | + trace_context = create_trace_config() |
| 877 | + |
| 878 | + async def overwrite_timestamps(session, trace_config_ctx, params): |
| 879 | + span = trace_config_ctx.span |
| 880 | + span.start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) |
| 881 | + span.timestamp = datetime.datetime(2024, 1, 1, microsecond=99999) |
| 882 | + |
| 883 | + trace_context.on_request_end.insert(0, overwrite_timestamps) |
| 884 | + |
| 885 | + return trace_context |
| 886 | + |
| 887 | + with mock.patch( |
| 888 | + "sentry_sdk.integrations.aiohttp.create_trace_config", |
| 889 | + fake_create_trace_context, |
| 890 | + ): |
| 891 | + client = await aiohttp_client(app) |
| 892 | + await client.get("/") |
| 893 | + |
| 894 | + (event,) = events |
| 895 | + |
| 896 | + span = event["spans"][-1] |
| 897 | + assert span["description"].startswith("GET") |
| 898 | + |
| 899 | + data = span.get("data", {}) |
| 900 | + |
| 901 | + assert SPANDATA.CODE_LINENO not in data |
| 902 | + assert SPANDATA.CODE_NAMESPACE not in data |
| 903 | + assert SPANDATA.CODE_FILEPATH not in data |
| 904 | + assert SPANDATA.CODE_FUNCTION not in data |
| 905 | + |
| 906 | + |
| 907 | +@pytest.mark.asyncio |
| 908 | +async def test_request_source_if_duration_over_threshold( |
| 909 | + sentry_init, aiohttp_raw_server, aiohttp_client, capture_events |
| 910 | +): |
| 911 | + sentry_init( |
| 912 | + integrations=[AioHttpIntegration()], |
| 913 | + traces_sample_rate=1.0, |
| 914 | + enable_http_request_source=True, |
| 915 | + http_request_source_threshold_ms=100, |
| 916 | + ) |
| 917 | + |
| 918 | + # server for making span request |
| 919 | + async def handler(request): |
| 920 | + return web.Response(text="OK") |
| 921 | + |
| 922 | + raw_server = await aiohttp_raw_server(handler) |
| 923 | + |
| 924 | + async def handler_with_outgoing_request(request): |
| 925 | + span_client = await aiohttp_client(raw_server) |
| 926 | + await span_client.get("/") |
| 927 | + return web.Response(text="hello") |
| 928 | + |
| 929 | + app = web.Application() |
| 930 | + app.router.add_get(r"/", handler_with_outgoing_request) |
| 931 | + |
| 932 | + events = capture_events() |
| 933 | + |
| 934 | + def fake_create_trace_context(*args, **kwargs): |
| 935 | + trace_context = create_trace_config() |
| 936 | + |
| 937 | + async def overwrite_timestamps(session, trace_config_ctx, params): |
| 938 | + span = trace_config_ctx.span |
| 939 | + span.start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) |
| 940 | + span.timestamp = datetime.datetime(2024, 1, 1, microsecond=100001) |
| 941 | + |
| 942 | + trace_context.on_request_end.insert(0, overwrite_timestamps) |
| 943 | + |
| 944 | + return trace_context |
| 945 | + |
| 946 | + with mock.patch( |
| 947 | + "sentry_sdk.integrations.aiohttp.create_trace_config", |
| 948 | + fake_create_trace_context, |
| 949 | + ): |
| 950 | + client = await aiohttp_client(app) |
| 951 | + await client.get("/") |
| 952 | + |
| 953 | + (event,) = events |
| 954 | + |
| 955 | + span = event["spans"][-1] |
| 956 | + assert span["description"].startswith("GET") |
| 957 | + |
| 958 | + data = span.get("data", {}) |
| 959 | + |
| 960 | + assert SPANDATA.CODE_LINENO in data |
| 961 | + assert SPANDATA.CODE_NAMESPACE in data |
| 962 | + assert SPANDATA.CODE_FILEPATH in data |
| 963 | + assert SPANDATA.CODE_FUNCTION in data |
| 964 | + |
| 965 | + assert type(data.get(SPANDATA.CODE_LINENO)) == int |
| 966 | + assert data.get(SPANDATA.CODE_LINENO) > 0 |
| 967 | + assert ( |
| 968 | + data.get(SPANDATA.CODE_NAMESPACE) == "tests.integrations.aiohttp.test_aiohttp" |
| 969 | + ) |
| 970 | + assert data.get(SPANDATA.CODE_FILEPATH).endswith( |
| 971 | + "tests/integrations/aiohttp/test_aiohttp.py" |
| 972 | + ) |
| 973 | + |
| 974 | + is_relative_path = data.get(SPANDATA.CODE_FILEPATH)[0] != os.sep |
| 975 | + assert is_relative_path |
| 976 | + |
| 977 | + assert data.get(SPANDATA.CODE_FUNCTION) == "handler_with_outgoing_request" |
| 978 | + |
| 979 | + |
636 | 980 | @pytest.mark.asyncio
|
637 | 981 | async def test_span_origin(
|
638 | 982 | sentry_init,
|
|
0 commit comments