mentortools/libs/: prometheus-tools-abm-5.2.76943 metadata and description

Simple index

tools to collect metrics to prometheus

author Vasya Svintsov
author_email v.svintsov@techokert.ru
classifiers
  • Programming Language :: Python :: 3
  • Programming Language :: Python :: 3.11
description_content_type text/markdown
requires_dist
  • http-tools-abm (>=6.1.70486,<7.0.0)
  • more-itertools (>=10.2.0,<11.0.0)
  • prometheus-client (>=0.17.0,<1)
requires_python >=3.11,<4.0
File Tox results History
prometheus_tools_abm-5.2.76943-py3-none-any.whl
Size
11 KB
Type
Python Wheel
Python
3
prometheus_tools_abm-5.2.76943.tar.gz
Size
10 KB
Type
Source

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()