Flask supportedit

Getting Elastic APM set up for your Flask project is easy, and there are various ways you can tweak it to fit to your needs.

Installationedit

Install the Elastic APM agent using pip:

$ pip install elastic-apm[flask]

or add elastic-apm[flask] to your project’s requirements.txt file.

For apm-server 6.2+, make sure you use version 2.0 or higher of elastic-apm.

If you use Flask with uwsgi, make sure to enable threads.

Setupedit

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 page.

To initialize the agent for your application using environment variables:

from elasticapm.contrib.flask import ElasticAPM

app = Flask(__name__)

apm = ElasticAPM(app)

To configure the agent using ELASTIC_APM in your application’s settings:

from elasticapm.contrib.flask import ElasticAPM

app.config['ELASTIC_APM'] = {
    'SERVICE_NAME': '<SERVICE-NAME>',
    'SECRET_TOKEN': '<SECRET-TOKEN>',
}
apm = ElasticAPM(app)

The final option is to initialize the agent with the settings as arguments:

from elasticapm.contrib.flask import ElasticAPM

apm = ElasticAPM(app, service_name='<APP-ID>', secret_token='<SECRET-TOKEN>')
Debug modeedit

Please note that errors and transactions will only be sent to the APM Server if your app is not in debug mode.

To force the agent to send data while the app is in debug mode, set the value of DEBUG in the ELASTIC_APM dictionary to True:

app.config['ELASTIC_APM'] = {
        'SERVICE_NAME': '<SERVICE-NAME>',
        'SECRET_TOKEN': '<SECRET-TOKEN>',
        'DEBUG': True
}
Building applications on the fly?edit

You can use the agent’s init_app hook for adding the application on the fly:

from elasticapm.contrib.flask import ElasticAPM
apm = ElasticAPM()

def create_app():
    app = Flask(__name__)
    apm.init_app(app, service_name='<SERVICE-NAME>', secret_token='<SECRET-TOKEN>')
    return app

Usageedit

Once you have configured the agent, it will automatically track transactions and capture uncaught exceptions within Flask. If you want to send additional events, a couple of shortcuts are provided on the ElasticAPM Flask middleware object by raising an exception or logging a generic message.

Capture an arbitrary exception by calling capture_exception:

try:
    1 / 0
except ZeroDivisionError:
    apm.capture_exception()

Log a generic message with capture_message:

apm.capture_message('hello, world!')

Shipping Logs to Elasticsearchedit

Enable automatic log shipping by passing logging=LEVEL to the ElasticAPM constructor. The agent will then ship all log messages with the given log-level or higher, from Python’s built-in logging module to Elasticsearch. LEVEL must be one of the levels defined in the logging module.

import logging

from elasticapm.contrib.flask import ElasticAPM

apm = ElasticAPM(app, logging=logging.ERROR)

To ship all log messages independent of log levels to Elastic APM, pass logging=True to the constructor:

from elasticapm.contrib.flask import ElasticAPM

apm = ElasticAPM(app, logging=True)

using logging=True can lead to high number of logs being sent to Elastic APM. We recommend to always limit logging with Elastic APM to a high level (WARNING or ERROR). For shipping of less urgent logs, we recommend to use filebeat.

For fine-grained control, you can initialize a logging handler and add it, just as you would with any other handler.

For information on how to increase the log verbosity from the APM agent, see troubleshooting agent logging.

from flask import Flask

from elasticapm.contrib.flask import ElasticAPM
from elasticapm.handlers.logging import LoggingHandler

app = Flask(__name__)

apm = ElasticAPM(app)

if __name__ == '__main__':
    # Create a logging handler and attach it.
    handler = LoggingHandler(client=apm.client)
    handler.setLevel(logging.WARN)
    app.logger.addHandler(handler)

You can also capture exceptions and send them explicitly:

@app.route('/')
def bar():
    try:
        1 / 0
    except ZeroDivisionError:
        app.logger.error( 'I cannot math', exc_info=True)

exc_info=True adds the exception info to the data that gets sent to the APM Server. Without it, only the message is sent.

Extra dataedit

In addition to what the agents log by default, you can send extra information:

@app.route('/')
def bar():
    try:
        1 / 0
    except ZeroDivisionError:
        app.logger.error('Math is hard',
            exc_info=True,
            extra={
                'good_at_math': False,
            }
        )
    )
Celery tasksedit

The Elastic APM agent will automatically send errors and performance data from your Celery tasks to the APM Server.

Performance metricsedit

If you’ve followed the instructions above, the agent has already hooked into the right signals and should be reporting performance metrics.

Ignoring specific routesedit

You can use the 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:

app.config['ELASTIC_APM'] = {
    ...
    'TRANSACTIONS_IGNORE_PATTERNS': ['^OPTIONS ', '/api/']
    ...
}

This would ignore any requests using the OPTIONS method and any requests containing /api/.

Integrating with the RUM Agentedit

To correlate performance measurement in the browser with measurements in your Flask app, you can help the RUM (Real User Monitoring) agent by configuring it with the Trace ID and Span ID of the backend request. We provide a handy template context processor which adds all the necessary bits into the context of your templates.

The context processor is installed automatically when you initialize ElasticAPM. All that is left to do is to update the call to initialize the RUM agent (which probably happens in your base template) like this:

elasticApm.init({
    serviceName: "my-frontend-service",
    pageLoadTraceId: "{{ apm["trace_id"] }}",
    pageLoadSpanId: "{{ apm["span_id"]() }}",
    pageLoadSampled: {{ apm["is_sampled_js"] }}
})

See the JavaScript RUM agent documentation for more information.

Supported Flask and Python versionsedit

A list of supported Flask and Python versions can be found on our Supported Technologies page.