rycus86 / prometheus_flask_exporter

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

PrometheusMetrics.info(): Set label value on the fly #107

Open nervous-inhuman opened 3 years ago

nervous-inhuman commented 3 years ago

Hello,

I'm trying to expose statistics from my application in a format like this:

# HELP offer_count Per-company count of offers
# TYPE offer_count gauge

offer_count{company="Foo Corp LLC."} 123

Right now, I can only create a metric, and pass static label name and label value to it, however I see no way to update the label value once the gauge is created:

offer_count_gauge = metrics.info("offer_count", "Per-company count of offers", labels={"company":"Foo Corp LLC."})
offer_count_gauge.set(fetch_offer_count())

However, I'd like to do something more akin to this:

companies = ['Foo Corp LLC.', 'Fake Inc.']
offer_count_gauge = metrics.info("offer_count", "Per-company count of offers")

for company in companies:
    offer_count_gauge.set(labels={"company": company}, value=fetch_offer_count(company))

Is this use-case not supported, or am I missing something?

Thank you.

nervous-inhuman commented 3 years ago

I realized that I can do this, since the returned object from PrometheusMetrics.info() is type Gauge:

offer_count_gauge = metrics.info("offer_count", "Per-company count of offers", labelnames=['company'])
def get_companies():
    companies = Company.select()

    for c in companies:
        company_joboffer_count_gauge.labels(c.name).set(c.joboffers.count())

This works well and gives me the result I want, provided that I comment out the initial gauge.set(1) in __init__.py:826, otherwise I get this error from prometheus_client:

Traceback (most recent call last):
  File "/Users/me/dev/jobseek/web.py", line 41, in <module>
    company_joboffer_count_gauge = metrics.info("offer_count", "Per-company count of offers", labelnames=['company'])

  File "/Users/me/dev/jobs/venv/lib/python3.9/site-packages/prometheus_flask_exporter/__init__.py", line 826, in info
    gauge.set(1)

  File "/Users/me/dev/jobs/venv/lib/python3.9/site-packages/prometheus_client/metrics.py", line 364, in set
    self._value.set(float(value))

AttributeError: 'Gauge' object has no attribute '_value'
nervous-inhuman commented 3 years ago

Would this be sufficient as a fix? https://github.com/nervous-inhuman/prometheus_flask_exporter/commit/6e7b03939d2c63b2bb55022cf8000214c1ef305f

rycus86 commented 3 years ago

Hi!

For specific metrics constructs, such as Gauge I highly recommend instantiating them from the underlying prometheus-client library, so you have better control over how that works, rather than using a specialized type here that hasn't got good support for manual changes (as you found out).

import random

from flask import Flask
from prometheus_client import Gauge
from prometheus_flask_exporter import PrometheusMetrics

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

custom_gauge = Gauge("offer_count", "Per-company count of offers", labelnames=['company'])

@app.route("/test")
def test_endpoint():
    for company in ('Foo', 'Bar', 'Xyz'):
        custom_gauge.labels(company=company).set(random.randint(1, 10))
    return 'OK\n'

if __name__ == '__main__':
    app.run('0.0.0.0', 4000)

The custom_gauge above comes from the underlying, official Prometheus client library and works pretty much as you found out in your tests.

$ curl -i localhost:4000/test
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 3
Server: Werkzeug/2.0.1 Python/3.8.10
Date: Tue, 24 Aug 2021 11:14:45 GMT

OK

$ curl -si localhost:4000/metrics | grep offer_count
# HELP offer_count Per-company count of offers
# TYPE offer_count gauge
offer_count{company="Foo"} 1.0
offer_count{company="Bar"} 2.0
offer_count{company="Xyz"} 5.0

Would this help you?

Perhaps I could highlight this in the README that the preferred way to add additional custom metrics is to use the official client library.