Repository: DataDog/dd-trace-py

Issue:
0.50 bug sanic APM
Thanks for taking the time for reporting an issue!

Before reporting an issue on dd-trace-py, please be sure to provide all
necessary information.

If you're hitting a bug, make sure that you're using the latest version of this
library.

### Which version of dd-trace-py are you using?
0.50.0

### Which version of pip are you using?
19.0.3

### Which version of the libraries are you using?
Package             Version
------------------- ---------
aiofiles            0.7.0
aiohttp             3.6.2
aiomysql            0.0.20
astroid             2.3.3
async-timeout       3.0.1
attrs               19.3.0
certifi             2019.9.11
cffi                1.13.2
chardet             3.0.4
Click               7.0
cryptography        2.8
ddtrace             0.50.0
Deprecated          1.2.11
elasticsearch       7.5.1
elasticsearch-async 6.2.0
h11                 0.8.1
h2                  3.1.1
hpack               3.0.0
hstspreload         2020.1.7
httpcore            0.3.0
httptools           0.0.13
httpx               0.9.3
hyperframe          5.2.0
idna                2.8
isort               4.3.21
lazy-object-proxy   1.4.3
mccabe              0.6.1
motor               2.4.0
multidict           5.1.0
packaging           21.0
peewee              3.13.1
pip                 19.0.3
protobuf            3.17.3
pycparser           2.19
PyJWT               1.7.1
pymongo             3.11.4
PyMySQL             0.9.2
pyparsing           2.4.7
pytz                2019.3
PyYAML              5.3
requests            2.22.0
requests-async      0.5.0
rfc3986             1.3.2
sanic               21.3.4
sanic-motor         0.5.0
sanic-routing       0.6.2
sanic-scheduler     1.0.7
setuptools          40.8.0
six                 1.14.0
sniffio             1.1.0
stringcase          1.2.0
tenacity            8.0.1
typed-ast           1.4.1
ujson               1.35
urllib3             1.25.6
uvloop              0.13.0
websockets          8.1
wrapt               1.11.2
yarl                1.4.2

### How can we reproduce your problem?
#### Description
It's not working patch when apply APM on Sanic
If path variable type is int on sanic route

Case code
```
@app.route('/<gam_id:int>/slot/count', methods=['GET'])
async def slot_count(request, gam_id):
    try:
        pass
    except Exception as e:
        abort(500, e)
    return json(response(200, 'Complete Successfully', {}))

```

Error
```
[2021-07-13 19:50:48 +0000] [13] [ERROR] Exception occurred while handling uri: 'http://xxxxxxxxx.xxx/25/slot/count'
NoneType: None

```

### What is the result that you get?
my production env is not working on Sanic


### What is the result that you expected?
I wanna use datadog APM on my production SANIC

I made already pull request
- https://github.com/DataDog/dd-trace-py/pull/2662



Discussion:
Why no reply?

Resolving patch:
diff --git a/ddtrace/contrib/sanic/patch.py b/ddtrace/contrib/sanic/patch.py
--- a/ddtrace/contrib/sanic/patch.py
+++ b/ddtrace/contrib/sanic/patch.py
@@ -78,6 +78,11 @@ def _get_path(request):
     except sanic.exceptions.SanicException:
         return path
     for key, value in match_info.items():
+        try:
+            value = str(value)
+        except Exception:
+            # Best effort
+            continue
         path = path.replace(value, f"<{key}>")
     return path
 


Test patch:
diff --git a/tests/contrib/sanic/test_sanic.py b/tests/contrib/sanic/test_sanic.py
--- a/tests/contrib/sanic/test_sanic.py
+++ b/tests/contrib/sanic/test_sanic.py
@@ -6,6 +6,7 @@
 from sanic import Sanic
 from sanic.config import DEFAULT_CONFIG
 from sanic.exceptions import ServerError
+from sanic.exceptions import abort
 from sanic.response import json
 from sanic.response import stream
 from sanic.response import text
@@ -88,6 +89,14 @@ async def invalid(request):
     async def empty(request):
         pass
 
+    @app.route("/<n:int>/count", methods=["GET"])
+    async def count(request, n):
+        try:
+            pass
+        except Exception as e:
+            abort(500, e)
+        return json({"hello": n})
+
     @app.exception(ServerError)
     def handler_exception(request, exception):
         return text(exception.args[0], exception.status_code)
@@ -339,3 +348,9 @@ async def test_http_request_header_tracing(tracer, client, test_spans):
     assert len(spans[0]) == 2
     request_span = spans[0][0]
     assert request_span.get_tag("http.request.headers.my-header") == "my_value"
+
+
+async def test_endpoint_with_numeric_arg(tracer, client, test_spans):
+    response = await client.get("/42/count")
+    assert _response_status(response) == 200
+    assert (await _response_text(response)) == '{"hello":42}'