Skip to content

Commit

Permalink
Add generic labels on metrics using request matchdict fields
Browse files Browse the repository at this point in the history
  • Loading branch information
leplatrem committed Oct 30, 2024
1 parent 5b2a9d1 commit 4057aa5
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 3 deletions.
1 change: 1 addition & 0 deletions kinto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"record_id_generator": "kinto.views.RelaxedUUID",
"project_name": "kinto",
"admin_assets_path": None,
"metrics_matchdict_fields": ["bucket_id", "collection_id", "group_id", "record_id"],
}


Expand Down
1 change: 1 addition & 0 deletions kinto/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"statsd_backend": "kinto.core.statsd",
"statsd_prefix": "kinto.core",
"statsd_url": None,
"metrics_matchdict_fields": [],
"storage_backend": "",
"storage_url": "",
"storage_max_fetch_size": 10000,
Expand Down
23 changes: 20 additions & 3 deletions kinto/core/initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,14 +481,30 @@ def on_new_response(event):
auth, user_id = user_id.split(":")
metrics_service.count("users", unique=[("auth", auth), ("userid", user_id)])

# Add extra labels to metrics, based on fields extracted from the request matchdict.
metrics_matchdict_fields = aslist(settings["metrics_matchdict_fields"])
# Turn the `id` field of object endpoints into `{resource}_id` (eg. `mushroom_id`, `bucket_id`)
enhanced_matchdict = dict(**request.matchdict)
try:
enhanced_matchdict[request.current_resource_name + "_id"] = enhanced_matchdict.get(
"id", ""
)
except AttributeError:
# Not on a resource.
pass
metrics_matchdict_labels = [
(field, enhanced_matchdict.get(field, "")) for field in metrics_matchdict_fields
]

# Count served requests.
metrics_service.count(
"request_summary",
unique=[
("method", request.method.lower()),
("endpoint", utils.strip_uri_prefix(request.path)),
("status", str(request.response.status_code)),
],
]
+ metrics_matchdict_labels,
)

try:
Expand All @@ -497,7 +513,8 @@ def on_new_response(event):
metrics_service.observe(
"request_duration",
duration,
labels=[("endpoint", utils.strip_uri_prefix(request.path))],
labels=[("endpoint", utils.strip_uri_prefix(request.path))]
+ metrics_matchdict_labels,
)
except AttributeError: # pragma: no cover
# Logging was not setup in this Kinto app (unlikely but possible)
Expand All @@ -507,7 +524,7 @@ def on_new_response(event):
metrics_service.observe(
"request_size",
len(request.response.body or b""),
labels=[("endpoint", utils.strip_uri_prefix(request.path))],
labels=[("endpoint", utils.strip_uri_prefix(request.path))] + metrics_matchdict_labels,
)

# Count authentication verifications.
Expand Down
45 changes: 45 additions & 0 deletions tests/test_views_metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import unittest
from unittest import mock

from kinto.core.testing import skip_if_no_prometheus

from .support import BaseWebTest


@skip_if_no_prometheus
class ViewsMetricsTest(BaseWebTest, unittest.TestCase):
@classmethod
def get_app_settings(cls, extras=None):
settings = super().get_app_settings(extras)
settings["includes"] += "\nkinto.plugins.prometheus"
return settings

def setUp(self):
super().setUp()
patch = mock.patch("kinto.plugins.prometheus.PrometheusService")
self.mocked = patch.start()
self.addCleanup(patch.stop)

def test_metrics_have_matchdict_labels(self):
self.app.put("/buckets/beers", headers=self.headers)
self.app.put("/buckets/beers/groups/amateurs", headers=self.headers)
self.app.put("/buckets/beers/collections/barley", headers=self.headers)
self.app.put("/buckets/beers/collections/barley/records/abc", headers=self.headers)

resp = self.app.get("/__metrics__")
self.assertIn(
'request_size_sum{bucket_id="beers",collection_id="",endpoint="/buckets/beers",group_id="",record_id=""}',
resp.text,
)
self.assertIn(
'request_size_sum{bucket_id="beers",collection_id="",endpoint="/buckets/beers/groups/amateurs",group_id="amateurs",record_id=""}',
resp.text,
)
self.assertIn(
'request_summary_total{bucket_id="beers",collection_id="barley",endpoint="/buckets/beers/collections/barley",group_id="",method="put",record_id="",status="201"}',
resp.text,
)
self.assertIn(
'request_duration_sum{bucket_id="beers",collection_id="barley",endpoint="/buckets/beers/collections/barley/records/abc",group_id="",record_id="abc"}',
resp.text,
)

0 comments on commit 4057aa5

Please sign in to comment.