rycus86 / prometheus_flask_exporter

Prometheus exporter for Flask applications
https://pypi.python.org/pypi/prometheus-flask-exporter
MIT License
645 stars 161 forks source link

How to record and query total requests (flask_http_request_total) PER endpoint/url_rule etc? #179

Open dempstert opened 4 months ago

dempstert commented 4 months ago

Problem

As per the title, I don't know how to see total requests per endpoint.

Additional Note about uncertainty

I am not sure if the problem is my prometheus queries, or my configuration of flask prometheus itself. I believe the issue is with the configuration.

Question

How to configure prometheus_flask_exporter so that I can record flask_http_request_total per url_rule?

flask_http_request_total seems to group by response code only, but I want to record it on a url_rule bases

flask_http_request_duration_seconds_bucket and flask_http_request_duration_seconds_count and flask_http_request_duration_seconds_sum is grouped by url_rule by default.

What I've Tried

Below is my configuration. I have added default_labels but it doesn't help.

    metrics = GunicornPrometheusMetrics.for_app_factory(
        group_by="url_rule",
        excluded_paths=[
            "/js/*",
            "/css/*",
            "/images/*",
            "/fonts/*",
        ],
        default_labels={"url_rule": lambda: request.url_rule},
    )
    metrics.init_app(app)
rycus86 commented 4 months ago

Some ideas that come to mind that you could try:

metrics.register_default(
    metrics.counter(
        'by_url_rule_counter', 'Request count by URL rule',
        labels={'url_rule': lambda: request.url_rule}
    )
)
dempstert commented 4 months ago

Thanks @rycus86 but I'm afraid I'm not having any luck with that.

Seems you can't call make_default when using an app factory pattern.

Also, I believe that calling init_app calls the register_default method.

What I tried

    metrics = GunicornPrometheusMetrics.for_app_factory(
        group_by="url_rule",
        excluded_paths=[
            "/js/*",
            "/css/*",
            "/images/*",
            "/fonts/*",
        ],
        # default_labels={"url_rule": lambda: request.url_rule},
    )
    counter = metrics.counter(
        "by_url_rule_counter", "Request count by URL rule", labels={"url_rule": lambda: request.url_rule}
    )
    metrics.register_default(counter)

Traceback

  File "/app/prosebit/app.py", line 86, in create_celery_app
    app = app or create_app()
                 ^^^^^^^^^^^^
  File "/app/prosebit/app.py", line 245, in create_app
    metrics.register_default(counter)
  File "/usr/local/lib/python3.11/site-packages/prometheus_flask_exporter/__init__.py", line 563, in register_default
    for endpoint, view_func in app.view_functions.items():
                               ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/werkzeug/local.py", line 316, in __get__
    obj = instance._get_current_object()  # type: ignore[misc]
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/werkzeug/local.py", line 509, in _get_current_object
    raise RuntimeError(unbound_message) from None
RuntimeError: Working outside of application context.
rycus86 commented 4 months ago

Would you be able to set up a full minimal example we could test this on? Based on that we should be able to figure out how to support what you're after.