Skip to content

Commit

Permalink
feat: Make add() accept args funcs (#230)
Browse files Browse the repository at this point in the history
* feat: Make add() accept args funcs

* chore: Update CHANGELOG.md
  • Loading branch information
trallnag authored Mar 8, 2023
1 parent 0cb465d commit 6b727bb
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 14 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0).

## Unreleased

### Added

- Adjusted the `add()` method to accept an arbitrary number of instrumentation
functions as arguments instead of a single one. Non-breaking change.
Implemented in pull request
[#230](https://github.com/trallnag/prometheus-fastapi-instrumentator/pull/230).

### Fixed

- Fixed multi process mode in `expose()` method that handles the `/metrics`
Expand Down
25 changes: 13 additions & 12 deletions src/prometheus_fastapi_instrumentator/instrumentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ def metrics(request: Request):

def add(
self,
instrumentation_function: Optional[
*instrumentation_function: Optional[
Callable[[metrics.Info], Union[None, Awaitable[None]]]
],
):
Expand All @@ -287,18 +287,19 @@ def add(
self: Instrumentator. Builder Pattern.
"""

if instrumentation_function:
if asyncio.iscoroutinefunction(instrumentation_function):
self.async_instrumentations.append(
cast(
Callable[[metrics.Info], Awaitable[None]],
instrumentation_function,
for func in instrumentation_function:
if func:
if asyncio.iscoroutinefunction(func):
self.async_instrumentations.append(
cast(
Callable[[metrics.Info], Awaitable[None]],
func,
)
)
else:
self.instrumentations.append(
cast(Callable[[metrics.Info], None], func)
)
)
else:
self.instrumentations.append(
cast(Callable[[metrics.Info], None], instrumentation_function)
)

return self

Expand Down
63 changes: 61 additions & 2 deletions tests/test_instrumentator_multiple_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from prometheus_client import CollectorRegistry, Counter
from starlette.testclient import TestClient

from prometheus_fastapi_instrumentator import Instrumentator
from prometheus_fastapi_instrumentator import Instrumentator, metrics


def test_multiple_apps_custom_registry():
Expand Down Expand Up @@ -63,7 +63,7 @@ async def dummy_middleware(request, call_next):


def test_multiple_apps_expose_defaults():
"""Tests instrumentation of multiple apps in combination with middlewares."""
"""Tests instrumentation of multiple apps in combination with middlewares."""

app1 = FastAPI()
app2 = FastAPI()
Expand Down Expand Up @@ -104,3 +104,62 @@ async def dummy_middleware(request, call_next):
want = 'http_requests_total{handler="/dummy",method="GET",status="2xx"} 1.0\n'
assert want in metrics1
assert want in metrics2


def test_multiple_apps_expose_full():
"""Tests instrumentation of multiple apps in combination with middlewares."""

app1 = FastAPI()
app2 = FastAPI()

@app1.get("/dummy")
def read_dummy_app1():
return "Hello from app1"

@app2.get("/dummy")
def read_dummy_app2():
return "Hello from app2"

Instrumentator().add(
metrics.request_size(),
metrics.requests(),
metrics.combined_size(),
metrics.response_size(),
metrics.latency(),
metrics.default(),
).instrument(app1).expose(app1)

Instrumentator().add(
metrics.request_size(),
metrics.requests(),
metrics.combined_size(),
metrics.response_size(),
metrics.latency(),
metrics.default(),
).instrument(app2).expose(app2)

# Add middleware after adding the instrumentator, this triggers another
# app.build_middleware_stack(), which creates the middleware again, but it
# will use the same Prometheus registry again, which could try to create the
# same metrics again causing duplication errors.
@app1.middleware("http")
@app2.middleware("http")
async def dummy_middleware(request, call_next):
response = await call_next(request)
return response

client1 = TestClient(app1)
client2 = TestClient(app2)

client1.get("/dummy")
client2.get("/dummy")

metrics1 = client1.get("/metrics").content.decode()
metrics2 = client2.get("/metrics").content.decode()

print("app1 GET /metrics\n" + metrics1)
print("app2 GET /metrics\n" + metrics2)

want = 'http_requests_total{handler="/dummy",method="GET",status="2xx"} 1.0\n'
assert want in metrics1
assert want in metrics2

0 comments on commit 6b727bb

Please sign in to comment.