Skip to content
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

Add an autoinstrumentation mechanism and an instrumentor for Flask #327

Merged
merged 29 commits into from
Mar 30, 2020

Conversation

ocelotl
Copy link
Contributor

@ocelotl ocelotl commented Dec 11, 2019

Fixes #300

@ocelotl
Copy link
Contributor Author

ocelotl commented Dec 11, 2019

This PR includes an autoinstrumentation mechanism and a Flask instrumentor (an instrumentor is a class that implements the _instrument and _uninstrument methods).

It works like this:

  1. A console command is defined here. This makes it possible to run a command named opentelemetry-auto-instrumentation that will execute this function.
  2. When the opentelemetry-auto-instrumentation command is executed, then the instrument method of the different instrumentors is called.
    2.In the case of the Flask instrumentor, the original flask.Flask gets replaced with _InstrumentedFlask (in this case, the Flask instrumentor uses monkey patching to perform the instrumentation, nevertheless, monkey patching is not always the method used to do this, so the name instrumentor is preferred over patcher).
  3. Once this is done, the app gets executed here.

@ocelotl ocelotl force-pushed the issue_300 branch 2 times, most recently from 1b300de to 577d5c6 Compare December 19, 2019 21:53
@ocelotl ocelotl added discussion Issue or PR that needs/is extended discussion. ext WIP Work in progress labels Dec 20, 2019
@ocelotl
Copy link
Contributor Author

ocelotl commented Dec 20, 2019

Please leave your comments 👍

Copy link
Member

@toumorokoshi toumorokoshi left a comment

Choose a reason for hiding this comment

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

Nice! this is a big step, thanks for focusing on this.

My two cents (and I think we need some more opinions here) are:

Enable programmatic control of patching

I'm not a huge fan of the patch executable (like auto_agent) myself, because I think that inverts control away from the application author and toward some other entity. These are hard to chain (e.g. if something else wants to go modify the import and startup logic), and hard to customize.

Looking at dd-agent-py, which we're taking inspiration from, customization of the dd agent happens via several environment variables. https://docs.datadoghq.com/tracing/setup/python/#environment-variable. I've personally found environment variables (in essence a dictionary of key-value pairs) fairly limiting, as it doesn't enable things like more complex types or programmatic control.

Also I don't know how robust or flexible sitecustomize is as an interface. Will it impact it's use with other systems that modify sitecustomize, such as zc.buildout?

Patching Interface

