Package: prometheus-flask-exporter
Prometheus Flask exporterThis library provides HTTP request metrics to export intoPrometheus.
It can also track method invocations using convenient functions.InstallingInstall usingPIP:pipinstallprometheus-flask-exporteror paste it into requirements.txt:# newest version
prometheus-flask-exporter

# or with specific version number
prometheus-flask-exporter==0.23.0and then install dependencies from requirements.txt file as usual:pip install -r requirements.txtUsagefromflaskimportFlask,requestfromprometheus_flask_exporterimportPrometheusMetricsapp=Flask(__name__)metrics=PrometheusMetrics(app)# static information as metricmetrics.info('app_info','Application info',version='1.0.3')@app.route('/')defmain():pass# requests tracked by default@app.route('/skip')@metrics.do_not_track()defskip():pass# default metrics are not collected@app.route('/<item_type>')@metrics.do_not_track()@metrics.counter('invocation_by_type','Number of invocations by type',labels={'item_type':lambda:request.view_args['type']})defby_type(item_type):pass# only the counter is collected, not the default metrics@app.route('/long-running')@metrics.gauge('in_progress','Long running requests in progress')deflong_running():pass@app.route('/status/<int:status>')@metrics.do_not_track()@metrics.summary('requests_by_status','Request latencies by status',labels={'status':lambdar:r.status_code})@metrics.histogram('requests_by_status_and_path','Request latencies by status and path',labels={'status':lambdar:r.status_code,'path':lambda:request.path})defecho_status(status):return'Status:%s'%status,statusDefault metricsThe following metrics are exported by default
(unless theexport_defaultsis set toFalse).flask_http_request_duration_seconds(Histogram)
Labels:method,pathandstatus.
Flask HTTP request duration in seconds for all Flask requests.flask_http_request_total(Counter)
Labels:methodandstatus.
Total number of HTTP requests for all Flask requests.flask_http_request_exceptions_total(Counter)
Labels:methodandstatus.
Total number of uncaught exceptions when serving Flask requests.flask_exporter_info(Gauge)
Information about the Prometheus Flask exporter itself (e.g.version).The prefix for the default metrics can be controlled by thedefaults_prefixparameter.
If you don't want to use any prefix, pass theprometheus_flask_exporter.NO_PREFIXvalue in.
The buckets on the default request latency histogram can be changed by thebucketsparameter, and if using a summary for them is more appropriate for your use case, then use thedefault_latency_as_histogram=Falseparameter.To register your owndefaultmetrics that will track all registered
Flask view functions, use theregister_defaultfunction.app=Flask(__name__)metrics=PrometheusMetrics(app)@app.route('/simple')defsimple_get():passmetrics.register_default(metrics.counter('by_path_counter','Request count by request paths',labels={'path':lambda:request.path}))Note:register your default metrics after all routes have been set up.
Also note, that Gauge metrics registered as default will track the/metricsendpoint, and this can't be disabled at the moment.If you want to apply the same metric to multiple (but not all) endpoints,
create its wrapper first, then add to each function.app=Flask(__name__)metrics=PrometheusMetrics(app)by_path_counter=metrics.counter('by_path_counter','Request count by request paths',labels={'path':lambda:request.path})@app.route('/simple')@by_path_counterdefsimple_get():pass@app.route('/plain')@by_path_counterdefplain():pass@app.route('/not/tracked/by/path')defnot_tracked_by_path():passYou can avoid recording metrics on individual endpoints
by decorating them with@metrics.do_not_track(), or use theexcluded_pathsargument when creating thePrometheusMetricsinstance
that takes a regular expression (either a single string, or a list) and
matching paths will be excluded. These apply to both built-in and user-defined
default metrics, unless you disable it by setting theexclude_user_defaultsargument toFalse. If you have functions that are inherited or otherwise get
metrics collected that you don't want, you can use@metrics.exclude_all_metrics()to exclude both default and non-default metrics being collected from it.ConfigurationBy default, the metrics are exposed on the same Flask application on the/metricsendpoint and using the core Prometheus registry.
If this doesn't suit your needs, set thepathargument toNoneand/or
theexport_defaultsargument toFalseplus change theregistryargument if needed.Thegroup_byconstructor argument controls what
the default request duration metric is tracked by: endpoint (function)
instead of URI path (the default). This parameter also accepts a function
to extract the value from the request, or a name of a property of the request object.
Examples:PrometheusMetrics(app,group_by='path')# the defaultPrometheusMetrics(app,group_by='endpoint')# by endpointPrometheusMetrics(app,group_by='url_rule')# by URL ruledefcustom_rule(req):# the Flask request object""" The name of the function becomes the label name. """return'%s::%s'%(req.method,req.path)PrometheusMetrics(app,group_by=custom_rule)# by a function# Error: this is not supported:PrometheusMetrics(app,group_by=lambdar:r.path)Thegroup_by_endpointargument is deprecated since 0.4.0,
please use the newgroup_byargument.Theregister_endpointallows exposing the metrics endpoint on a specific path.
It also allows passing in a Flask application to register it on but defaults
to the main one if not defined.Similarly, thestart_http_serverallows exposing the endpoint on an
independent Flask application on a selected HTTP port.
It also supports overriding the endpoint's path and the HTTP listen address.You can also set default labels to add to every request managed by
aPrometheusMetricsinstance, using thedefault_labelsargument.
This needs to be a dictionary, where each key will become a metric
label name, and the values the label values.
These can be constant values, or dynamic functions, see below in theLabelssection.Thestatic_labelsargument is deprecated since 0.15.0,
please use the newdefault_labelsargument.If you use another framework over Flask (perhapsConnexion) then you might return
responses from your endpoints that Flask can't deal with by default.
If that is the case, you might need to pass in aresponse_converterthat takes the returned object and should convert that to a Flask
friendly response.
SeeConnexionPrometheusMetricsfor an example.LabelsWhen defining labels for metrics on functions,
the following values are supported in the dictionary:A simple static valueA no-argument callableA single argument callable that will receive the Flask response
as the argumentLabel values are evaluated within the request context.Initial metric valuesFor more info see:https://github.com/prometheus/client_python#labelsMetrics without any labels will get an initial value.
Metrics that only have static-value labels will also have an initial value. (except when they are created with the optioninitial_value_when_only_static_labels=False)
Metrics that have one or more callable-value labels will not have an initial value.Application informationThePrometheusMetrics.info(..)method provides a way to expose
information as aGaugemetric, the application version for example.The metric is returned from the method to allow changing its value
from the default1:metrics=PrometheusMetrics(app)info=metrics.info('dynamic_info','Something dynamic')...info.set(42.1)ExamplesSee some simple examples visualized on a Grafana dashboard by running
the demo in theexamples/sample-signalsfolder.App Factory PatternThis library also supports the Flaskapp factory pattern. Use theinit_appmethod to attach the library to one or more application objects. Note, that to use this mode, you'll need to use thefor_app_factory()class method to create themetricsinstance, or pass inNonefor theappin the constructor.metrics=PrometheusMetrics.for_app_factory()# then later:metrics.init_app(app)Securing the metrics endpointIf you wish to have authentication (or any other special handling) on the metrics endpoint,
you can use themetrics_decoratorargument when creating thePrometheusMetricsinstance.
For example to integrate withFlask-HTTPAuthuse it like it's shown in the example below.app=Flask(__name__)auth=HTTPBasicAuth()metrics=PrometheusMetrics(app,metrics_decorator=auth.login_required)# ... other authentication setup like @auth.verify_password belowSee a full example in theexamples/flask-httpauthfolder.Custom metrics endpointYou can also take full control of the metrics endpoint by generating its contents,
and managing how it is exposed by yourself.app=Flask(__name__)# path=None to avoid registering a /metrics endpoint on the same Flask appmetrics=PrometheusMetrics(app,path=None)# later ... generate the response (and its content type) to expose to Prometheusresponse_data,content_type=metrics.generate_metrics()See the related conversation inissue #135.Debug modePlease note, that changes being live-reloaded, when running the Flask
app withdebug=True, are not going to be reflected in the metrics.
Seehttps://github.com/rycus86/prometheus_flask_exporter/issues/4for more details.Alternatively - since version0.5.1- if you set theDEBUG_METRICSenvironment variable, you will get metrics for the latest reloaded code.
These will be exported on the main Flask app.
Serving the metrics on a different port is not going to work
most probably - e.g.PrometheusMetrics.start_http_server(..)is not
expected to work.WSGIGetting accurate metrics for WSGI apps might require a bit more setup.
See a working sample app in theexamplesfolder, and also theprometheus_flask_exporter#5issue.Multiprocess applicationsFor multiprocess applications (WSGI or otherwise), you can find some
helper classes in theprometheus_flask_exporter.multiprocessmodule.
These provide convenience wrappers for exposing metrics in an
environment where multiple copies of the application will run on a single host.# an extension targeted at Gunicorn deploymentsfromprometheus_flask_exporter.multiprocessimportGunicornPrometheusMetricsapp=Flask(__name__)metrics=GunicornPrometheusMetrics(app)# then in the Gunicorn config file:fromprometheus_flask_exporter.multiprocessimportGunicornPrometheusMetricsdefwhen_ready(server):GunicornPrometheusMetrics.start_http_server_when_ready(8080)defchild_exit(server,worker):GunicornPrometheusMetrics.mark_process_dead_on_child_exit(worker.pid)Also see theGunicornInternalPrometheusMetricsclass if you want to have
the metrics HTTP endpoint exposed internally, on the same Flask application.# an extension targeted at Gunicorn deployments with an internal metrics endpointfromprometheus_flask_exporter.multiprocessimportGunicornInternalPrometheusMetricsapp=Flask(__name__)metrics=GunicornInternalPrometheusMetrics(app)# then in the Gunicorn config file:fromprometheus_flask_exporter.multiprocessimportGunicornInternalPrometheusMetricsdefchild_exit(server,worker):GunicornInternalPrometheusMetrics.mark_process_dead_on_child_exit(worker.pid)There's a small wrapper available forGunicornanduWSGI, for everything
else you can extend theprometheus_flask_exporter.multiprocess.MultiprocessPrometheusMetricsclass
and implement theshould_start_http_servermethod at least.fromprometheus_flask_exporter.multiprocessimportMultiprocessPrometheusMetricsclassMyMultiprocessMetrics(MultiprocessPrometheusMetrics):defshould_start_http_server(self):returnthis_worker()==primary_worker()This should returnTrueon one process only, and the underlyingPrometheus client librarywill collect the metrics for all the forked children or siblings.An additional Flask extension for apps withprocesses=Nandthreaded=Falseexists
with theMultiprocessInternalPrometheusMetricsclass.fromflaskimportFlaskfromprometheus_flask_exporter.multiprocessimportMultiprocessInternalPrometheusMetricsapp=Flask(__name__)metrics=MultiprocessInternalPrometheusMetrics(app)...if__name__=='__main__':app.run('0.0.0.0',4000,processes=5,threaded=False)Note:this needs thePROMETHEUS_MULTIPROC_DIRenvironment variable
to point to a valid, writable directory.You'll also have to call themetrics.start_http_server()function
explicitly somewhere, and theshould_start_http_servertakes care of
only starting it once.
Theexamplesfolder
has some working examples on this.Please also note, that the Prometheus client library does not collect process level
metrics, like memory, CPU and Python GC stats when multiprocessing is enabled.
See theprometheus_flask_exporter#18issue for some more context and details.A final caveat is that the metrics HTTP server will listen onanypaths
on the given HTTP port, not only on/metrics, and it is not implemented
at the moment to be able to change this.uWSGI lazy-appsWhenuWSGIis configured
to run withlazy-apps,
exposing the metrics endpoint on a separate HTTP server (and port) is not functioning yet.
A workaround is to register the endpoint on the main Flask application.app=Flask(__name__)metrics=UWsgiPrometheusMetrics(app)metrics.register_endpoint('/metrics')# instead of metrics.start_http_server(port)See#31for context, and please let me know if you know a better way!Connexion integrationTheConnexionlibrary has some
support to automatically deal with certain response types, for example
dataclasses, which a plain Flask application would not accept.
To ease the integration, you can useConnexionPrometheusMetricsin
place ofPrometheusMetricsthat has theresponse_converterset
appropriately to be able to deal with whatever Connexion supports for
Flask integrations.importconnexionfromprometheus_flask_exporterimportConnexionPrometheusMetricsapp=connexion.App(__name__)metrics=ConnexionPrometheusMetrics(app)See a working sample app in theexamplesfolder, and also theprometheus_flask_exporter#61issue.There's a caveat about this integration, where any endpoints that
do not return JSON responses need to be decorated with@metrics.content_type('...')as this integration would force them
to beapplication/jsonotherwise.metrics=ConnexionPrometheusMetrics(app)@metrics.content_type('text/plain')defplain_response():return'plain text'See theprometheus_flask_exporter#64issue for more details.Flask-RESTful integrationTheFlask-RESTful libraryhas
some custom response handling logic, which can be helpful in some cases.
For example, returningNonewould fail on plain Flask, but it
works on Flask-RESTful.
To ease the integration, you can useRESTfulPrometheusMetricsin
place ofPrometheusMetricsthat sets theresponse_converterto use
the Flask-RESTfulAPIresponse utilities.fromflaskimportFlaskfromflask_restfulimportApifromprometheus_flask_exporterimportRESTfulPrometheusMetricsapp=Flask(__name__)restful_api=Api(app)metrics=RESTfulPrometheusMetrics(app,restful_api)See a working sample app in theexamplesfolder, and also theprometheus_flask_exporter#62issue.LicenseMIT