Prometheus exporter for Flask applications

Overview

Prometheus Flask exporter

PyPI PyPI PyPI - Downloads Travis Coverage Status Code Climate

This library provides HTTP request metrics to export into Prometheus. It can also track method invocations using convenient functions.

Installing

Install using PIP:

pip install prometheus-flask-exporter

or paste it into requirements.txt:

# newest version
prometheus-flask-exporter

# or with specific version number
prometheus-flask-exporter==0.18.1

and then install dependencies from requirements.txt file as usual:

pip install -r requirements.txt

Usage

from flask import Flask, request
from prometheus_flask_exporter import PrometheusMetrics

app = Flask(__name__)
metrics = PrometheusMetrics(app)

# static information as metric
metrics.info('app_info', 'Application info', version='1.0.3')

@app.route('/')
def main():
    pass  # requests tracked by default

@app.route('/skip')
@metrics.do_not_track()
def skip():
    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']})
def by_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')
def long_running():
    pass

@app.route('/status/<int:status>')
@metrics.do_not_track()
@metrics.summary('requests_by_status', 'Request latencies by status',
                 labels={'status': lambda r: r.status_code})
@metrics.histogram('requests_by_status_and_path', 'Request latencies by status and path',
                   labels={'status': lambda r: r.status_code, 'path': lambda: request.path})
def echo_status(status):
    return 'Status: %s' % status, status

Default metrics

The following metrics are exported by default (unless the export_defaults is set to False).

  • flask_http_request_duration_seconds (Histogram) Labels: method, path and status. Flask HTTP request duration in seconds for all Flask requests.
  • flask_http_request_total (Counter) Labels: method and status. Total number of HTTP requests for all Flask requests.
  • flask_http_request_exceptions_total (Counter) Labels: method and status. 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 the defaults_prefix parameter. If you don't want to use any prefix, pass the prometheus_flask_exporter.NO_PREFIX value in. The buckets on the default request latency histogram can be changed by the buckets parameter, and if using a summary for them is more appropriate for your use case, then use the default_latency_as_histogram=False parameter.

To register your own default metrics that will track all registered Flask view functions, use the register_default function.

app = Flask(__name__)
metrics = PrometheusMetrics(app)

@app.route('/simple')
def simple_get():
    pass
    
metrics.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 /metrics endpoint, 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_counter
def simple_get():
    pass
    
@app.route('/plain')
@by_path_counter
def plain():
    pass
    
@app.route('/not/tracked/by/path')
def not_tracked_by_path():
    pass

You can avoid recording metrics on individual endpoints by decorating them with @metrics.do_not_track(), or use the excluded_paths argument when creating the PrometheusMetrics instance that takes a regular expression (either a single string, or a list) and matching paths will be excluded. 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.

Configuration

By default, the metrics are exposed on the same Flask application on the /metrics endpoint and using the core Prometheus registry. If this doesn't suit your needs, set the path argument to None and/or the export_defaults argument to False plus change the registry argument if needed.

The group_by constructor 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 default
PrometheusMetrics(app, group_by='endpoint')     # by endpoint
PrometheusMetrics(app, group_by='url_rule')     # by URL rule

def custom_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=lambda r: r.path)

The group_by_endpoint argument is deprecated since 0.4.0, please use the new group_by argument.

The register_endpoint allows 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, the start_http_server allows 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 a PrometheusMetrics instance, using the default_labels argument. 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 the Labels section.

The static_labels argument is deprecated since 0.15.0, please use the new default_labels argument.

If you use another framework over Flask (perhaps Connexion) 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 a response_converter that takes the returned object and should convert that to a Flask friendly response. See ConnexionPrometheusMetrics for an example.

Labels

When defining labels for metrics on functions, the following values are supported in the dictionary:

  • A simple static value
  • A no-argument callable
  • A single argument callable that will receive the Flask response as the argument

Label values are evaluated within the request context.

Application information

The PrometheusMetrics.info(..) method provides a way to expose information as a Gauge metric, the application version for example.

The metric is returned from the method to allow changing its value from the default 1:

metrics = PrometheusMetrics(app)
info = metrics.info('dynamic_info', 'Something dynamic')
...
info.set(42.1)

Examples

See some simple examples visualized on a Grafana dashboard by running the demo in the examples/sample-signals folder.

Example dashboard

App Factory Pattern

This library also supports the Flask app factory pattern. Use the init_app method to attach the library to one or more application objects. Note, that to use this mode, you'll need to use the for_app_factory() class method to create the metrics instance, or pass in None for the app in the constructor.

