Добавление метрик в приложение, клиентские библиотеки, counter и gauge
«Нельзя улучшить то, что нельзя измерить»
Инструментирование — это добавление кода в приложение для сбора метрик.
Prometheus не знает о вашем приложении ничего, пока вы не расскажете ему об этом через метрики.
Уровни инструментирования:
В этой теме научимся добавлять метрики вручную.
Prometheus предоставляет официальные клиентские библиотеки для популярных языков:
prometheus-clientprometheus/client_golangprometheus/client_javaprom-clientprometheus-clientprometheus/client_phpБиблиотеки решают три задачи:
/metricsВспомним типы метрик из темы про архитектуру и разберём, когда какой пользоваться.
Используйте для:
Python пример:
from prometheus_client import Counter, start_http_server
# Создаём counter
http_requests = Counter(
'http_requests_total', # имя метрики
'Total HTTP requests', # описание
['method', 'status'] # лейблы
)
def handle_request(method, status):
# Инкремент на 1
http_requests.labels(method=method, status=status).inc()
# Инкремент на произвольное значение
# http_requests.labels(method=method, status=status).inc(5)
# Запускаем HTTP сервер для экспорта метрик
start_http_server(8000)Важно:
_total (конвенция Prometheus)Используйте для:
Python пример:
from prometheus_client import Gauge
import random
active_users = Gauge(
'active_users',
'Number of active users'
)
def update_active_users():
# Устанавливаем конкретное значение
active_users.set(random.randint(0, 100))
# Инкремент/декремент
# active_users.inc() # +1
# active_users.dec(5) # -5Важно:
set() для установки абсолютного значенияinc()/dec() для относительных измененийИспользуйте для:
Python пример:
from prometheus_client import Histogram
import time
# Создаём histogram с кастомными бакетами
request_latency = Histogram(
'http_request_duration_seconds',
'HTTP request duration in seconds',
['method'],
buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0)
)
@request_latency.time() # Декоратор автоматически замеряет время
def handle_request(method):
# Ваш код
time.sleep(0.1)
return "OK"
# Или вручную:
def handle_request_manual(method):
start = time.time()
try:
# Ваш код
pass
finally:
request_latency.labels(method=method).observe(time.time() - start)Важно:
_bucket, _sum, _count метрикиИспользуйте редко, когда:
Python пример:
from prometheus_client import Summary
import time
request_summary = Summary(
'http_request_duration_seconds',
'HTTP request duration in seconds',
['method']
)
@request_summary.time()
def handle_request(method):
time.sleep(0.1)Важно:
Рассмотрим реальное приложение с полным инструментированием.
from flask import Flask, request, jsonify
from prometheus_client import Counter, Histogram, Gauge, generate_latest, CONTENT_TYPE_LATEST
import time
import random
app = Flask(__name__)
# === МЕТРИКИ ===
# Counter: количество запросов
http_requests_total = Counter(
'http_requests_total',
'Total HTTP requests',
['method', 'endpoint', 'status']
)
# Histogram: время ответа
http_request_duration = Histogram(
'http_request_duration_seconds',
'HTTP request duration in seconds',
['method', 'endpoint'],
buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0)
)
# Gauge: количество запросов в обработке
requests_in_progress = Gauge(
'http_requests_in_progress',
'Number of HTTP requests currently being processed',
['method']
)
# === MIDDLEWARE ===
@app.before_request
def before_request():
# Засекаем время начала
request.start_time = time.time()
# Инкремент запросов в обработке
requests_in_progress.labels(method=request.method).inc()
@app.after_request
def after_request(response):
# Декремент запросов в обработке
requests_in_progress.labels(method=request.method).dec()
# Записываем метрики
duration = time.time() - request.start_time
http_request_duration.labels(
method=request.method,
endpoint=request.endpoint or 'unknown'
).observe(duration)
http_requests_total.labels(
method=request.method,
endpoint=request.endpoint or 'unknown',
status=response.status_code
).inc()
return response
# === ЭНДПОИНТ МЕТРИК ===
@app.route('/metrics')
def metrics():
return generate_latest(), 200, {'Content-Type': CONTENT_TYPE_LATEST}
# === БИЗНЕС-ЛОГИКА ===
@app.route('/api/users')
def get_users():
# Имитация работы
time.sleep(random.uniform(0.01, 0.1))
return jsonify({'users': []})
@app.route('/api/orders', methods=['POST'])
def create_order():
# Имитация ошибки 10% запросов
if random.random() < 0.1:
return jsonify({'error': 'Internal error'}), 500
return jsonify({'order_id': 123}), 201
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)Что измеряем:
http_requests_total — все запросы с разбивкой по методу, эндпоинту, статусуhttp_request_duration_seconds — время ответа для анализа перцентилейhttp_requests_in_progress — текущая нагрузка на серверДобавьте приложение в конфигурацию Prometheus:
scrape_configs:
- job_name: 'myapp'
static_configs:
- targets: ['localhost:5000']
metrics_path: /metrics # Путь к эндпоинту метрик (по умолчанию /metrics)
scrape_interval: 15s # Частота опросаПерезапустите Prometheus и проверьте в UI:
# Количество запросов в секунду
rate(http_requests_total[5m])
# 95-й перцентиль времени ответа
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
# Запросов в обработке прямо сейчас
http_requests_in_progressНе ограничивайтесь техническими метриками. Добавляйте метрики, важные для бизнеса.
Примеры бизнес-метрик:
from prometheus_client import Counter, Histogram
# Количество зарегистрированных пользователей
user_registrations_total = Counter(
'user_registrations_total',
'Total user registrations',
['source'] # источник: email, google, github
)
# Количество заказов
orders_total = Counter(
'orders_total',
'Total orders',
['status', 'payment_method']
)
# Сумма выручки
revenue_total = Counter(
'revenue_total',
'Total revenue in cents',
['currency']
)
# Время обработки заказа
order_processing_time = Histogram(
'order_processing_seconds',
'Order processing time in seconds',
buckets=(1, 5, 10, 30, 60, 120, 300)
)Зачем: чтобы связывать технические проблемы с бизнес-последствиями. Если упала база данных — это техническая проблема. Если из-за этого упала выручка — это проблема бизнеса.
# ПЛОХО
requests = Counter('req', 'Requests')
# ХОРОШО
http_requests_total = Counter(
'http_requests_total',
'Total HTTP requests'
)Конвенции:
..._total..._seconds, ..._bytes# ПЛОХО: слишком много лейблов (cardinality explosion!)
Counter('requests', 'Requests', ['user_id', 'request_id', 'timestamp'])
# ХОРОШО: разумные лейблы
Counter('requests', 'Requests', ['method', 'endpoint', 'status'])Проблема: каждая уникальная комбинация лейблов создаёт новый временной ряд. user_id с миллионами пользователей = миллионы рядов = проблемы с производительностью.
http_requests_total = Counter(
'http_requests_total',
'Total HTTP requests',
['method', 'endpoint', 'status'],
unit='requests' # В новых версиях клиентских библиотек
)Добавляйте HELP текст, который объясняет, что измеряет метрика.
Не измеряйте всё подряд. Сфокусируйтесь на:
Добавьте тесты, которые проверяют:
def test_metrics_exported():
response = client.get('/metrics')
assert response.status_code == 200
assert 'http_requests_total' in response.data.decode()curl http://localhost:5000/metricsДолжны увидеть текст в формате Prometheus:
# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="GET",endpoint="get_users",status="200"} 42.0
http://localhost:9090)Проблема: метрики не появляются.
Причины:
metrics_path в конфигурации PrometheusПроблема: слишком много временных рядов.
Причины:
Решение: уберите проблемные лейблы.
_total./metrics через curl и Prometheus UI.Теперь у вас есть метрики в приложении. В следующей теме вы научитесь визуализировать их в Grafana — создавать дашборды, которые помогают понимать состояние системы.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.