mentortools/libs/: prometheus-tools-abm-5.2.76943 metadata and description
tools to collect metrics to prometheus
| author | Vasya Svintsov |
| author_email | v.svintsov@techokert.ru |
| classifiers |
|
| description_content_type | text/markdown |
| requires_dist |
|
| requires_python | >=3.11,<4.0 |
| File | Tox results | History |
|---|---|---|
prometheus_tools_abm-5.2.76943-py3-none-any.whl
|
|
|
prometheus_tools_abm-5.2.76943.tar.gz
|
|
Quick Start
EXAMPLE 1: Basic usage
Допустим, у нас есть http-сервер, бизнес-логика которого - отправка писем.
Будем собирать количество отправленных писем с помощью счётчика (counter).
import asyncio
from http_tools import HttpServer
from prometheus_tools.prometheus_controller import PrometheusController
async def send_email() -> None:
pass
async def main():
http_server = HttpServer(HttpServer.Config(port=9999), HttpServer.Context(instance_id='example'))
metrics = PrometheusController(PrometheusController.Config(), PrometheusController.Context(http_server))
await http_server.async_init() # запускаем http_server
async def do(): # эмулируем выполнение работы сервиса
await send_email() # отправляем письмо
metrics.counter('email_sent').inc() # инкрементируем счетчик писем
while True:
await do()
await asyncio.sleep(1)
asyncio.run(main())
Получение собранных метрик:
curl --location 'http://0.0.0.0:9999/metrics'
EXAMPLE 2: Basic usage with labels
Расширим предыдущий пример: Теперь отправка письма может быть успешной или провальной.
В таком случае имеет смысл собирать количество писем с учётом статуса отправки.
Для этого используются метки(labels), которые представляют собой словарь.
import asyncio
import random
from http_tools import HttpServer
from prometheus_tools.prometheus_controller import PrometheusController
async def send_email() -> bool:
return bool(random.randint(0, 1)) # эмулируем статус отправки письма
async def main():
http_server = HttpServer(HttpServer.Config(port=9999), HttpServer.Context(instance_id='example'))
metrics = PrometheusController(PrometheusController.Config(), PrometheusController.Context(http_server))
await http_server.async_init()
async def do(): # эмулируем выполнение работы сервиса
sent_status = await send_email() # отправляем письмо
metrics.counter('email_sent', {'sent_status': sent_status}).inc() # инкрементируем счетчик писем
while True:
await do()
await asyncio.sleep(1)
asyncio.run(main())
ВАЖНО!
- Перечень ключей меток одной метрики всегда должен быть одинаковым. В примере выше всегда передаётся одна метка
sent_status- Перечень значений каждой метки должен быть ограничен. В примере выше у метки есть два варианта значений: true/false. Никогда нельзя использовать таймштемп в качестве значения метки
EXAMPLE 3: Track without labels
Расширим предыдущий пример: Теперь отправка писем занимает длительное время.
В таком случае имеет смысл также собирать затраченное время.
Для этого в библиотеке есть метод track_time.
import asyncio
import random
from http_tools import HttpServer
from prometheus_tools.prometheus_controller import PrometheusController
async def send_email() -> bool:
await asyncio.sleep(0.5 + random.random()) # эмулируем длительное выполнение
return bool(random.randint(0, 1)) # эмулируем статус отправки письма
async def main():
http_server = HttpServer(HttpServer.Config(port=9999), HttpServer.Context(instance_id='example'))
metrics = PrometheusController(PrometheusController.Config(), PrometheusController.Context(http_server))
await http_server.async_init()
async def do(): # эмулируем выполнение работы сервиса
with metrics.track_time('email_sending'): # отслеживаем затраченное время на отправку письма
sent_status = await send_email() # отправляем письмо
metrics.counter('email_sent', {'sent_status': sent_status}).inc() # инкрементируем счетчик писем
while True:
await do()
await asyncio.sleep(1)
asyncio.run(main())
EXAMPLE 4: Track with labels
В предыдущем примере мы отдельно использовали методы counter и track_time.
Для большего удобства предусмотрен метод track, который объединяет в себе методы counter и track_time.
import asyncio
import random
from http_tools import HttpServer
from prometheus_tools.prometheus_controller import PrometheusController
async def send_email() -> bool:
await asyncio.sleep(0.5 + random.random()) # эмулируем длительное выполнение
return bool(random.randint(0, 1)) # эмулируем статус отправки письма
async def main():
http_server = HttpServer(HttpServer.Config(port=9999), HttpServer.Context(instance_id='example'))
metrics = PrometheusController(PrometheusController.Config(), PrometheusController.Context(http_server))
await http_server.async_init()
async def do(): # эмулируем выполнение работы сервиса
# поскольку перечень меток должен быть одинаковым, то все метки, которые будут проставлены внутри,
# должны быть указаны при создании трекера
with metrics.track('email_sending') as tracker: # отслеживаем затраченное время на отправку письма и количество отправленных писем
sent_status = await send_email() # отправляем письмо
tracker.labels['sent_status'] = sent_status # выставляем трекеру значение метки статуса отправки письма
while True:
await do()
await asyncio.sleep(1)
asyncio.run(main())
EXAMPLE 5: track with labels when an error may occur
Расширим предыдущий пример: Теперь отправка писем может быстро завершиться исключением.*
В таком случае имеет смысл маркировать затраченное время фактом выполнения или возникновения исключения.
Если этого не делать - метрики затраченного времени будут сильно искажены.
import asyncio
import random
from http_tools import HttpServer
from prometheus_tools.prometheus_controller import PrometheusController
async def send_email() -> bool:
if random.random() > 0.9: raise ValueError # эмулируем периодически возникающие первого исключения
if random.random() > 0.8: raise TypeError # эмулируем периодически возникающие второго исключения
await asyncio.sleep(0.5 + random.random()) # эмулируем длительное выполнение
return bool(random.randint(0, 1)) # эмулируем статус отправки письма
async def main():
http_server = HttpServer(HttpServer.Config(port=9999), HttpServer.Context(instance_id='example'))
metrics = PrometheusController(PrometheusController.Config(), PrometheusController.Context(http_server))
await http_server.async_init()
async def do(): # эмулируем выполнение работы сервиса
# поскольку перечень меток должен быть одинаковым, то все метки, которые будут проставлены внутри,
# должны быть указаны при создании трекера
with metrics.track('email_sending') as tracker:
try:
sent_status = await send_email() # отправляем письмо
tracker.labels['status'] = sent_status # записываем в метку статус отправки письма
except Exception as er:
tracker.labels['error'] = type(er).__name__ # записываем в метку тип ошибки
while True:
await do()
await asyncio.sleep(1)
asyncio.run(main())
EXAMPLE 6: Initer
Этот пример показывает, как предполагается инициализировать данную библиотеку в сервисах, использующих Initer
from dataclasses import dataclass
import init_helpers
from aiohttp import ClientSession
from async_tools import AsyncInitable, AsyncDeinitable
from http_tools import HttpServer
from prometheus_tools.prometheus_controller import PrometheusController
@dataclass
class Initer:
@dataclass
class Config:
http_server: HttpServer.Config
metrics: PrometheusController.Config
config: Config
@dataclass
class Context(AsyncInitable, AsyncDeinitable):
instance_id: str = "Example"
http_server: HttpServer = None
metrics: PrometheusController = None
session: ClientSession = None
def __post_init__(self) -> None:
AsyncInitable.__init__(self)
AsyncDeinitable.__init__(self)
context: Context
def __init__(self) -> None:
self.config = init_helpers.parse_args(config_file=init_helpers.Arg.ini_file_to_dataclass(self.Config))
self.context = self.Context()
async def __aenter__(self) -> None:
self.context.http_server = HttpServer(self.config.http_server, self.context)
self.context.metrics = PrometheusController(self.config.metrics, self.context)
self.session = self.context.metrics.get_monitored_client_session() # получаем MonitoredClientSession
await self.context.async_init()
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
if self.context.session is not None:
await self.context.session.close()
await self.context.async_deinit()