metrics = PrometheusMetrics.for_app_factory()
# then later:
metrics.init_app(app)

Securing the metrics endpoint

If you wish to have authentication (or any other special handling) on the metrics endpoint, you can use the metrics_decorator argument when creating the PrometheusMetrics instance. For example to integrate with Flask-HTTPAuth use 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 below

See a full example in the examples/flask-httpauth folder.

Debug mode

Please note, that changes being live-reloaded, when running the Flask app with debug=True, are not going to be reflected in the metrics. See https://github.com/rycus86/prometheus_flask_exporter/issues/4 for more details.

Alternatively - since version 0.5.1 - if you set the DEBUG_METRICS environment 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.

WSGI

Getting accurate metrics for WSGI apps might require a bit more setup. See a working sample app in the examples folder, and also the prometheus_flask_exporter#5 issue.

Multiprocess applications

For multiprocess applications (WSGI or otherwise), you can find some helper classes in the prometheus_flask_exporter.multiprocess module. 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 deployments
from prometheus_flask_exporter.multiprocess import GunicornPrometheusMetrics

app = Flask(__name__)
metrics = GunicornPrometheusMetrics(app)

# then in the Gunicorn config file:
from prometheus_flask_exporter.multiprocess import GunicornPrometheusMetrics

def when_ready(server):
    GunicornPrometheusMetrics.start_http_server_when_ready(8080)

def child_exit(server, worker):
    GunicornPrometheusMetrics.mark_process_dead_on_child_exit(worker.pid)

Also see the GunicornInternalPrometheusMetrics class 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 endpoint
from prometheus_flask_exporter.multiprocess import GunicornInternalPrometheusMetrics

app = Flask(__name__)
metrics = GunicornInternalPrometheusMetrics(app)

# then in the Gunicorn config file:
from prometheus_flask_exporter.multiprocess import GunicornInternalPrometheusMetrics

def child_exit(server, worker):
    GunicornInternalPrometheusMetrics.mark_process_dead_on_child_exit(worker.pid)

There's a small wrapper available for Gunicorn and uWSGI, for everything else you can extend the prometheus_flask_exporter.multiprocess.MultiprocessPrometheusMetrics class and implement the should_start_http_server method at least.

from prometheus_flask_exporter.multiprocess import MultiprocessPrometheusMetrics

class MyMultiprocessMetrics(MultiprocessPrometheusMetrics):
    def should_start_http_server(self):
        return this_worker() == primary_worker()

This should return True on one process only, and the underlying Prometheus client library will collect the metrics for all the forked children or siblings.

Note: this needs the prometheus_multiproc_dir environment variable to point to a valid, writable directory.

You'll also have to call the metrics.start_http_server() function explicitly somewhere, and the should_start_http_server takes care of only starting it once. The examples folder 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 the prometheus_flask_exporter#18 issue for some more context and details.

A final caveat is that the metrics HTTP server will listen on any paths 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-apps

When uWSGI is configured to run with lazy-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 #31 for context, and please let me know if you know a better way!

Connexion integration

The Connexion library 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 use ConnexionPrometheusMetrics in place of PrometheusMetrics that has the response_converter set appropriately to be able to deal with whatever Connexion supports for Flask integrations.

import connexion
from prometheus_flask_exporter import ConnexionPrometheusMetrics

app = connexion.App(__name__)
metrics = ConnexionPrometheusMetrics(app)

See a working sample app in the examples folder, and also the prometheus_flask_exporter#61 issue.

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 be application/json otherwise.

metrics = ConnexionPrometheusMetrics(app)

@metrics.content_type('text/plain')
def plain_response():
    return 'plain text'

See the prometheus_flask_exporter#64 issue for more details.

Flask-RESTful integration

The Flask-RESTful library has some custom response handling logic, which can be helpful in some cases. For example, returning None would fail on plain Flask, but it works on Flask-RESTful. To ease the integration, you can use RESTfulPrometheusMetrics in place of PrometheusMetrics that sets the response_converter to use the Flask-RESTful API response utilities.

from flask import Flask
from flask_restful import Api
from prometheus_flask_exporter import RESTfulPrometheusMetrics

app = Flask(__name__)
restful_api = Api(app)
metrics = RESTfulPrometheusMetrics(app, restful_api)

See a working sample app in the examples folder, and also the prometheus_flask_exporter#62 issue.

License

MIT

