ΠΠ°ΡΠ° ΡΡΡΠΎΠΊ ΠΊΠΎΠ΄Π° ΠΈ Π²Π°ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π³Π΅Π½Π΅ΡΠΈΡΡΠ΅Ρ ΠΌΠ΅ΡΡΠΈΠΊΠΈ, Π²Π°Ρ!
ΠΠ»Ρ ΡΠΎΠ³ΠΎ ΡΡΠΎ Π±Ρ ΠΏΠΎΠ½ΡΡΡ ΠΊΠ°ΠΊ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ prometheus_flask_exporter Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΠΏΡΠΈΠΌΠ΅ΡΠ°:
from flask import Flask
from prometheus_flask_exporter import PrometheusMetrics
app = Flask(__name__)
metrics = PrometheusMetrics(app)
@app.route('/')
def main():
return 'OK'
ΠΡΠΎ Π²ΡΠ΅ ΡΡΠΎ Π½ΡΠΆΠ½ΠΎ Π΄Π»Ρ Π½Π°ΡΠ°Π»Π°! ΠΠΎΠ±Π°Π²ΠΈΠ² ΠΈΠΌΠΏΠΎΡΡ ΠΈ ΡΡΡΠΎΠΊΡ Π΄Π»Ρ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ PrometheusMetrics, Π²Ρ ΠΏΠΎΠ»ΡΡΠΈΡΠ΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ Π·Π°ΠΏΡΠΎΡΠΎΠ² ΠΈ ΡΡΠ΅ΡΡΠΈΠΊΠΈ Π·Π°ΠΏΡΠΎΡΠΎΠ², ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ°Π΅ΠΌΡΠ΅ Π² ΠΊΠΎΠ½Π΅ΡΠ½ΠΎΠΉ ΡΠΎΡΠΊΠ΅ /metrics ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ Flask, Π² ΠΊΠΎΡΠΎΡΠΎΠΌ ΠΎΠ½ΠΎ Π·Π°ΡΠ΅Π³ΠΈΡΡΡΠΈΡΠΎΠ²Π°Π½ΠΎ, Π° ΡΠ°ΠΊΠΆΠ΅ Π²ΡΠ΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ, ΠΊΠΎΡΠΎΡΡΠ΅ Π²Ρ ΠΏΠΎΠ»ΡΡΠ°Π΅ΡΠ΅ ΠΈΠ· Π±Π°Π·ΠΎΠ²ΠΎΠΉ
ΠΡ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π½Π°ΠΉΡΠΈ
ΠΡ ΡΠ°ΠΊΠΆΠ΅ Π½Π°ΠΉΠ΄Π΅ΡΠ΅ ΡΠΏΠΈΡΠΎΠΊ ΠΏΠΎΠΊΠ°Π·Π°ΡΠ΅Π»Π΅ΠΉ Π²
ΠΠ°ΡΡΡΠΎΠΉΠΊΠ°
Π Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ΅ Π΅ΡΡΡ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²ΠΎ ΠΎΠΏΡΠΈΠΉ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ, ΠΏΠΎΡΠΌΠΎΡΡΠΈΡΠ΅ Π²
ΠΠ°Π·ΠΎΠ²Π°Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΏΠΎΠΊΠ°Π·Π°Π½Π° Π²Π²Π΅ΡΡ Ρ. ΠΡΠΎΡΡΠΎ ΡΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ PrometheusMetrics, Π½Π°Π·ΠΎΠ²Π΅ΠΌ Π΅Π³ΠΎ metrics, Π° Π·Π°ΡΠ΅ΠΌ Ρ Π΅Π³ΠΎ ΠΏΠΎΠΌΠΎΡΡΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΠ΅ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ, ΠΊΠΎΡΠΎΡΡΠ΅ Π²Ρ Ρ ΠΎΡΠΈΡΠ΅ ΡΠΎΠ±ΠΈΡΠ°ΡΡ, Π΄Π΅ΠΊΠΎΡΠΈΡΠΎΠ²Π°Π² ΡΡΠ½ΠΊΡΠΈΠΈ:
-
@metrics.counter(..)
-
@metrics.gauge(..)
-
@metrics.summary(..)
-
@metrics.histogram(..)
Π‘ΡΠ΅ΡΡΠΈΠΊΠΈ ΠΏΠΎΠ΄ΡΡΠΈΡΡΠ²Π°ΡΡ Π²ΡΠ·ΠΎΠ²Ρ, Π° ΠΎΡΡΠ°Π»ΡΠ½ΡΠ΅ ΡΠΎΠ±ΠΈΡΠ°ΡΡ ΠΌΠ΅ΡΡΠΈΠΊΠΈ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ ΡΡΠΈΡ Π²ΡΠ·ΠΎΠ²ΠΎΠ². ΠΡ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΠΌΠ΅ΡΠΊΠΈ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΈΠ· Π½ΠΈΡ , ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ ΡΠ²ΠΎΠΉΡΡΠ²Π° Π·Π°ΠΏΡΠΎΡΠ° ΠΈΠ»ΠΈ ΠΎΡΠ²Π΅ΡΠ°. ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ:
from flask import Flask, request
from prometheus_flask_exporter import PrometheusMetrics
app = Flask(__name__)
# group by endpoint rather than path
metrics = PrometheusMetrics(app, group_by='endpoint')
@app.route('/collection/:collection_id/item/:item_id')
@metrics.counter(
'cnt_collection', 'Number of invocations per collection', labels={
'collection': lambda: request.view_args['collection_id'],
'status': lambda resp: resp.status_code
})
def get_item_from_collection(collection_id, item_id):
pass
Π ΠΏΡΠΈΠ²Π΅Π΄Π΅Π½Π½ΠΎΠΌ Π²ΡΡΠ΅ ΠΏΡΠΈΠΌΠ΅ΡΠ΅ Π½Π°ΠΆΠ°ΡΠΈΠ΅ Π½Π° ΠΊΠΎΠ½Π΅ΡΠ½ΡΡ ΡΠΎΡΠΊΡ /collection/10002/item/76 ΠΏΡΠΈΠ²Π΅Π΄Π΅Ρ ΠΊ ΡΠ²Π΅Π»ΠΈΡΠ΅Π½ΠΈΡ ΡΡΠ΅ΡΡΠΈΠΊΠ°, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ cnt_collection{collection = «10002», status = «200»}, ΠΏΠ»ΡΡ Π²Ρ ΠΏΠΎΠ»ΡΡΠΈΡΠ΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ (Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠΎΠ½Π΅ΡΠ½ΠΎΠΉ ΡΠΎΡΠΊΠΈ Π² ΡΡΠΎΠΌ ΠΏΡΠΈΠΌΠ΅ΡΠ΅ ) ΠΈΠ· Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ:
-
flask_http_request_duration_seconds
— ΠΡΠΎΠ΄ΠΎΠ»ΠΆΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ HTTP-Π·Π°ΠΏΡΠΎΡΠ° Π² ΡΠ΅ΠΊΡΠ½Π΄Π°Ρ Π΄Π»Ρ Π²ΡΠ΅Ρ Π·Π°ΠΏΡΠΎΡΠΎΠ² Flask ΠΏΠΎ ΠΌΠ΅ΡΠΎΠ΄Ρ, ΠΏΡΡΠΈ ΠΈ ΡΡΠ°ΡΡΡΡ -
flask_http_request_total
— ΠΠ±ΡΠ΅Π΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ HTTP-Π·Π°ΠΏΡΠΎΡΠΎΠ² ΠΏΠΎ ΠΌΠ΅ΡΠΎΠ΄Π°ΠΌ ΠΈ ΡΡΠ°ΡΡΡΠ°ΠΌ
ΠΡΡΡ Π²Π°ΡΠΈΠ°Π½ΡΡ ΠΏΡΠΎΠΏΡΡΡΠΈΡΡ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½Π½ΡΡ
ΠΊΠΎΠ½Π΅ΡΠ½ΡΡ
ΡΠΎΡΠ΅ΠΊ, Π·Π°ΡΠ΅Π³ΠΈΡΡΡΠΈΡΠΎΠ²Π°ΡΡ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ ΠΈΠ»ΠΈ ΠΏΡΠΎΠΏΡΡΡΠΈΡΡ ΡΠ΅, ΡΡΠΎ ΡΠΊΠ°Π·Π°Π½Ρ Π²ΡΡΠ΅, ΠΈΠ»ΠΈ ΠΏΡΠΈΠΌΠ΅Π½ΠΈΡΡ ΠΎΠ΄Π½Ρ ΠΈ ΡΡ ΠΆΠ΅ Π½Π°ΡΡΡΠ°ΠΈΠ²Π°Π΅ΠΌΡΡ ΠΌΠ΅ΡΡΠΈΠΊΡ ΠΊ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΠΌ ΠΊΠΎΠ½Π΅ΡΠ½ΡΠΌ ΡΠΎΡΠΊΠ°ΠΌ. ΠΠ·Π½Π°ΠΊΠΎΠΌΡΡΠ΅ΡΡ Ρ
app = Flask(__name__)
metrics = PrometheusMetrics(app)
@app.route('/')
def main():
pass # requests tracked by default
@app.route('/skip')
@metrics.do_not_track()
def skip():
pass # default metrics are not collected
# custom metric to be applied to multiple endpoints
common_counter = metrics.counter(
'by_endpoint_counter', 'Request count by endpoints',
labels={'endpoint': lambda: request.endpoint}
)
@app.route('/common/one')
@common_counter
def endpoint_one():
pass # tracked by the custom and the default metrics
@app.route('/common/two')
@common_counter
def endpoint_two():
pass # also tracked by the custom and the default metrics
# register additional default metrics
metrics.register_default(
metrics.counter(
'by_path_counter', 'Request count by request paths',
labels={'path': lambda: request.path}
)
)
Π Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ΅ Π΅ΡΡΡ ΡΠ΄ΠΎΠ±Π½ΡΠ΅ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ Π΄Π»Ρ ΠΏΠΎΠΏΡΠ»ΡΡΠ½ΡΡ ΠΌΠ½ΠΎΠ³ΠΎΠΏΡΠΎΡΠ΅ΡΡΠΎΡΠ½ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊ, ΡΠ°ΠΊΠΈΡ ΠΊΠ°ΠΊ uWSGI ΠΈ Gunicorn. ΠΡ ΡΠ°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π½Π°ΠΉΡΠΈ Π½Π΅Π±ΠΎΠ»ΡΡΠΈΠ΅ ΠΏΡΠΈΠΌΠ΅ΡΡ ΡΠ΅Π»Π΅Π²ΡΡ Π²Π°ΡΠΈΠ°Π½ΡΠΎΠ² ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ, Π² ΡΠΎΠΌ ΡΠΈΡΠ»Π΅ ΠΌΠ½ΠΎΠ³ΠΎΠΏΡΠΎΡΠ΅ΡΡΠΎΡΠ½ΡΡ .
Π‘Π±ΠΎΡ ΠΌΠ΅ΡΡΠΈΠΊ
ΠΠ°ΠΊ ΡΠΏΠΎΠΌΠΈΠ½Π°Π»ΠΎΡΡ Π²ΡΡΠ΅, Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΠΊΠΎΠ½Π΅ΡΠ½ΡΡ ΡΠΎΡΠΊΡ /metrics Π² ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ Flask, ΠΊΠΎΡΠΎΡΠ°Ρ ΠΌΠΎΠΆΠ΅Ρ ΡΠ»ΡΠΆΠΈΡΡ ΡΠ΅Π»ΡΡ Π΄Π»Ρ
Π ΠΏΡΠΈΠ²Π΅Π΄Π΅Π½Π½ΠΎΠΌ Π²ΡΡΠ΅ ΠΏΡΠΈΠΌΠ΅ΡΠ΅ Ρ Π΄Π°ΡΠ±ΠΎΡΠ΄ΠΎΠΌ Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π½Π°ΡΠ΅Π»ΠΈΡΡ ΡΠ²ΠΎΠΉ Prometheus Π½Π° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Flask Ρ Π½Π°ΡΡΡΠΎΠΉΠΊΠ°ΠΌΠΈ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ Ρ ΡΠ°ΠΊΠΎΠΉ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠ΅ΠΉ:
scrape_configs:
- job_name: 'example'
dns_sd_configs:
- names: ['app']
port: 5000
type: A
refresh_interval: 5s
Π‘ΠΌΠΎΡΡΠΈΡΠ΅ ΠΏΠΎΠ»Π½ΡΠΉ ΠΏΡΠΈΠΌΠ΅Ρ Π²
ΠΡΠ»ΠΈ ΡΠ°ΠΊΠΎΠ΅ ΡΠ°ΡΠΊΡΡΡΠΈΠ΅ ΠΊΠΎΠ½Π΅ΡΠ½ΠΎΠΉ ΡΠΎΡΠΊΠΈ ΠΌΠ΅ΡΡΠΈΠΊ Π²Π°ΠΌ Π½Π΅ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ΠΈΡ, Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ Π²Ρ Π½Π΅ Ρ ΠΎΡΠΈΡΠ΅ ΡΠ°Π·ΡΠ΅ΡΠ°ΡΡ Π²Π½Π΅ΡΠ½ΠΈΠΉ Π΄ΠΎΡΡΡΠΏ ΠΊ Π½Π΅ΠΉ, Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π»Π΅Π³ΠΊΠΎ ΠΎΡΠΊΠ»ΡΡΠΈΡΡ Π΅Π΅, ΠΏΠ΅ΡΠ΅Π΄Π°Π² path=None ΠΏΡΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΠΈ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠ° PrometheusMetrics.
from flask import Flask, request
from prometheus_flask_exporter import PrometheusMetrics
app = Flask(__name__)
metrics = PrometheusMetrics(app, path=None)
...
metrics.start_http_server(5099)
ΠΠ°ΡΠ΅ΠΌ Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ start_http_server(port), ΡΡΠΎΠ±Ρ ΠΎΡΠΊΡΡΡΡ ΡΡΡ ΠΊΠΎΠ½Π΅ΡΠ½ΡΡ ΡΠΎΡΠΊΡ Π½Π° Π΄ΡΡΠ³ΠΎΠΌ HTTP-ΠΏΠΎΡΡΡ, 5099 Π² ΠΏΡΠΈΠ²Π΅Π΄Π΅Π½Π½ΠΎΠΌ Π²ΡΡΠ΅ ΠΏΡΠΈΠΌΠ΅ΡΠ΅. Π ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ Π°Π»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Ρ, Π΅ΡΠ»ΠΈ Π²Π°Ρ ΡΡΡΡΠ°ΠΈΠ²Π°Π΅Ρ ΡΠΎ, ΡΡΠΎ ΠΊΠΎΠ½Π΅ΡΠ½Π°Ρ ΡΠΎΡΠΊΠ° Π½Π°Ρ ΠΎΠ΄ΠΈΡΡΡ Π² ΡΠΎΠΌ ΠΆΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ Flask, Π½ΠΎ Π²Π°ΠΌ Π½ΡΠΆΠ½ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡΡ Π΅Π΅ ΠΏΡΡΡ ΠΎΡ /metrics, Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π»ΠΈΠ±ΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ Π΄ΡΡΠ³ΠΎΠΉ URI Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ° ΠΏΡΡΠΈ, Π»ΠΈΠ±ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ register_endpoint(..), ΡΡΠΎΠ±Ρ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ ΡΡΠΎ ΠΏΠΎΠ·ΠΆΠ΅.
Π‘ΡΡΠ»ΠΊΠΈ
-
rycus86/prometheus_flask_exporter — ΠΡΠΎΡ ΡΠΊΡΠΏΠΎΡΡΠ΅Ρ ΡΠ»Π°ΠΊΠΎΠ½ΠΎΠ² Π΄Π»Ρ Prometheus -
prometheus-flask-exporter README — ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅, ΠΏΡΠΈΠΌΠ΅ΡΡ ΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ -
prometheus-flask-exporter examples — ΠΡΠΈΠΌΠ΅ΡΡ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ ΠΌΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³Π° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ Flask ΡΠ°Π·Π½ΡΠΌΠΈ ΡΠΏΠΎΡΠΎΠ±Π°ΠΌΠΈ -
prometheus-flask-exporter on PyPI — ΠΡΠΎΡ ΠΏΡΠΎΠ΅ΠΊΡ Π½Π° PyPI -
prometheus/client_python — ΠΡΠΈΡΠΈΠ°Π»ΡΠ½Π°Ρ ΠΊΠ»ΠΈΠ΅Π½ΡΡΠΊΠ°Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Prometheus Π΄Π»Ρ Python
ΠΡΠ»ΠΈ Π²Ρ ΡΠ΅ΡΠΈΡΠ΅ ΠΏΠΎΠΏΡΠΎΠ±ΠΎΠ²Π°ΡΡ, Π½Π΅ ΡΡΠ΅ΡΠ½ΡΠΉΡΠ΅ΡΡ ΠΎΡΠΊΡΠΎΠΉΡΠ΅ issue Π½Π° GitHub ΠΈΠ»ΠΈ ΠΎΡΡΠ°Π²Π»ΡΡΡ ΡΠ²ΠΎΠΈ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ, ΠΎΡΠ·ΡΠ²Ρ ΠΈ ΠΏΡΠ΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΡ!
Π‘ΠΏΠ°ΡΠΈΠ±ΠΎ!
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com