-
Notifications
You must be signed in to change notification settings - Fork 227
Add support for tornado.web #661
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
Merged
Merged
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
e3c00c2
Add vscode files and pytest_cache to gitignore
basepi 6e4e884
Add tornado integration
basepi a7ab699
Add skeleton for tornado instrumentation
basepi cfe5bda
Fix path
basepi ca4c821
First pass at instrumenting RequestHandler._execute
basepi f5ab53b
Finish up request context info
basepi 0202fec
Implement review suggestions
basepi 6c5d688
Add documentation and create_transactions override for instrumentation
basepi 1400737
Fix a typo
basepi 06ecbbb
Fix flake8 excludes for vscode
basepi b31d615
Use FIXME for now
basepi 50f2cf2
Add instrumentation to the register
basepi d6cb2bf
Fix (some very hard to track down) copy pasta
basepi 18d81db
Add first passing test
basepi 1f86df5
Fix transaction name and add more docs about sampling
basepi 3e28231
Fix test for new transaction name
basepi c25da7e
Add instrumentation for tornado exception handling
basepi b4f02be
Add tests for exception and TraceParent
basepi 380dd65
Merge remote-tracking branch 'upstream/master' into tornado
basepi 9feb2c5
Instrument template rendering
basepi 35b345c
Add render test for tornado
basepi fef8f8b
Finish implementing request/response capture
basepi 4409762
Merge remote-tracking branch 'upstream/master' into tornado
basepi 4026292
Add documentation, fix up jenkins configs
basepi bfe3546
Remove tornado 5.0 support
basepi a20b38c
Fix requirements-tornado-newest.txt to be 6.0+
basepi d2a9218
Move tornado tests under asyncio for proper pytest discovery
basepi c94cbb7
Rename tornado tests
basepi 87fd851
Fix tests with late import
basepi a6eaed1
Add setup.py changes I missed
basepi 636beaf
Add tornado to known_third_party
basepi 2866c64
Apply suggestions from code review
basepi 612a7e8
Add blank line after final bullet
basepi 58398bf
Merge remote-tracking branch 'upstream/master' into tornado
basepi 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
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,3 +35,4 @@ FRAMEWORK: | |
- pymysql-newest | ||
- aiohttp-newest | ||
- aiopg-newest | ||
- tornado-newest |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,3 +65,4 @@ FRAMEWORK: | |
- aiohttp-4.0 | ||
- aiohttp-newest | ||
- aiopg-newest | ||
- tornado-newest |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,11 @@ | ||
[flake8] | ||
exclude=elasticapm/utils/wrapt,build,src,tests,dist,conftest.py,setup.py | ||
exclude= | ||
elasticapm/utils/wrapt/**, | ||
build/**, | ||
src/**, | ||
tests/**, | ||
dist/**, | ||
conftest.py, | ||
setup.py | ||
max-line-length=120 | ||
ignore=E731,W503,E203,BLK100,B301 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,3 +28,5 @@ venv | |
benchmarks/result* | ||
coverage.xml | ||
tests/python-agent-junit.xml | ||
*.code-workspace | ||
.pytest_cache/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
[[tornado-support]] | ||
=== Tornado Support | ||
|
||
Incorporating Elastic APM into your Tornado project only requires a few easy | ||
steps. | ||
|
||
[float] | ||
[[tornado-installation]] | ||
==== Installation | ||
|
||
Install the Elastic APM agent using pip: | ||
|
||
[source,bash] | ||
---- | ||
$ pip install elastic-apm | ||
---- | ||
|
||
or add `elastic-apm` to your project's `requirements.txt` file. | ||
|
||
|
||
[float] | ||
[[tornado-setup]] | ||
==== Setup | ||
|
||
To set up the agent, you need to initialize it with appropriate settings. | ||
|
||
The settings are configured either via environment variables, | ||
the application's settings, or as initialization arguments. | ||
|
||
You can find a list of all available settings in the | ||
<<configuration, Configuration>> page. | ||
|
||
To initialize the agent for your application using environment variables: | ||
|
||
[source,python] | ||
---- | ||
import tornado.web | ||
from elasticapm.contrib.tornado import ElasticAPM | ||
|
||
app = tornado.web.Application() | ||
apm = ElasticAPM(app) | ||
---- | ||
|
||
To configure the agent using `ELASTIC_APM` in your application's settings: | ||
|
||
[source,python] | ||
---- | ||
import tornado.web | ||
from elasticapm.contrib.tornado import ElasticAPM | ||
|
||
app = tornado.web.Application() | ||
app.settings['ELASTIC_APM'] = { | ||
'SERVICE_NAME': '<SERVICE-NAME>', | ||
'SECRET_TOKEN': '<SECRET-TOKEN>', | ||
} | ||
apm = ElasticAPM(app) | ||
---- | ||
|
||
[float] | ||
[[tornado-usage]] | ||
==== Usage | ||
|
||
Once you have configured the agent, it will automatically track transactions | ||
and capture uncaught exceptions within tornado. | ||
|
||
Capture an arbitrary exception by calling | ||
<<client-api-capture-exception,`capture_exception`>>: | ||
|
||
[source,python] | ||
---- | ||
try: | ||
1 / 0 | ||
except ZeroDivisionError: | ||
apm.client.capture_exception() | ||
---- | ||
|
||
Log a generic message with <<client-api-capture-message,`capture_message`>>: | ||
|
||
[source,python] | ||
---- | ||
apm.client.capture_message('hello, world!') | ||
---- | ||
|
||
[float] | ||
[[tornado-performance-metrics]] | ||
==== Performance metrics | ||
|
||
If you've followed the instructions above, the agent has installed our | ||
instrumentation within the base RequestHandler class in tornado.web. This will | ||
measure response times, as well as detailed performance data for all supported | ||
technologies. | ||
|
||
NOTE: Due to the fact that `asyncio` drivers are usually separate from their | ||
synchronous counterparts, specific instrumentation is needed for all drivers. | ||
The support for asynchronous drivers is currently quite limited. | ||
|
||
[float] | ||
[[tornado-ignoring-specific-views]] | ||
===== Ignoring specific routes | ||
|
||
You can use the | ||
<<config-transactions-ignore-patterns,`TRANSACTIONS_IGNORE_PATTERNS`>> | ||
configuration option to ignore specific routes. The list given should be a | ||
list of regular expressions which are matched against the transaction name: | ||
|
||
[source,python] | ||
---- | ||
app.settings['ELASTIC_APM'] = { | ||
# ... | ||
'TRANSACTIONS_IGNORE_PATTERNS': ['^GET SecretHandler', 'MainHandler'] | ||
# ... | ||
} | ||
---- | ||
|
||
This would ignore any requests using the `GET SecretHandler` route | ||
and any requests containing `MainHandler`. | ||
|
||
|
||
[float] | ||
[[supported-tornado-and-python-versions]] | ||
==== Supported tornado and Python versions | ||
|
||
A list of supported <<supported-tornado,tornado>> and <<supported-python,Python>> versions can be found on our <<supported-technologies,Supported Technologies>> page. | ||
|
||
NOTE: Elastic APM only supports `asyncio` when using Python 3.7+ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# BSD 3-Clause License | ||
# | ||
# Copyright (c) 2019, Elasticsearch BV | ||
# All rights reserved. | ||
# | ||
# Redistribution and use in source and binary forms, with or without | ||
# modification, are permitted provided that the following conditions are met: | ||
# | ||
# * Redistributions of source code must retain the above copyright notice, this | ||
# list of conditions and the following disclaimer. | ||
# | ||
# * Redistributions in binary form must reproduce the above copyright notice, | ||
# this list of conditions and the following disclaimer in the documentation | ||
# and/or other materials provided with the distribution. | ||
# | ||
# * Neither the name of the copyright holder nor the names of its | ||
# contributors may be used to endorse or promote products derived from | ||
# this software without specific prior written permission. | ||
# | ||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
""" | ||
Framework integration for Tornado | ||
|
||
Note that transaction creation is actually done in the tornado | ||
instrumentation. This module only creates the client for later use by the | ||
that instrumentation, and triggers the global instrumentation itself. | ||
""" | ||
import elasticapm | ||
import tornado | ||
from elasticapm import Client | ||
|
||
|
||
class ElasticAPM: | ||
def __init__(self, app, client=None, **config): | ||
""" | ||
Create the elasticapm Client object and store in the app for later | ||
use. | ||
|
||
ElasticAPM configuration is sent in via the **config kwargs, or | ||
optionally can be added to the application via the Application object | ||
(as a dictionary under the "ELASTIC_APM" key in the settings). | ||
""" | ||
if "ELASTIC_APM" in app.settings and isinstance(app.settings["ELASTIC_APM"], dict): | ||
settings = app.settings["ELASTIC_APM"] | ||
settings.update(config) | ||
config = settings | ||
if not client: | ||
config.setdefault("framework_name", "tornado") | ||
config.setdefault("framework_version", tornado.version) | ||
client = Client(config) | ||
self.app = app | ||
self.client = client | ||
app.elasticapm_client = client | ||
|
||
# Don't instrument if debug=True in tornado, unless client.config.debug is True | ||
if (not self.app.settings.get("debug") or client.config.debug) and client.config.instrument: | ||
elasticapm.instrument() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# BSD 3-Clause License | ||
# | ||
# Copyright (c) 2019, Elasticsearch BV | ||
# All rights reserved. | ||
# | ||
# Redistribution and use in source and binary forms, with or without | ||
# modification, are permitted provided that the following conditions are met: | ||
# | ||
# * Redistributions of source code must retain the above copyright notice, this | ||
# list of conditions and the following disclaimer. | ||
# | ||
# * Redistributions in binary form must reproduce the above copyright notice, | ||
# this list of conditions and the following disclaimer in the documentation | ||
# and/or other materials provided with the distribution. | ||
# | ||
# * Neither the name of the copyright holder nor the names of its | ||
# contributors may be used to endorse or promote products derived from | ||
# this software without specific prior written permission. | ||
# | ||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
import elasticapm | ||
from elasticapm.conf import constants | ||
from elasticapm.utils import compat | ||
|
||
try: | ||
import tornado | ||
except ImportError: | ||
pass | ||
|
||
|
||
def get_data_from_request(request_handler, request, capture_body=False, capture_headers=True): | ||
""" | ||
Capture relevant data from a tornado.httputil.HTTPServerRequest | ||
""" | ||
result = { | ||
"method": request.method, | ||
"socket": {"remote_address": request.remote_ip, "encrypted": request.protocol == "https"}, | ||
"cookies": request.cookies, | ||
"http_version": request.version, | ||
} | ||
if capture_headers: | ||
result["headers"] = dict(request.headers) | ||
if request.method in constants.HTTP_WITH_BODY: | ||
if tornado.web._has_stream_request_body(request_handler.__class__): | ||
# Body is a future and streaming is expected to be handled by | ||
# the user in the RequestHandler.data_received function. | ||
# Currently not sure of a safe way to get the body in this case. | ||
result["body"] = "[STREAMING]" if capture_body else "[REDACTED]" | ||
else: | ||
body = None | ||
try: | ||
body = tornado.escape.json_decode(request.body) | ||
except Exception: | ||
body = request.body | ||
|
||
if body is not None: | ||
result["body"] = body if capture_body else "[REDACTED]" | ||
|
||
result["url"] = elasticapm.utils.get_url_dict(request.full_url()) | ||
return result | ||
|
||
|
||
def get_data_from_response(request_handler, capture_headers=True): | ||
result = {} | ||
|
||
result["status_code"] = request_handler.get_status() | ||
|
||
if capture_headers and request_handler._headers: | ||
headers = request_handler._headers | ||
result["headers"] = {key: ";".join(headers.get_list(key)) for key in compat.iterkeys(headers)} | ||
pass | ||
return result |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.