Comments
  • wsgi daemon mode

    wsgi daemon mode

    I run my stateless flask apps with mod_wsgi/apache using daemon mode like:

    WSGIDaemonProcess foo-services python-home=/opt/my_org/foo-services/_env processes=8 threads=48 maximum-requests=10000 display-name=%{GROUP}
    WSGIApplicationGroup %{GLOBAL}
    WSGISocketPrefix /var/run/wsgi
    
    
    Alias /image-services "/opt/my_org/foo-services/wsgi.py"
    <Location "/for-services">
    SetHandler wsgi-script
    Options +ExecCGI
    FileETag None
    ExpiresActive On
    ExpiresDefault "access plus 1 year"
    WSGIProcessGroup image-services
    </Location>
    
    

    Which means when a request gets to the service it could be hitting 1 of 8 daemon processes each of which have their own memory in isolation of the others. Does the metrics endpoint store the prometheus data in a way that is shared across these daemons?

    I can create some tests to verify if thats the case or not, just curious if the answer is already known.

    Thanks, Thatcher

    opened by thatcher 30
  • PrometheusMetrics for werkzeug

    PrometheusMetrics for werkzeug

    Do we support metrics for multiprocessed werkzeug server flask applicaiton? Is there an example how to set the metrics?

    I am running the application like below app.run(host='0.0.0.0', port=8080, processes=10, threaded=False)

    opened by Velan987 27
  • Usage with flaskRESTFUL

    Usage with flaskRESTFUL

    How can metrics be logged and customized when API is constructed using flaskRESTful library, where instead of routes, you have:

    class Coordinates(Resource):
        def __init__(self):
            self.parser = reqparse.RequestParser()
            self.parser.add_argument('radius', type=float, required=False, help="Radius", location='args', default=1)
    
        def post(self):
            args = self.parser.parse_args()
    
    opened by captify-dieter 25
  • Handle 500 errors when an exception is raised inside a route

    Handle 500 errors when an exception is raised inside a route

    Sometimes when exceptions are raised inside endpoints, requests won't reach the "after_request" step. On the other hand, they will always pass through "teardown_request". We can use teardown_request in order to always handle 500 errors and bump the needed metrics.

    opened by kabooboo 16
  • use the init_app style for use with app factory pattern

    use the init_app style for use with app factory pattern

    Hey,

    I'd love to use this in my flask app, but we use the "app factory" pattern. http://flask.pocoo.org/docs/0.12/extensiondev/#the-extension-code shows an example of this. I tried to do two things here:

    1. Allow no app object to be passed to the constructor.
    2. Use current_app instead of self.app (See the "Note on init_app" near the bottom of that linked section)

    The **kwargs basically copying the constructor args might be a little awkward, but is there for backwards compatibility? Maybe? You could also scrap that, and just leave the constructor args, and init_app just take the app object.

    In any case, if you're into this, or would like some changes, please let me know.

    opened by hoopes 16
  • Option to add new metrics or new lable

    Option to add new metrics or new lable

    Hello,

    I there an option to add a new metrics or new lable to the existing metrics? for example, i want to add a lable with the requester ip to the default metric

    or add a new metric that contain the location data of the requester ip.

    I tried to use the group by, but it overwrite the path lable, and i cant add more than one lable.

    Thanks, Idan

    opened by BuSHari 15
  • After MultiProcessCollector used, the exposed endpoint `/metrics` has no value returned

    After MultiProcessCollector used, the exposed endpoint `/metrics` has no value returned

    I'm using MultiProcessCollector with UWsgiPrometheusMetrics, why does the endpoint /metrics return none?

    I have tried some of the cases in the examples and found that none of them worked.

    opened by YaoJusheng 14
  • The function either returned None or ended without a return statement while using with flask_restful

    The function either returned None or ended without a return statement while using with flask_restful

    flask_restful supports returning None from the view function, but I am getting error this error when I use metrics for endpoint.

    e.g

    
    from flask_restful import Resource
    
    by_path_counter = metrics.counter(
        'by_path_counter', 'Request count by request paths',
        labels={'path': lambda: request.path}
    )
    
    class FoodApi(Resource):
        @by_path_counter
        def post(self):
            create_food()
            return None, 200
    

    It works fine if I don't use @by_path_counter decorator, and returns null in the response.

    But when I add the decorator, I get this error.

    Traceback (most recent call last):
      File "/home/wartner/.local/share/virtualenvs/config_service-TDer8cy_/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
        rv = self.dispatch_request()
      File "/home/wartner/.local/share/virtualenvs/config_service-TDer8cy_/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
        return self.view_functions[rule.endpoint](**req.view_args)
      File "/home/wartner/.local/share/virtualenvs/config_service-TDer8cy_/lib/python3.8/site-packages/flask_restful/__init__.py", line 468, in wrapper
        resp = resource(*args, **kwargs)
      File "/home/wartner/.local/share/virtualenvs/config_service-TDer8cy_/lib/python3.8/site-packages/flask/views.py", line 89, in view
        return self.dispatch_request(*args, **kwargs)
      File "/home/wartner/.local/share/virtualenvs/config_service-TDer8cy_/lib/python3.8/site-packages/flask_restful/__init__.py", line 583, in dispatch_request
        resp = meth(*args, **kwargs)
      File "/home/wartner/.local/share/virtualenvs/config_service-TDer8cy_/lib/python3.8/site-packages/prometheus_flask_exporter/__init__.py", line 633, in func
        response = make_response(response)
      File "/home/wartner/.local/share/virtualenvs/config_service-TDer8cy_/lib/python3.8/site-packages/flask/helpers.py", line 223, in make_response
        return current_app.make_response(args)
      File "/home/wartner/.local/share/virtualenvs/config_service-TDer8cy_/lib/python3.8/site-packages/flask/app.py", line 2097, in make_response
        raise TypeError(
    TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.
    

    Is there any way, I can work around this? (I have to return None)

    Any kind of help will be highly appreciated, thank you :slightly_smiling_face:

    Note: It works fine on other view functions where I return not None values

    opened by paurakhsharma 12
  • Exclude Paths Issue

    Exclude Paths Issue

    Not sure if this is necessarily an issue or I'm just not sure how to do it. My project uses Swagger docs and our API of course receives requests for those webpages. We are not interested in tracking those endpoints. Right now I create a histogram and a counter to run on all of our endpoints using the register_default method. I assume I can use the excluded_paths to not track any metrics for endpoints matching the regex expressions passed in as a list for this argument.

    I use the following argument when creating the PrometheusMetrics object: excluded_paths=["^/swagger/.*$"]

    However I am still seeing the following when I hit the metrics endpoint: image

    Is there something I am obviously doing wrong here?

    opened by jimcmahon 10
  • Prometheus Flask exporter with __main__

    Prometheus Flask exporter with __main__

    I want to use Prometheus Flask exporter with __main__.

    This works fine by running env FLASK_APP=app.py flask run --port=80 --host='0.0.0.0':

    from flask import Flask
    from prometheus_flask_exporter import PrometheusMetrics
    
    app = Flask(__name__)
    metrics = PrometheusMetrics(app)
    
    app.debug = True
    
    @app.route("/", methods=['GET'])
    def index():
        return "hello world"
    

    But I want to use my app in __main__, running python app.py.

    from flask import Flask
    from prometheus_flask_exporter import PrometheusMetrics
    
    app = Flask(__name__)
    metrics = PrometheusMetrics(app=None, path='/metrics')
    
    app.debug = True
    
    @app.route("/", methods=['GET'])
    def index():
        return "hello world"
    
    if __name__ == '__main__':
        metrics.init_app(app)
        app.run(host='0.0.0.0', port=80)
    

    Here I get 400 on /metrics.

    I got no clue how to init metrics correctly.

    thx for helping klml

    PS I asked this already stackoverflow.com, but got no answer.

    opened by klml 10
  • Allow empty prefixes for default metrics

    Allow empty prefixes for default metrics

    Hey there!

    I've just started using prometheus_flask_exporter and it does everything I want except allowing default metrics to not have prefix at all. The reason for that is that we are already exporting some metrics from other applications and we want them to have the same name if possible. Since those metrics are coming from different stacks/languages having flask_ prefix is making this a bit problematic. I've modified the code to allow setting defaults_prefix to be empty string. It should not be a problem since all functions that deal with default metrics have sane defaults.

    Let me know what you think about it.

    opened by harnash 10
  • Multiprocess application sometimes throwing UnicodeDecodeError

    Multiprocess application sometimes throwing UnicodeDecodeError

    Hi I have multiprocess enabled application, sometimes it is throwing below error File "usr/local/lib/python3.8/site-packages/prometheus_client/mmap_dict.py", line 44, in _read_all_values yield encoded_key.decode('utf-8'), value, pos UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf0 in position 18: invalid continuation byte.

    After this error my application is crashed. Can someone please help me on this?

    opened by Velan987 1
  • Streaming responses and latency

    Streaming responses and latency

    What's the best way to measure duration for streaming endpoints?

    If I'm not mistaken, the current latency measurements don't work for streaming responses. prometheus_flask_exporter measures the time to return the response generator rather than the time to actually generate the stream response.

    Flask's stream documentation gives an example of a streaming endpoint,

    @app.route('/large.csv')
    def generate_large_csv():
        def generate():
            for row in iter_all_rows():
                yield f"{','.join(row)}\n"
        return app.response_class(generate(), mimetype='text/csv')
    

    In this example, prometheus_flask_exporter would start a duration timer via Flask.before_request and then record the duration via Flask.after_request. When after_request is invoked, the actual response bytes haven't been generated or sent.

    I wonder if measuring via Flask.teardown_request along with stream_with_context() would work, but not sure.

    Thoughts appreciated!

    opened by bheilbrun 1
  • Support for push using Prometheus remote_write?

    Support for push using Prometheus remote_write?

    Prometheus 2.33 stabilizes the remote_write API. https://promlabs.com/blog/2022/01/30/whats-new-in-prometheus-2-33#remote-write-receiver-marked-stable

    While not intended to be a replacement for the pull-based model, it can be very practical from background workers and batch jobs to be able to push, without having to set up a gateway. Especially since the offical pushgateway is very limited, and other options like prom-aggregation-gateway are not activily maintained.

    What do you think?

    opened by jonnor 2
  • 🐞 TypeError: The view function for 'main' did not return a valid response.

    🐞 TypeError: The view function for 'main' did not return a valid response.

    Did it like described in https://github.com/rycus86/prometheus_flask_exporter#usage but with app.run(debug=True) at the end? 🤔

    > curl localhost:5000
    <!doctype html>
    <html lang=en>
      <head>
        <title>TypeError: The view function for 'main' did not return a valid response. The function either returned None or ended without a return statement.
     // Werkzeug Debugger</title>
        <link rel="stylesheet" href="?__debugger__=yes&amp;cmd=resource&amp;f=style.css">
        <link rel="shortcut icon"
            href="?__debugger__=yes&amp;cmd=resource&amp;f=console.png">
        <script src="?__debugger__=yes&amp;cmd=resource&amp;f=debugger.js"></script>
        <script>
          var CONSOLE_MODE = false,
              EVALEX = true,
              EVALEX_TRUSTED = false,
              SECRET = "Vdo3qNNRy8n4S2jNd334";
        </script>
      </head>
      <body style="background-color: #fff">
        <div class="debugger">
    <h1>TypeError</h1>
    <div class="detail">
      <p class="errormsg">TypeError: The view function for &#x27;main&#x27; did not return a valid response. The function either returned None or ended without a return statement.
    </p>
    </div>
    <h2 class="traceback">Traceback <em>(most recent call last)</em></h2>
    <div class="traceback">
      <h3></h3>
      <ul><li><div class="frame" id="frame-4369751440">
      <h4>File <cite class="filename">"/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py"</cite>,
          line <em class="line">2095</em>,
          in <code class="function">__call__</code></h4>
      <div class="source "><pre class="line before"><span class="ws">    </span>def __call__(self, environ: dict, start_response: t.Callable) -&gt; t.Any:</pre>
    <pre class="line before"><span class="ws">        </span>&quot;&quot;&quot;The WSGI server calls the Flask application object as the</pre>
    <pre class="line before"><span class="ws">        </span>WSGI application. This calls :meth:`wsgi_app`, which can be</pre>
    <pre class="line before"><span class="ws">        </span>wrapped to apply middleware.</pre>
    <pre class="line before"><span class="ws">        </span>&quot;&quot;&quot;</pre>
    <pre class="line current"><span class="ws">        </span>return self.wsgi_app(environ, start_response)</pre></div>
    </div>
    
    <li><div class="frame" id="frame-4369752336">
      <h4>File <cite class="filename">"/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py"</cite>,
          line <em class="line">2080</em>,
          in <code class="function">wsgi_app</code></h4>
      <div class="source "><pre class="line before"><span class="ws">            </span>try:</pre>
    <pre class="line before"><span class="ws">                </span>ctx.push()</pre>
    <pre class="line before"><span class="ws">                </span>response = self.full_dispatch_request()</pre>
    <pre class="line before"><span class="ws">            </span>except Exception as e:</pre>
    <pre class="line before"><span class="ws">                </span>error = e</pre>
    <pre class="line current"><span class="ws">                </span>response = self.handle_exception(e)</pre>
    <pre class="line after"><span class="ws">            </span>except:  # noqa: B001</pre>
    <pre class="line after"><span class="ws">                </span>error = sys.exc_info()[1]</pre>
    <pre class="line after"><span class="ws">                </span>raise</pre>
    <pre class="line after"><span class="ws">            </span>return response(environ, start_response)</pre>
    <pre class="line after"><span class="ws">        </span>finally:</pre></div>
    </div>
    
    <li><div class="frame" id="frame-4369752560">
      <h4>File <cite class="filename">"/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py"</cite>,
          line <em class="line">2077</em>,
          in <code class="function">wsgi_app</code></h4>
      <div class="source "><pre class="line before"><span class="ws">        </span>ctx = self.request_context(environ)</pre>
    <pre class="line before"><span class="ws">        </span>error: t.Optional[BaseException] = None</pre>
    <pre class="line before"><span class="ws">        </span>try:</pre>
    <pre class="line before"><span class="ws">            </span>try:</pre>
    <pre class="line before"><span class="ws">                </span>ctx.push()</pre>
    <pre class="line current"><span class="ws">                </span>response = self.full_dispatch_request()</pre>
    <pre class="line after"><span class="ws">            </span>except Exception as e:</pre>
    <pre class="line after"><span class="ws">                </span>error = e</pre>
    <pre class="line after"><span class="ws">                </span>response = self.handle_exception(e)</pre>
    <pre class="line after"><span class="ws">            </span>except:  # noqa: B001</pre>
    <pre class="line after"><span class="ws">                </span>error = sys.exc_info()[1]</pre></div>
    </div>
    
    <li><div class="frame" id="frame-4369752224">
      <h4>File <cite class="filename">"/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py"</cite>,
          line <em class="line">1526</em>,
          in <code class="function">full_dispatch_request</code></h4>
      <div class="source "><pre class="line before"><span class="ws">            </span>rv = self.preprocess_request()</pre>
    <pre class="line before"><span class="ws">            </span>if rv is None:</pre>
    <pre class="line before"><span class="ws">                </span>rv = self.dispatch_request()</pre>
    <pre class="line before"><span class="ws">        </span>except Exception as e:</pre>
    <pre class="line before"><span class="ws">            </span>rv = self.handle_user_exception(e)</pre>
    <pre class="line current"><span class="ws">        </span>return self.finalize_request(rv)</pre>
    <pre class="line after"><span class="ws"></span> </pre>
    <pre class="line after"><span class="ws">    </span>def finalize_request(</pre>
    <pre class="line after"><span class="ws">        </span>self,</pre>
    <pre class="line after"><span class="ws">        </span>rv: t.Union[ResponseReturnValue, HTTPException],</pre>
    <pre class="line after"><span class="ws">        </span>from_error_handler: bool = False,</pre></div>
    </div>
    
    <li><div class="frame" id="frame-4369751328">
      <h4>File <cite class="filename">"/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py"</cite>,
          line <em class="line">1545</em>,
          in <code class="function">finalize_request</code></h4>
      <div class="source "><pre class="line before"><span class="ws">        </span>with the `from_error_handler` flag.  If enabled, failures in</pre>
    <pre class="line before"><span class="ws">        </span>response processing will be logged and otherwise ignored.</pre>
    <pre class="line before"><span class="ws"></span> </pre>
    <pre class="line before"><span class="ws">        </span>:internal:</pre>
    <pre class="line before"><span class="ws">        </span>&quot;&quot;&quot;</pre>
    <pre class="line current"><span class="ws">        </span>response = self.make_response(rv)</pre>
    <pre class="line after"><span class="ws">        </span>try:</pre>
    <pre class="line after"><span class="ws">            </span>response = self.process_response(response)</pre>
    <pre class="line after"><span class="ws">            </span>request_finished.send(self, response=response)</pre>
    <pre class="line after"><span class="ws">        </span>except Exception:</pre>
    <pre class="line after"><span class="ws">            </span>if not from_error_handler:</pre></div>
    </div>
    
    <li><div class="frame" id="frame-4369752448">
      <h4>File <cite class="filename">"/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py"</cite>,
          line <em class="line">1701</em>,
          in <code class="function">make_response</code></h4>
      <div class="source "><pre class="line before"><span class="ws">                    </span>&quot; (body, status), or (body, headers).&quot;</pre>
    <pre class="line before"><span class="ws">                </span>)</pre>
    <pre class="line before"><span class="ws"></span> </pre>
    <pre class="line before"><span class="ws">        </span># the body must not be None</pre>
    <pre class="line before"><span class="ws">        </span>if rv is None:</pre>
    <pre class="line current"><span class="ws">            </span>raise TypeError(</pre>
    <pre class="line after"><span class="ws">                </span>f&quot;The view function for {request.endpoint!r} did not&quot;</pre>
    <pre class="line after"><span class="ws">                </span>&quot; return a valid response. The function either returned&quot;</pre>
    <pre class="line after"><span class="ws">                </span>&quot; None or ended without a return statement.&quot;</pre>
    <pre class="line after"><span class="ws">            </span>)</pre>
    <pre class="line after"><span class="ws"></span> </pre></div>
    </div>
    </ul>
      <blockquote>TypeError: The view function for &#x27;main&#x27; did not return a valid response. The function either returned None or ended without a return statement.
    </blockquote>
    </div>
    
    <div class="plain">
        <p>
          This is the Copy/Paste friendly version of the traceback.
        </p>
        <textarea cols="50" rows="10" name="code" readonly>Traceback (most recent call last):
      File &quot;/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py&quot;, line 2095, in __call__
        return self.wsgi_app(environ, start_response)
      File &quot;/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py&quot;, line 2080, in wsgi_app
        response = self.handle_exception(e)
      File &quot;/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py&quot;, line 2077, in wsgi_app
        response = self.full_dispatch_request()
      File &quot;/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py&quot;, line 1526, in full_dispatch_request
        return self.finalize_request(rv)
      File &quot;/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py&quot;, line 1545, in finalize_request
        response = self.make_response(rv)
      File &quot;/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py&quot;, line 1701, in make_response
        raise TypeError(
    TypeError: The view function for &#x27;main&#x27; did not return a valid response. The function either returned None or ended without a return statement.
    </textarea>
    </div>
    <div class="explanation">
      The debugger caught an exception in your WSGI application.  You can now
      look at the traceback which led to the error.  <span class="nojavascript">
      If you enable JavaScript you can also use additional features such as code
      execution (if the evalex feature is enabled), automatic pasting of the
      exceptions and much more.</span>
    </div>
          <div class="footer">
            Brought to you by <strong class="arthur">DON'T PANIC</strong>, your
            friendly Werkzeug powered traceback interpreter.
          </div>
        </div>
    
        <div class="pin-prompt">
          <div class="inner">
            <h3>Console Locked</h3>
            <p>
              The console is locked and needs to be unlocked by entering the PIN.
              You can find the PIN printed out on the standard output of your
              shell that runs the server.
            <form>
              <p>PIN:
                <input type=text name=pin size=14>
                <input type=submit name=btn value="Confirm Pin">
            </form>
          </div>
        </div>
      </body>
    </html>
    
    <!--
    
    Traceback (most recent call last):
      File "/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py", line 2095, in __call__
        return self.wsgi_app(environ, start_response)
      File "/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py", line 2080, in wsgi_app
        response = self.handle_exception(e)
      File "/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py", line 2077, in wsgi_app
        response = self.full_dispatch_request()
      File "/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py", line 1526, in full_dispatch_request
        return self.finalize_request(rv)
      File "/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py", line 1545, in finalize_request
        response = self.make_response(rv)
      File "/Users/tgotwig/.asdf/installs/python/3.10.5/lib/python3.10/site-packages/flask/app.py", line 1701, in make_response
        raise TypeError(
    TypeError: The view function for 'main' did not return a valid response. The function either returned None or ended without a return statement.
    
    
    -->
    
    opened by TGotwig 1
  • refactor: Use f-strings and simplify operations

    refactor: Use f-strings and simplify operations

    This replaces the use of lower-performance interpolation with f-strings and simplifies conditionals and file reading to match the supported Python versions as per the README.md badges

    opened by kkirsche 4
Prometheus integration for Starlette.

Starlette Prometheus Introduction Prometheus integration for Starlette. Requirements Python 3.6+ Starlette 0.9+ Installation $ pip install starlette-p

José Antonio Perdiguero 229 Dec 21, 2022
Exports osu! user stats to prometheus metrics for a specified set of users

osu! to prometheus exporter This tool exports osu! user statistics into prometheus metrics for a specified set of user ids. Just copy the config.json.

Peter Oettig 1 Feb 24, 2022
Automatically monitor the evolving performance of Flask/Python web services.

Flask Monitoring Dashboard A dashboard for automatic monitoring of Flask web-services. Key Features • How to use • Live Demo • Feedback • Documentatio

null 663 Dec 29, 2022
Display machine state using Python3 with Flask.

Flask-State English | 简体中文 Flask-State is a lightweight chart plugin for displaying machine state data in your web application. Monitored Metric: CPU,

null 622 Dec 18, 2022
Prometheus exporter for Flask applications

Prometheus Flask exporter This library provides HTTP request metrics to export into Prometheus. It can also track method invocations using convenient

Viktor Adam 535 Dec 23, 2022
Prometheus exporter for Starlette and FastAPI

starlette_exporter Prometheus exporter for Starlette and FastAPI. The middleware collects basic metrics: Counter: starlette_requests_total Histogram:

Steve Hillier 225 Jan 5, 2023
Prometheus exporter for Starlette and FastAPI

starlette_exporter Prometheus exporter for Starlette and FastAPI. The middleware collects basic metrics: Counter: starlette_requests_total Histogram:

Steve Hillier 82 Feb 13, 2021
Prometheus exporter for several chia node statistics

prometheus-chia-exporter Prometheus exporter for several chia node statistics It's assumed that the full node, the harvester and the wallet run on the

null 30 Sep 19, 2022
A Prometheus exporter for monitoring & analyzing Grafana Labs' technical documentation

grafana-docs-exporter A Prometheus exporter for monitoring & analyzing Grafana Labs' technical documentation Here is the public endpoint.

Matt Abrams 5 May 2, 2022
A prometheus exporter for torrent downloader like qbittorrent/transmission/deluge

downloader-exporter A prometheus exporter for qBitorrent/Transmission/Deluge. Get metrics from multiple servers and offers them in a prometheus format

Lei Shi 41 Nov 18, 2022
Prometheus exporter for Spigot accounts

SpigotExporter Prometheus exporter for Spigot accounts What it provides SpigotExporter will output metrics for each of your plugins and a cumulative d

Jacob Bashista 5 Dec 20, 2021
Prometheus exporter for CNMC API

CNMC Prometheus exporter It needs a Prometheus Pushgateway Install requirements via pip install -r requirements.txt Export the following environment v

GISCE-TI 1 Oct 20, 2021
Prometheus exporter for AWS Simple Queue Service (SQS)

Prometheus SQS Exporter Prometheus exporter for AWS Simple Queue Service (SQS) Metrics Metric Description ApproximateNumberOfMessages Returns the appr

Gabriel M. Dutra 0 Jan 31, 2022
Rundeck / Grafana / Prometheus / Rundeck Exporter integration demo

Rundeck / Prometheus / Grafana integration demo via Rundeck Exporter This is a demo environment that shows how to monitor a Rundeck instance using Run

Reiner 4 Oct 14, 2022
Prometheus exporter for metrics from the MyAudi API

Prometheus Audi Exporter This Prometheus exporter exports metrics that it fetches from the MyAudi API. Usage Checkout submodules Install dependencies

Dieter Maes 7 Dec 19, 2022
Prometheus Exporter for data scraped from datenplattform.darmstadt.de

darmstadt-opendata-exporter Scrapes data from https://datenplattform.darmstadt.de and presents it in the Prometheus Exposition format. Pull requests w

Martin Weinelt 2 Apr 12, 2022
Import some key/value data to Prometheus custom-built Node Exporter in Python

About the app In one particilar project, i had to import some key/value data to Prometheus. So i have decided to create my custom-built Node Exporter

Hamid Hosseinzadeh 1 May 19, 2022
Prometheus exporter for chess.com player data

chess-exporter Prometheus exporter for chess.com player data implemented via chess.com's published data API and Prometheus Python Client Example use c

Mário Uhrík 7 Feb 28, 2022
Prometheus exporter for Cisco Unified Computing System (UCS) Manager

prometheus-ucs-exporter Overview Use metrics from the UCS API to export relevant metrics to Prometheus This repository is a fork of Drew Stinnett's or

Marshall Wace 6 Nov 7, 2022
Prometheus instrumentation library for Python applications

Prometheus Python Client The official Python 2 and 3 client for Prometheus. Three Step Demo One: Install the client: pip install prometheus-client Tw

Prometheus 3.2k Jan 7, 2023