scoutapp / scout_apm_python

ScoutAPM Python Agent. Supports Django, Flask, and many other frameworks.
https://scoutapm.com
MIT License
67 stars 21 forks source link

Instrument the Odoo framework #232

Open adamchainz opened 5 years ago

adamchainz commented 5 years ago

Odoo is an ERP web application framework with its own ORM. We should look into instrumenting it to capture web requests and the SQL queries it makes.

SQL Queries can be captured through monkey-patching the ORM's cursor wrapper here: https://github.com/odoo/odoo/blob/12.0/odoo/sql_db.py#L213 (Similar to how we do for Django <2.0)

It uses Jinja2 for its templating so we are already set there for instrumenting.

kkarolis commented 5 years ago

We have integrated previously with NewRelic and it had a WSGIApplicationWrapper in the library as described here. It integrated quite neatly since it's the only thing which was necessary to get the data through.

adamchainz commented 5 years ago

Oh cool, thanks for the reference. Providing a generic WSGI wrapper is probably a good idea so we can give basic support to all web frameworks.

adamchainz commented 5 years ago

Hi @kkarolis

I've had a look through the documentation and it seems the normal Odoo deployment doesn't use the WSGI application, at least not directly. It's documented as an extra setup step.

I also found there's a third party Odoo module odoo-newrelic that adds New Relic wrapping through monkey patching rather than wsgi wrapping.

In your experience, have you always used the WSGI deployment mode, and is it common enough in the Odoo community? Or would it be better if we'd provide an official Odoo module, since that seems easier to install?

Thanks,

Adam

kkarolis commented 5 years ago

Hey @adamchainz,

Indeed its not ordinary (from my experience) to deploy odoo using standard wsgi runners. However, as You figured out Yourself,- it is used indirectly. Normally, odoo is installed into virtualenv like environment, and odoo script is used to control server. Internally, it calls this main function which in turn calls this start function which launches odoo in a pre-fork web server model fashion. WSGI compliant application function handles the requests then.

Regarding the module You've posted,- I personally haven't used it, but it actually does something similar what we did with newrelic and newrelic.agent.WSGIApplicationWrapper. On the other hand, I see some corner case handling in there, which we don't do, so likely its more robust. We are loading the agent via odoo post-init-hook though, so I guess some improvements can be made as well. I'll drop in a snippet of what we have:

import logging
import threading

from odoo.service import server
from odoo.tools import config as odoo_config

_instrumentation_lock = threading.Lock()
_logger = logging.getLogger(__name__)

try:
    import newrelic.agent
except ImportError:
    _logger.info(
        'newrelic pip package is not installed, new_relic_agent will not work')

def install_newrelic():
    newrelic_config = odoo_config.get(
        'newrelic_config', '').strip()
    newrelic_environment = odoo_config.get(
        'newrelic_environment', 'test').strip()

    with _instrumentation_lock:
        if getattr(server.server.app, '_newrelic_agent_wrapped', False):
            return

        _logger.info('Installing New Relic agent')
        newrelic.agent.initialize(newrelic_config, newrelic_environment)

        server.server.app = newrelic.agent.WSGIApplicationWrapper(
            server.server.app)
        server.server.app._newrelic_agent_wrapped = True

Regarding wrapper vs odoo addon, in my opinion, WSGIApplicationWrapper like newrelic one would be quite useful already, but odoo addon would be super awesome. I'm not sure how willing are you to maintain it though :)

Regards, Karolis