Skip to content

Commit

Permalink
feat: simple prometheus asgi optional integration (experimental, will…
Browse files Browse the repository at this point in the history
… change)
  • Loading branch information
hartym committed Jun 16, 2024
1 parent 290b519 commit 56beea1
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 3 deletions.
30 changes: 30 additions & 0 deletions docs/operate/prometheus.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Prometheus
==========

.. warning::

This feature is mostly for internal use for now, and although we'll keep a prometheus integration in the future,
configuration will change.

Enable the middleware using ...

.. code-block:: bash
USE_ASGI_PROMETHEUS_MIDDLEWARE=true harp ...
Then you can access the metrics at ``/.prometheus/metrics``.

Here is an example scrape configuration for prometheus:

.. code-block:: yaml
scrape_configs:
- job_name: harp
honor_timestamps: true
scrape_interval: 10s
scrape_timeout: 10s
metrics_path: /.prometheus/metrics
scheme: http
static_configs:
- targets:
- url.or.ip.for.harp.example.com:4080
11 changes: 10 additions & 1 deletion harp/config/adapters/hypercorn.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from hypercorn.utils import LifespanFailureError

from harp import get_logger
from harp.utils.env import get_bool_from_env

logger = get_logger(__name__)

Expand Down Expand Up @@ -34,11 +35,19 @@ async def serve(self):
from hypercorn.asyncio import serve

asgi_app, binds = await self.factory.build()

if get_bool_from_env("USE_ASGI_PROMETHEUS_MIDDLEWARE", False):
from asgi_prometheus import PrometheusMiddleware

_metrics_url = "/.prometheus/metrics"
asgi_app = PrometheusMiddleware(asgi_app, metrics_url=_metrics_url, group_paths=["/"])
logger.info(f"🌎 PrometheusMiddleware enabled, metrics under {_metrics_url}.")

config = self._create_config(binds)
logger.debug(f"🌎 {type(self).__name__}::serve({', '.join(config.bind)})")

try:
return await serve(asgi_app, config)
return await serve(asgi_app, config, mode="asgi")
except LifespanFailureError as exc:
logger.exception(f"Server initiliation failed: {repr(exc.__cause__)}", exc_info=exc.__cause__)
if isinstance(exc.__cause__, PostgresError):
Expand Down
2 changes: 1 addition & 1 deletion harp/utils/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ def get_bool_from_env(key, default):
return default

try:
return cast_bool(key)
return cast_bool(value)
except ValueError:
raise ValueError(f"Invalid boolean value for {key}: {value!r}")
28 changes: 28 additions & 0 deletions harp/utils/tests/test_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from unittest.mock import patch

import pytest

from harp.utils.env import cast_bool, get_bool_from_env

parametrize_with_string_booleans = pytest.mark.parametrize(
"value, expected",
[
("true", True),
("yes", True),
("1", True),
("false", False),
("no", False),
("0", False),
],
)


@parametrize_with_string_booleans
def test_cast_bool(value, expected):
assert cast_bool(value) == expected


@parametrize_with_string_booleans
def test_get_bool_from_env(value, expected):
with patch.dict("os.environ", {"TEST_ENV_VAR": value}):
assert get_bool_from_env("TEST_ENV_VAR", False) == expected
35 changes: 34 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ svix-ksuid = "^0.6.2"
watchfiles = { version = "^0.22.0", optional = true }
whistle = { version = "2.0.0b1", allow-prereleases = true }
pyheck = "^0.1.5"
asgi-prometheus = "^1.1.2"


[tool.poetry.group.dev.dependencies]
Expand Down

0 comments on commit 56beea1

Please sign in to comment.