I think the patch interface looks good. I feel like some extensions will probably want to expose more primitives (e.g. flasks' instrument_app) to allow finer control of the extensions themselves.

Overall I think this achieves standardization of patching for auto-instrumentation, which is great, as well as it's utilization of setuptools entry points which is best practice.

@@ -36,7 +36,9 @@ def setspanattr(key, value):

self.span.set_attribute = setspanattr

self.app = Flask(__name__)
FlaskPatcher().patch()
Copy link
Member

Choose a reason for hiding this comment

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

will this cause issues with other unit tests that might want the raw Flask, as it will permanently Flask with an instrumented variety?

We may also use Flask apps to bring up example test servers, or we may in the future.

There's a technique used to reload modules we could use during teardown to ensure it gets set back: f328909

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It may cause issues, but I think it is necessary for patching purposes. I can investigate a way that the patching is done in a way that only happens for the testing purposes of the patching agent. Also, I can think about a unpatching method that gets called automatically when patching is no longer needed.

Copy link
Member

Choose a reason for hiding this comment

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

Does the reload function not work for your purposes? I believe it should reset everything you've patched: https://docs.python.org/3/library/importlib.html#importlib.reload

Although maybe a best-effort unpatch API is also a good idea as it'll be hard for people to be aware of the new patches that were added from version to version, so they may find their previous custom unpatching function not unpatching new patches added by a newer version of the auto-instrumentation.

@@ -40,6 +40,14 @@
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
],
entry_points={
"console_scripts": [
"auto_agent = opentelemetry.commands.auto_agent:run"
Copy link
Member

Choose a reason for hiding this comment

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

I think "agent" is probably the wrong word. how about Instrument? auto_instrument would also match the opentelemetry spec name (autoinstrumentation) that we're trying to support here.

Copy link
Member

Choose a reason for hiding this comment

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

also we should probably prefix any commands with opentelemetry, as console_scripts is a global namespace.

so "opentelemetry-auto-instrument" as a final suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I'll have to read the documentation on console_scripts thoroughly, I am under the impression it must have this name for our purposes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Never mind, I understand now what you mean.

opentelemetry-api/src/opentelemetry/commands/auto_agent.py Outdated Show resolved Hide resolved

_LOG = getLogger(__file__)

for entry_point in iter_entry_points("opentelemetry_patcher"):
Copy link
Member

Choose a reason for hiding this comment

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

can this be also be available as a public method? I know we're looking at ddtrace for inspiration, and that codebase includes a way to patch things via patch_all.

@ocelotl
Copy link
Contributor Author

ocelotl commented Jan 2, 2020

Thanks for your comments, @toumorokoshi. 👍 Regarding the patch executable, I think there is value in having a fire-and-forget auto instrumentation executable that allows the user to just take non-instrumented code and make it instrumented automatically. This seems useful for situations when quick instrumentation is just needed an not much else needs consideration.

That being said, I see our users in need of configuration soon after they get the simplest of automatic instrumentations done. Of course, this means supporting both scenarios.

I'm not much a fan of environment variables myself, they are vulnerable to them being lost after a reboot or a new shell being started and they require usually long prefixes to keep them separated from each other. I prefer configuration files myself, since it is possible to keep them in version control. That being said, other people prefer command line arguments because they make it very easy to adjust stuff when making many runs of a command 🤷‍♂️. I'll look into this and will propose a solution.

You raise a valid point when you mention the lack of a programmatic approach to auto instrumentation. This looks like a job for a well defined and documented API that can be used to create customized auto instrumentation tools. Also, will look into this and will propose a solution.

Please let me know if you have more design-level ideas for me to consider.

Thanks!

@ocelotl ocelotl force-pushed the issue_300 branch 3 times, most recently from d55604c to a81cbc4 Compare January 7, 2020 19:51
@codecov-io
Copy link

codecov-io commented Jan 8, 2020

Codecov Report

Merging #327 into master will increase coverage by 0.14%.
The diff coverage is 88.46%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #327      +/-   ##
==========================================
+ Coverage   89.50%   89.64%   +0.14%     
==========================================
  Files          43       43              
  Lines        2229     2240      +11     
  Branches      248      248              
==========================================
+ Hits         1995     2008      +13     
  Misses        159      159              
+ Partials       75       73       -2     
Impacted Files Coverage Δ
...-ext-flask/src/opentelemetry/ext/flask/__init__.py 90.16% <88.00%> (+0.16%) ⬆️
...app/src/opentelemetry_example_app/flask_example.py 100.00% <100.00%> (ø)
...emetry-sdk/src/opentelemetry/sdk/trace/__init__.py 92.38% <0.00%> (+0.34%) ⬆️
...elemetry-api/src/opentelemetry/context/__init__.py 95.45% <0.00%> (+4.54%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7b1d866...5bb20d1. Read the comment docs.

@ocelotl ocelotl marked this pull request as ready for review January 8, 2020 23:17
@ocelotl ocelotl requested a review from a team January 8, 2020 23:17
@ocelotl
Copy link
Contributor Author

ocelotl commented Jan 8, 2020

I have fixed the last issues I had in this PR, @toumorokoshi, please let me know what you think.

Copy link
Member

@Oberon00 Oberon00 left a comment

Choose a reason for hiding this comment

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

Requesting changes because:

@Oberon00
Copy link
Member

Something like the BasePatcher interface is much-needed! 👍

@ocelotl
Copy link
Contributor Author

ocelotl commented Jan 31, 2020

Thank you for your review, @Oberon00!

Copy link
Contributor

@codeboten codeboten left a comment

Choose a reason for hiding this comment

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

Requested a few changes, but this is looking pretty good! Excited to see this functionality in the library.

opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py Outdated Show resolved Hide resolved
opentelemetry-api/src/opentelemetry/patcher/__init__.py Outdated Show resolved Hide resolved
examples/auto_instrumentation/README.md Outdated Show resolved Hide resolved
examples/auto_instrumentation/README.md Outdated Show resolved Hide resolved
examples/auto_instrumentation/README.md Outdated Show resolved Hide resolved
examples/auto_instrumentation/publisher.py Outdated Show resolved Hide resolved
examples/auto_instrumentation/publisher_untraced.py Outdated Show resolved Hide resolved
examples/auto_instrumentation/utils.py Outdated Show resolved Hide resolved
@@ -40,6 +40,15 @@
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
],
entry_points={
"console_scripts": [
Copy link
Contributor

Choose a reason for hiding this comment

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

+1 on moving the console script to a separate package. Maybe it ends up moving into the https://github.com/open-telemetry/opentelemetry-auto-instr-python/ repo, just a thought

opentelemetry-api/setup.py Outdated Show resolved Hide resolved
examples/auto_instrumentation/README.md Outdated Show resolved Hide resolved
@ocelotl ocelotl removed the WIP Work in progress label Feb 19, 2020
Copy link
Contributor

@codeboten codeboten left a comment

Choose a reason for hiding this comment

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

Thanks for addressing my feedback! LGTM

@ocelotl ocelotl changed the title Add autoinstrumentation prototype for Flask Add an autoinstrumentation and an instrumentor for Flask Mar 24, 2020
@ocelotl ocelotl changed the title Add an autoinstrumentation and an instrumentor for Flask Add an autoinstrumentation mechanism and an instrumentor for Flask Mar 24, 2020
Copy link
Member

@mauriciovasquezbernal mauriciovasquezbernal left a comment

Choose a reason for hiding this comment

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

Overall looks good to me.
I think the documentation needs a little bit of rework, specially because it has been changed a lot after this PR was opened.

About the example, I think the right place for that is docs/examples.

opentelemetry-auto-instrumentation/README.rst Outdated Show resolved Hide resolved
opentelemetry-auto-instrumentation/README.rst Outdated Show resolved Hide resolved
opentelemetry-auto-instrumentation/tests/__init__.py Outdated Show resolved Hide resolved
docs/index.rst Outdated Show resolved Hide resolved
opentelemetry-auto-instrumentation/example/formatter.py Outdated Show resolved Hide resolved

The code in ``program.py`` needs to use one of the packages for which there is
an OpenTelemetry extension. For a list of the available extensions please check
`here <https://opentelemetry-python.readthedocsio/>`_.

Choose a reason for hiding this comment

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

It's better to use an "internal" target for this link. So if we decide to host our documentation somewhere else the generated link is not broken.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, but what do you mean with "internal"? Can you provide an example, please?

Copy link
Member

@toumorokoshi toumorokoshi left a comment

Choose a reason for hiding this comment

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

I think there's still a lot of additional work that will have to go into auto-instrumentation in general, but I think the patterns you've laid our here look really good, and put us in a great position to actually build this out.

Also great idea to try to find a pattern that allows extensions to be leveraged for auto-instrumentation as well. I see that as as huge win for maintainability in the future.

Copy link
Member

@c24t c24t left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for pushing through this long review cycle @ocelotl.

My only remaining (and nit-level) comment is that opentelemetry_auto_instrumentation_instrumentor is a mouthfull, opentelemetry_instrumentor seems to describe the same thing and is much shorter.

@@ -0,0 +1,28 @@
# Copyright The OpenTelemetry Authors
Copy link
Member

Choose a reason for hiding this comment

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

FYI it looks like this isn't included in the built docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, which docs are the built docs?

Copy link
Member

Choose a reason for hiding this comment

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

The ones you get running tox -e docs or make html in the docs dir.

@ocelotl
Copy link
Contributor Author

ocelotl commented Mar 30, 2020

LGTM! Thanks for pushing through this long review cycle @ocelotl.

My only remaining (and nit-level) comment is that opentelemetry_auto_instrumentation_instrumentor is a mouthfull, opentelemetry_instrumentor seems to describe the same thing and is much shorter.

👍 Thanks for the review! I have shortened the entry point name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request needs reviewers PRs with this label are ready for review and needs people to review to move forward.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Research integrating auto- and manual instrumentations
9 participants