diff --git a/.riot/requirements/1099f43.txt b/.riot/requirements/1099f43.txt deleted file mode 100644 index 14b9dabeecd..00000000000 --- a/.riot/requirements/1099f43.txt +++ /dev/null @@ -1,20 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile --no-annotate .riot/requirements/1099f43.in -# -attrs==23.1.0 -coverage[toml]==7.3.4 -hypothesis==6.45.0 -iniconfig==2.0.0 -mock==5.1.0 -opentracing==2.4.0 -packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 -pytest-cov==4.1.0 -pytest-mock==3.12.0 -pytest-randomly==3.15.0 -sortedcontainers==2.4.0 -tornado==5.1.1 diff --git a/.riot/requirements/116a9e0.txt b/.riot/requirements/116a9e0.txt deleted file mode 100644 index b3b91aa0739..00000000000 --- a/.riot/requirements/116a9e0.txt +++ /dev/null @@ -1,22 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.10 -# by the following command: -# -# pip-compile --no-annotate .riot/requirements/116a9e0.in -# -attrs==23.1.0 -coverage[toml]==7.3.4 -exceptiongroup==1.2.0 -hypothesis==6.45.0 -iniconfig==2.0.0 -mock==5.1.0 -opentracing==2.4.0 -packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 -pytest-cov==4.1.0 -pytest-mock==3.12.0 -pytest-randomly==3.15.0 -sortedcontainers==2.4.0 -tomli==2.0.1 -tornado==4.5.3 diff --git a/.riot/requirements/17298d6.txt b/.riot/requirements/17298d6.txt deleted file mode 100644 index e9bbd296193..00000000000 --- a/.riot/requirements/17298d6.txt +++ /dev/null @@ -1,24 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# -# pip-compile --no-annotate .riot/requirements/17298d6.in -# -attrs==23.1.0 -coverage[toml]==7.3.4 -exceptiongroup==1.2.0 -hypothesis==6.45.0 -importlib-metadata==7.0.0 -iniconfig==2.0.0 -mock==5.1.0 -opentracing==2.4.0 -packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 -pytest-cov==4.1.0 -pytest-mock==3.12.0 -pytest-randomly==3.15.0 -sortedcontainers==2.4.0 -tomli==2.0.1 -tornado==4.5.3 -zipp==3.17.0 diff --git a/.riot/requirements/18f2a74.txt b/.riot/requirements/18f2a74.txt deleted file mode 100644 index b7ad296f5ae..00000000000 --- a/.riot/requirements/18f2a74.txt +++ /dev/null @@ -1,20 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile --no-annotate .riot/requirements/18f2a74.in -# -attrs==23.1.0 -coverage[toml]==7.3.4 -hypothesis==6.45.0 -iniconfig==2.0.0 -mock==5.1.0 -opentracing==2.4.0 -packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 -pytest-cov==4.1.0 -pytest-mock==3.12.0 -pytest-randomly==3.15.0 -sortedcontainers==2.4.0 -tornado==4.5.3 diff --git a/.riot/requirements/1d03e99.txt b/.riot/requirements/1d03e99.txt deleted file mode 100644 index 82e7adb4b4e..00000000000 --- a/.riot/requirements/1d03e99.txt +++ /dev/null @@ -1,22 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.10 -# by the following command: -# -# pip-compile --no-annotate .riot/requirements/1d03e99.in -# -attrs==23.1.0 -coverage[toml]==7.3.4 -exceptiongroup==1.2.0 -hypothesis==6.45.0 -iniconfig==2.0.0 -mock==5.1.0 -opentracing==2.4.0 -packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 -pytest-cov==4.1.0 -pytest-mock==3.12.0 -pytest-randomly==3.15.0 -sortedcontainers==2.4.0 -tomli==2.0.1 -tornado==5.1.1 diff --git a/.riot/requirements/aaa8f63.txt b/.riot/requirements/1da9e5b.txt similarity index 53% rename from .riot/requirements/aaa8f63.txt rename to .riot/requirements/1da9e5b.txt index ade552f5cd7..47d73b16717 100644 --- a/.riot/requirements/aaa8f63.txt +++ b/.riot/requirements/1da9e5b.txt @@ -2,10 +2,10 @@ # This file is autogenerated by pip-compile with Python 3.9 # by the following command: # -# pip-compile --allow-unsafe --no-annotate --resolver=backtracking .riot/requirements/aaa8f63.in +# pip-compile --allow-unsafe --no-annotate .riot/requirements/1da9e5b.in # -attrs==25.3.0 -coverage[toml]==7.8.2 +attrs==25.4.0 +coverage[toml]==7.10.7 exceptiongroup==1.3.0 hypothesis==6.45.0 importlib-metadata==8.7.0 @@ -15,11 +15,11 @@ opentracing==2.4.0 packaging==25.0 pluggy==1.6.0 pytest==8.0.0 -pytest-cov==6.1.1 -pytest-mock==3.14.1 -pytest-randomly==3.16.0 +pytest-cov==7.0.0 +pytest-mock==3.15.1 +pytest-randomly==4.0.1 sortedcontainers==2.4.0 -tomli==2.2.1 -tornado==6.0.4 -typing-extensions==4.14.0 +tomli==2.3.0 +tornado==6.1 +typing-extensions==4.15.0 zipp==3.23.0 diff --git a/.riot/requirements/2249718.txt b/.riot/requirements/2249718.txt deleted file mode 100644 index e7b5613ef40..00000000000 --- a/.riot/requirements/2249718.txt +++ /dev/null @@ -1,24 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# -# pip-compile --no-annotate .riot/requirements/2249718.in -# -attrs==23.1.0 -coverage[toml]==7.3.4 -exceptiongroup==1.2.0 -hypothesis==6.45.0 -importlib-metadata==7.0.0 -iniconfig==2.0.0 -mock==5.1.0 -opentracing==2.4.0 -packaging==23.2 -pluggy==1.3.0 -pytest==7.4.3 -pytest-cov==4.1.0 -pytest-mock==3.12.0 -pytest-randomly==3.15.0 -sortedcontainers==2.4.0 -tomli==2.0.1 -tornado==5.1.1 -zipp==3.17.0 diff --git a/ddtrace/contrib/integration_registry/registry.yaml b/ddtrace/contrib/integration_registry/registry.yaml index 2d0900d3431..fde6e910528 100644 --- a/ddtrace/contrib/integration_registry/registry.yaml +++ b/ddtrace/contrib/integration_registry/registry.yaml @@ -874,7 +874,7 @@ integrations: - tornado tested_versions_by_dependency: tornado: - min: 6.0.4 + min: "6.1" max: 6.5.1 - integration_name: unittest diff --git a/ddtrace/contrib/internal/tornado/__init__.py b/ddtrace/contrib/internal/tornado/__init__.py index e69de29bb2d..81213100c59 100644 --- a/ddtrace/contrib/internal/tornado/__init__.py +++ b/ddtrace/contrib/internal/tornado/__init__.py @@ -0,0 +1,99 @@ +r""" +The Tornado integration traces all ``RequestHandler`` defined in a Tornado web application. +Auto instrumentation is available using the ``patch`` function that **must be called before** +importing the tornado library. + +This integration supports Tornado >=6.1, which is asyncio-based. The integration properly +handles async/await coroutines and functions that return Futures, ensuring accurate span +durations and correct context propagation. + +The following is an example:: + + # patch before importing tornado and concurrent.futures + import asyncio + import tornado.web + import tornado.httpserver + + class MainHandler(tornado.web.RequestHandler): + def get(self): + self.write("Hello, world") + + async def main(): + app = tornado.web.Application([ + (r"/", MainHandler), + ]) + server = tornado.httpserver.HTTPServer(app) + server.listen(8888) + + # Let the asyncio loop run forever + await asyncio.Event().wait() + + if __name__ == "__main__": + asyncio.run(main()) + +When any type of ``RequestHandler`` is hit, a request root span is automatically created. If +you want to trace more parts of your application, you can use the ``wrap()`` decorator and +the ``trace()`` method as usual:: + + class MainHandler(tornado.web.RequestHandler): + async def get(self): + await self.notify() + await self.blocking_method() + with tracer.trace('tornado.before_write') as span: + # trace more work in the handler + + @tracer.wrap('tornado.executor_handler') + @tornado.concurrent.run_on_executor + def blocking_method(self): + # do something expensive + + @tracer.wrap('tornado.notify', service='tornado-notification') + async def notify(self): + # do something + +If you are overriding the ``on_finish`` or ``log_exception`` methods on a +``RequestHandler``, you will need to call the super method to ensure the +tracer's patched methods are called:: + + class MainHandler(tornado.web.RequestHandler): + async def get(self): + self.write("Hello, world") + + def on_finish(self): + super(MainHandler, self).on_finish() + # do other clean-up + + def log_exception(self, typ, value, tb): + super(MainHandler, self).log_exception(typ, value, tb) + # do other logging + +Tornado settings can be used to change some tracing configuration, like:: + + settings = { + 'datadog_trace': { + 'default_service': 'my-tornado-app', + 'tags': {'env': 'production'}, + 'distributed_tracing': False, + }, + } + + app = tornado.web.Application([ + (r'/', MainHandler), + ], **settings) + +The available settings are: + +* ``default_service`` (default: `tornado-web`): set the service name used by the tracer. Usually + this configuration must be updated with a meaningful name. Can also be configured via the + ``DD_SERVICE`` environment variable. +* ``tags`` (default: `{}`): set global tags that should be applied to all spans. +* ``enabled`` (default: `True`): define if the tracer is enabled or not. If set to `false`, the + code is still instrumented but no spans are sent to the APM agent. +* ``distributed_tracing`` (default: `None`): enable distributed tracing if this is called + remotely from an instrumented application. Overrides the integration config which is configured via the + ``DD_TORNADO_DISTRIBUTED_TRACING`` environment variable. + We suggest to enable it only for internal services where headers are under your control. +* ``agent_hostname`` (default: `localhost`): define the hostname of the APM agent. +* ``agent_port`` (default: `8126`): define the port of the APM agent. +* ``settings`` (default: ``{}``): Tracer extra settings used to change, for instance, the filtering behavior. +""" diff --git a/ddtrace/contrib/internal/tornado/patch.py b/ddtrace/contrib/internal/tornado/patch.py index bc7fc387ec6..ccf63a3070e 100644 --- a/ddtrace/contrib/internal/tornado/patch.py +++ b/ddtrace/contrib/internal/tornado/patch.py @@ -44,7 +44,7 @@ def get_version(): def _supported_versions() -> Dict[str, str]: - return {"tornado": ">=6.0.0"} + return {"tornado": ">=6.1"} def patch(): diff --git a/ddtrace/contrib/tornado.py b/ddtrace/contrib/tornado.py deleted file mode 100644 index ad29c6492ac..00000000000 --- a/ddtrace/contrib/tornado.py +++ /dev/null @@ -1,119 +0,0 @@ -r""" -The Tornado integration traces all ``RequestHandler`` defined in a Tornado web application. -Auto instrumentation is available using the ``patch`` function that **must be called before** -importing the tornado library. - -The following is an example:: - - # patch before importing tornado and concurrent.futures - from ddtrace.trace import tracer, patch - patch(tornado=True) - - import tornado.web - import tornado.gen - import tornado.ioloop - - # create your handlers - class MainHandler(tornado.web.RequestHandler): - @tornado.gen.coroutine - def get(self): - self.write("Hello, world") - - # create your application - app = tornado.web.Application([ - (r'/', MainHandler), - ]) - - # and run it as usual - app.listen(8888) - tornado.ioloop.IOLoop.current().start() - -When any type of ``RequestHandler`` is hit, a request root span is automatically created. If -you want to trace more parts of your application, you can use the ``wrap()`` decorator and -the ``trace()`` method as usual:: - - class MainHandler(tornado.web.RequestHandler): - @tornado.gen.coroutine - def get(self): - yield self.notify() - yield self.blocking_method() - with tracer.trace('tornado.before_write') as span: - # trace more work in the handler - - @tracer.wrap('tornado.executor_handler') - @tornado.concurrent.run_on_executor - def blocking_method(self): - # do something expensive - - @tracer.wrap('tornado.notify', service='tornado-notification') - @tornado.gen.coroutine - def notify(self): - # do something - -If you are overriding the ``on_finish`` or ``log_exception`` methods on a -``RequestHandler``, you will need to call the super method to ensure the -tracer's patched methods are called:: - - class MainHandler(tornado.web.RequestHandler): - @tornado.gen.coroutine - def get(self): - self.write("Hello, world") - - def on_finish(self): - super(MainHandler, self).on_finish() - # do other clean-up - - def log_exception(self, typ, value, tb): - super(MainHandler, self).log_exception(typ, value, tb) - # do other logging - -Tornado settings can be used to change some tracing configuration, like:: - - settings = { - 'datadog_trace': { - 'default_service': 'my-tornado-app', - 'tags': {'env': 'production'}, - 'distributed_tracing': False, - }, - } - - app = tornado.web.Application([ - (r'/', MainHandler), - ], **settings) - -The available settings are: - -* ``default_service`` (default: `tornado-web`): set the service name used by the tracer. Usually - this configuration must be updated with a meaningful name. Can also be configured via the - ``DD_SERVICE`` environment variable. -* ``tags`` (default: `{}`): set global tags that should be applied to all spans. -* ``enabled`` (default: `True`): define if the tracer is enabled or not. If set to `false`, the - code is still instrumented but no spans are sent to the APM agent. -* ``distributed_tracing`` (default: `None`): enable distributed tracing if this is called - remotely from an instrumented application. Overrides the integration config which is configured via the - ``DD_TORNADO_DISTRIBUTED_TRACING`` environment variable. - We suggest to enable it only for internal services where headers are under your control. -* ``agent_hostname`` (default: `localhost`): define the hostname of the APM agent. -* ``agent_port`` (default: `8126`): define the port of the APM agent. -* ``settings`` (default: ``{}``): Tracer extra settings used to change, for instance, the filtering behavior. -""" - -from ddtrace.contrib.internal.tornado.stack_context import TracerStackContext -from ddtrace.contrib.internal.tornado.stack_context import context_provider -from ddtrace.contrib.internal.tornado.stack_context import run_with_trace_context -from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning -from ddtrace.vendor.debtcollector import deprecate - - -deprecate( - "ddtrace.contrib.tornado is deprecated", - message="Use ``import ddtrace.auto`` and ``DD_PATCH_MODULES`` to configure tracing for Tornado.", - category=DDTraceDeprecationWarning, - removal_version="4.0.0", -) - -__all__ = [ - "context_provider", - "run_with_trace_context", - "TracerStackContext", -] diff --git a/docs/index.rst b/docs/index.rst index 65e88bb1b95..808111b1969 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -206,7 +206,7 @@ contacting support. +--------------------------------------------------+------------+----------+------+ | :ref:`subprocess` | \* | Yes | | +--------------------------------------------------+------------+----------+------+ -| :ref:`tornado` | >= 6.0 | No | | +| :ref:`tornado` | >= 6.1 | No | | +--------------------------------------------------+------------+----------+------+ | :ref:`unittest` | \* | Yes | | +--------------------------------------------------+------------+----------+------+ diff --git a/docs/integrations.rst b/docs/integrations.rst index a877f6c4b50..9f57ccb5c3f 100644 --- a/docs/integrations.rst +++ b/docs/integrations.rst @@ -566,7 +566,7 @@ Subprocess Tornado ^^^^^^^ -.. automodule:: ddtrace.contrib.tornado +.. automodule:: ddtrace.contrib.internal.tornado .. _unittest: diff --git a/releasenotes/notes/update-tornado-support-5c87ee2d79ae54c4.yaml b/releasenotes/notes/update-tornado-support-5c87ee2d79ae54c4.yaml new file mode 100644 index 00000000000..da90a1d2a95 --- /dev/null +++ b/releasenotes/notes/update-tornado-support-5c87ee2d79ae54c4.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + tornado: Updated minimum supported version to v6.1+. diff --git a/riotfile.py b/riotfile.py index da36295eecc..2784dba382c 100644 --- a/riotfile.py +++ b/riotfile.py @@ -2688,15 +2688,6 @@ def select_pys(min_version: str = MIN_PYTHON_VERSION, max_version: str = MAX_PYT command="pytest {cmdargs} tests/opentracer/test_tracer_asyncio.py", pkgs={"pytest-asyncio": "==0.21.1"}, ), - Venv( - pys=select_pys(min_version="3.9", max_version="3.11"), - command="pytest {cmdargs} tests/opentracer/test_tracer_tornado.py", - # TODO: update opentracing tests to be compatible with Tornado v6. - # https://github.com/opentracing/opentracing-python/issues/136 - pkgs={ - "tornado": ["~=4.5.0", "~=5.1.0"], - }, - ), Venv( command="pytest {cmdargs} tests/opentracer/test_tracer_gevent.py", venvs=[ @@ -2791,7 +2782,7 @@ def select_pys(min_version: str = MIN_PYTHON_VERSION, max_version: str = MAX_PYT # tornado added support for Python 3.9 in 6.1 pys="3.9", # tornado 6.0.x and pytest 8.x have a compatibility bug - pkgs={"tornado": ["~=6.0.0", "~=6.2"], "pytest": "<=8"}, + pkgs={"tornado": ["==6.1", "~=6.2"], "pytest": "<=8"}, ), Venv( # tornado added support for Python 3.10 in 6.2 diff --git a/supported_versions_output.json b/supported_versions_output.json index 5ebdb160b4d..4f3d562a148 100644 --- a/supported_versions_output.json +++ b/supported_versions_output.json @@ -645,7 +645,7 @@ { "dependency": "tornado", "integration": "tornado", - "minimum_tracer_supported": "6.0.4", + "minimum_tracer_supported": "6.1", "max_tracer_supported": "6.5.1", "pinned": "true", "auto-instrumented": false diff --git a/supported_versions_table.csv b/supported_versions_table.csv index 46e993b9e41..b1587475fc2 100644 --- a/supported_versions_table.csv +++ b/supported_versions_table.csv @@ -89,7 +89,7 @@ sqlalchemy,sqlalchemy,1.3.24,2.0.43,False pysqlite3-binary,sqlite3,0.5.2.post3,0.5.2.post3,True starlette,starlette,0.14.2,0.48.0,True structlog,structlog,20.2.0,25.4.0,True -tornado,tornado *,6.0.4,6.5.1,False +tornado,tornado *,6.1.4,6.5.1,False urllib3,urllib3,1.25.8,2.5.0,False valkey,valkey,6.0.2,6.1.1,True google-cloud-aiplatform,vertexai,1.71.1,1.71.1,True diff --git a/tests/contrib/suitespec.yml b/tests/contrib/suitespec.yml index 5dcc3177b57..c61b75638f7 100644 --- a/tests/contrib/suitespec.yml +++ b/tests/contrib/suitespec.yml @@ -175,7 +175,6 @@ components: subprocess: - ddtrace/contrib/internal/subprocess/* tornado: - - ddtrace/contrib/tornado.py - ddtrace/contrib/internal/tornado/* urllib3: - ddtrace/contrib/internal/urllib3/* diff --git a/tests/internal/test_module.py b/tests/internal/test_module.py index df29f2e7d79..9798efd2749 100644 --- a/tests/internal/test_module.py +++ b/tests/internal/test_module.py @@ -569,7 +569,6 @@ def test_public_modules_in_ddtrace_contrib(): "ddtrace.contrib.__init__", "ddtrace.contrib.trace_utils", "ddtrace.contrib.celery", - "ddtrace.contrib.tornado", "ddtrace.contrib.valkey", "ddtrace.contrib.asgi", "ddtrace.contrib.bottle",