## Quick Start

#### EXAMPLE 1: Basic usage
Допустим, у нас есть http-сервер, бизнес-логика которого - отправка писем.

Будем собирать **количество отправленных писем** с помощью **счётчика** (`counter`).

```python
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())
```
Получение собранных метрик: 
```bash
curl --location 'http://0.0.0.0:9999/metrics'
```

#### EXAMPLE 2: Basic usage with labels
**Расширим предыдущий пример**: Теперь отправка письма может быть успешной или провальной.

В таком случае имеет смысл собирать **количество писем с учётом статуса отправки**.

Для этого используются **метки**(`labels`), которые представляют собой словарь.

```python
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`.

```python
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`.

```python
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
**Расширим предыдущий пример**: Теперь отправка писем может быстро завершиться исключением.*

В таком случае имеет смысл **маркировать затраченное время фактом выполнения или возникновения исключения**.

Если этого не делать - метрики затраченного времени будут сильно искажены.

```python
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**
```python
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()
```