From 7018ff80c8bebedb699a7c2281e8cd31a18d2eab Mon Sep 17 00:00:00 2001 From: Noelle Wang <73260931+No767@users.noreply.github.com> Date: Mon, 13 May 2024 23:42:54 -0700 Subject: [PATCH] Implement Prometheus observability (#119) --- bot/cogs/config.py | 2 + bot/cogs/ext/prometheus.py | 122 +++++++++++++++++++++++++++++++++++ bot/cogs/tickets.py | 5 ++ bot/rodhaj.py | 13 ++++ docs/dev-guide/intro.rst | 23 ++++++- docs/user-guide/features.rst | 15 ++++- poetry.lock | 119 +++++++++++++++++++++++++++++++++- pyproject.toml | 2 + requirements.txt | 2 + 9 files changed, 300 insertions(+), 3 deletions(-) create mode 100644 bot/cogs/ext/prometheus.py diff --git a/bot/cogs/config.py b/bot/cogs/config.py index 6a1bf6a..6a59431 100644 --- a/bot/cogs/config.py +++ b/bot/cogs/config.py @@ -623,6 +623,7 @@ async def blocklist_add( await tr.rollback() await ctx.send("Unable to block user") else: + self.bot.metrics.features.blocked_users.inc() await tr.commit() self.bot.blocklist.replace(blocklist) @@ -680,6 +681,7 @@ async def blocklist_remove(self, ctx: GuildContext, entity: discord.Member) -> N await tr.rollback() await ctx.send("Unable to block user") else: + self.bot.metrics.features.blocked_users.dec() await tr.commit() self.bot.blocklist.replace(blocklist) await block_ticket.cog.soft_unlock_ticket( diff --git a/bot/cogs/ext/prometheus.py b/bot/cogs/ext/prometheus.py new file mode 100644 index 0000000..3879cd2 --- /dev/null +++ b/bot/cogs/ext/prometheus.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +import platform +from typing import TYPE_CHECKING + +import discord +from discord.ext import commands, tasks + +try: + + from prometheus_async.aio.web import start_http_server + from prometheus_client import Counter, Enum, Gauge, Info, Summary +except ImportError: + raise RuntimeError( + "Prometheus libraries are required to be installed. " + "Either install those libraries or disable Prometheus extension" + ) + +if TYPE_CHECKING: + from bot.rodhaj import Rodhaj + +METRIC_PREFIX = "discord_" + + +class FeatureCollector: + __slots__ = ( + "bot", + "active_tickets", + "closed_tickets", + "locked_tickets", + "blocked_users", + ) + + def __init__(self, bot: Rodhaj): + self.bot = bot + self.active_tickets = Gauge( + f"{METRIC_PREFIX}active_tickets", "Amount of active tickets" + ) + self.closed_tickets = Counter( + f"{METRIC_PREFIX}closed_tickets", "Number of closed tickets in this session" + ) + self.locked_tickets = Gauge( + f"{METRIC_PREFIX}locked_tickets", + "Number of soft locked tickets in this session", + ) + self.blocked_users = Gauge( + f"{METRIC_PREFIX}blocked_users", "Number of currently blocked users" + ) + + +# Maybe load all of these from an json file next time +class Metrics: + __slots__ = ("bot", "connected", "latency", "commands", "version", "features") + + def __init__(self, bot: Rodhaj): + self.bot = bot + self.connected = Enum( + f"{METRIC_PREFIX}connected", + "Connected to Discord", + ["shard"], + states=["connected", "disconnected"], + ) + self.latency = Gauge(f"{METRIC_PREFIX}latency", "Latency to Discord", ["shard"]) + self.commands = Summary(f"{METRIC_PREFIX}commands", "Total commands executed") + self.version = Info(f"{METRIC_PREFIX}version", "Versions of the bot") + self.features = FeatureCollector(self.bot) + + def get_commands(self) -> int: + total_commands = 0 + for _ in self.bot.walk_commands(): + # As some of the commands are parents, + # Grouped commands are also counted here + total_commands += 1 + + return total_commands + + def fill(self) -> None: + self.version.info( + { + "build_version": self.bot.version, + "dpy_version": discord.__version__, + "python_version": platform.python_version(), + } + ) + self.commands.observe(self.get_commands()) + + async def start(self, host: str, port: int) -> None: + await start_http_server(addr=host, port=port) + + +class Prometheus(commands.Cog): + """Prometheus exporter extension for Rodhaj""" + + def __init__(self, bot: Rodhaj): + self.bot = bot + self._connected_label = self.bot.metrics.connected.labels(None) + + async def cog_load(self) -> None: + self.latency_loop.start() + + async def cog_unload(self) -> None: + self.latency_loop.stop() + + @tasks.loop(seconds=5) + async def latency_loop(self) -> None: + self.bot.metrics.latency.labels(None).set(self.bot.latency) + + @commands.Cog.listener() + async def on_connect(self) -> None: + self._connected_label.state("connected") + + @commands.Cog.listener() + async def on_resumed(self) -> None: + self._connected_label.state("connected") + + @commands.Cog.listener() + async def on_disconnect(self) -> None: + self._connected_label.state("disconnected") + + +async def setup(bot: Rodhaj) -> None: + await bot.add_cog(Prometheus(bot)) diff --git a/bot/cogs/tickets.py b/bot/cogs/tickets.py index 850eae3..543e07e 100644 --- a/bot/cogs/tickets.py +++ b/bot/cogs/tickets.py @@ -151,6 +151,7 @@ async def lock_ticket( async def soft_lock_ticket( self, thread: discord.Thread, reason: Optional[str] = None ) -> discord.Thread: + self.bot.metrics.features.locked_tickets.inc() tags = thread.applied_tags locked_tag = self.get_locked_tag(thread.parent) @@ -162,6 +163,7 @@ async def soft_lock_ticket( async def soft_unlock_ticket( self, thread: discord.Thread, reason: Optional[str] = None ) -> discord.Thread: + self.bot.metrics.features.locked_tickets.dec() tags = thread.applied_tags locked_tag = self.get_locked_tag(thread.parent) @@ -176,6 +178,8 @@ async def close_ticket( connection: Union[asyncpg.Pool, asyncpg.Connection], author: Optional[Union[discord.User, discord.Member]] = None, ) -> Optional[discord.Thread]: + self.bot.metrics.features.closed_tickets.inc() + self.bot.metrics.features.active_tickets.dec() if isinstance(user, int): user = self.bot.get_user(user) or (await self.bot.fetch_user(user)) @@ -280,6 +284,7 @@ async def create_ticket(self, ticket: TicketThread) -> Optional[TicketOutput]: status=False, ticket=created_ticket, msg="Could not create ticket" ) else: + self.bot.metrics.features.active_tickets.inc() await tr.commit() return TicketOutput( status=True, diff --git a/bot/rodhaj.py b/bot/rodhaj.py index 57e4ab1..903b39d 100644 --- a/bot/rodhaj.py +++ b/bot/rodhaj.py @@ -9,6 +9,7 @@ from aiohttp import ClientSession from cogs import EXTENSIONS, VERSION from cogs.config import Blocklist, GuildWebhookDispatcher +from cogs.ext.prometheus import Metrics from discord.ext import commands from libs.tickets.structs import PartialConfig, ReservedTags, StatusChecklist from libs.tickets.utils import get_cached_thread, get_partial_ticket @@ -56,6 +57,7 @@ def __init__( self.blocklist = Blocklist(self) self.default_prefix = "r>" self.logger = logging.getLogger("rodhaj") + self.metrics = Metrics(self) self.session = session self.partial_config: Optional[PartialConfig] = None self.pool = pool @@ -65,6 +67,7 @@ def __init__( ) self._dev_mode = config.rodhaj.get("dev_mode", False) self._reloader = Reloader(self, Path(__file__).parent) + self._prometheus = config.rodhaj.get("prometheus", {}) ### Ticket related utils async def fetch_partial_config(self) -> Optional[PartialConfig]: @@ -214,6 +217,16 @@ async def setup_hook(self) -> None: await self.blocklist.load() self.partial_config = await self.fetch_partial_config() + if self._prometheus.get("enabled", False): + await self.load_extension("cogs.ext.prometheus") + prom_host = self._prometheus.get("host", "127.0.0.1") + prom_port = self._prometheus.get("port", 8555) + + await self.metrics.start(host=prom_host, port=prom_port) + self.logger.info("Prometheus Server started on %s:%s", prom_host, prom_port) + + self.metrics.fill() + if self._dev_mode: self.logger.info("Dev mode is enabled. Loading Reloader") self._reloader.start() diff --git a/docs/dev-guide/intro.rst b/docs/dev-guide/intro.rst index 8cebede..f3424ad 100644 --- a/docs/dev-guide/intro.rst +++ b/docs/dev-guide/intro.rst @@ -103,4 +103,25 @@ pre-built Docker Compose file is provided. Setup instructions are as follows: .. code-block:: bash - docker compose -f docker-compose-dev.yml up -d \ No newline at end of file + docker compose -f docker-compose-dev.yml up -d + +Extensions +========== + +Rodhaj includes the following extensions as noted: + +Prometheus Exporter +^^^^^^^^^^^^^^^^^^^ + +Rodhaj currently includes an `Prometheus `_ exporter. +This exporter is intended to be used in production environments, where +metrics surrounding ticket usage, bot health, and others would provide +valuable insight. This exporter can be enabled by setting the +``rodhaj.prometheus.enabled`` key within ``config.yml``. + +.. note:: + + Prometheus client libraries are listed within the + ``requirements.txt`` file. By default, these libraries + should be installed, but disabling the exporter will not + affect the usage of these libraries. \ No newline at end of file diff --git a/docs/user-guide/features.rst b/docs/user-guide/features.rst index 6e52134..d1479fd 100644 --- a/docs/user-guide/features.rst +++ b/docs/user-guide/features.rst @@ -52,4 +52,17 @@ Blocklist This feature acts very similar to an block/unblock feature. All blocked users as of writing will not get a message from the bot. Planned features with this feature include an timer to automatically remove those who are on the blocklist and -an history feature to track past incidents. \ No newline at end of file +an history feature to track past incidents. + +Prometheus Extension +-------------------- + +In order to aid in observability, Rodhaj includes an `Prometheus `_ exporter. +This is included as an extension to Rodhaj, which when used, provides valuable information +in regards to usage, and other metrics. This extension is designed primarily to be used in +production environments. + +.. note:: + + Disabling this extension will have no effect + on the bot itself. \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 1646740..f645ceb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1421,6 +1421,44 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "prometheus-async" +version = "22.2.0" +description = "Async helpers for prometheus_client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "prometheus-async-22.2.0.tar.gz", hash = "sha256:b0426370eb3b3bacd99afcf1fcc669c118cb67603cc951a6fe12434e9d4307f2"}, + {file = "prometheus_async-22.2.0-py3-none-any.whl", hash = "sha256:5cbfa535561342b834c087c4f3f3be0a3cb8785a0b8748111c916f3d68bbc370"}, +] + +[package.dependencies] +prometheus_client = ">=0.8.0" +typing_extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} +wrapt = "*" + +[package.extras] +aiohttp = ["aiohttp (>=3)"] +consul = ["aiohttp (>=3)"] +dev = ["aiohttp", "cogapp", "coverage[toml]", "furo", "mypy", "myst-parser", "pre-commit", "pytest", "pytest-asyncio", "pytest-twisted", "sphinx", "sphinx-notfound-page", "sphinxcontrib-asyncio", "tomli", "twisted"] +docs = ["aiohttp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-asyncio", "twisted"] +tests = ["coverage[toml]", "pytest", "pytest-asyncio"] +twisted = ["twisted"] + +[[package]] +name = "prometheus-client" +version = "0.20.0" +description = "Python client for the Prometheus monitoring system." +optional = false +python-versions = ">=3.8" +files = [ + {file = "prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7"}, + {file = "prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89"}, +] + +[package.extras] +twisted = ["twisted"] + [[package]] name = "psutil" version = "5.9.8" @@ -2349,6 +2387,85 @@ files = [ [package.dependencies] cython = "*" +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + [[package]] name = "yarl" version = "1.9.4" @@ -2470,4 +2587,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<4" -content-hash = "c50327840495c94943b219db9fe4dd6184ce3d22b6724668e294bffe05a97c6e" +content-hash = "97cfcfafe42cf554264e5095bf98474e570224e0cb8fe08201a558f1fdff8d67" diff --git a/pyproject.toml b/pyproject.toml index 637a32c..4464d8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,8 @@ jishaku = "^2.5.2" pyyaml = "^6.0.1" watchfiles = "^0.21.0" typing-extensions = "^4.11.0" +prometheus-client = "^0.20.0" +prometheus-async = "^22.2.0" [tool.poetry.group.dev.dependencies] # These are pinned by major version diff --git a/requirements.txt b/requirements.txt index 0d445f3..c3d2bfa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,8 @@ pygit2>=1.13.3,<2 python-dateutil>=2.8.2,<3 click>=8.1.7,<9 typing-extensions>=4.9.0,<5 +prometheus-client>=0.20.0,<1 +prometheus-async>=22.2.0,<23 async-lru>=2.0.4,<3 msgspec>=0.18.6,<1 jishaku>=2.5.2,<3