Skip to content

Commit 3e9f30b

Browse files
committed
Add /metrics
Fixes #190.
1 parent 9f3b669 commit 3e9f30b

File tree

3 files changed

+75
-31
lines changed

3 files changed

+75
-31
lines changed

docs/api/nightly.yaml

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,20 +1371,6 @@ components:
13711371
- `connected` and `disconnected` (from `connection_status`) are encoded as `1.0` and `0.0` respectively.
13721372
- `installed` and `deferred` (from `configuration.indexes`) are encoded as `1.0` and `0.0` respectively.
13731373
- `version` is not present in the Prometheus metrics.
1374-
examples:
1375-
response:
1376-
value: |
1377-
# TYPE kupo_connection_status gauge
1378-
kupo_connection_status 1.0
1379-
1380-
# TYPE kupo_most_recent_checkpoint counter
1381-
kupo_most_recent_checkpoint 294998
1382-
1383-
# TYPE kupo_most_recent_node_tip counter
1384-
kupo_most_recent_node_tip 71753381
1385-
1386-
# TYPE kupo_configuration_indexes gauge
1387-
kupo_configuration_indexes 1.0
13881374
13891375
parameters:
13901376
asset-name:
@@ -2197,6 +2183,18 @@ paths:
21972183
"text/plain;charset=utf-8":
21982184
schema:
21992185
$ref: "#/components/schemas/HealthPrometheus"
2186+
example: &HealthPrometheusExample |
2187+
# TYPE kupo_connection_status gauge
2188+
kupo_connection_status 1.0
2189+
2190+
# TYPE kupo_most_recent_checkpoint counter
2191+
kupo_most_recent_checkpoint 294998
2192+
2193+
# TYPE kupo_most_recent_node_tip counter
2194+
kupo_most_recent_node_tip 71753381
2195+
2196+
# TYPE kupo_configuration_indexes gauge
2197+
kupo_configuration_indexes 1.0
22002198

22012199
202:
22022200
description: Syncing
@@ -2207,6 +2205,7 @@ paths:
22072205
$ref: "#/components/schemas/Health"
22082206

22092207
"text/plain;charset=utf-8":
2208+
example: *HealthPrometheusExample
22102209
schema:
22112210
$ref: "#/components/schemas/HealthPrometheus"
22122211

@@ -2219,5 +2218,27 @@ paths:
22192218
$ref: "#/components/schemas/Health"
22202219

22212220
"text/plain;charset=utf-8":
2221+
example: *HealthPrometheusExample
2222+
schema:
2223+
$ref: "#/components/schemas/HealthPrometheus"
2224+
2225+
/metrics:
2226+
get:
2227+
operationId: getMetrics
2228+
tags: ["Health"]
2229+
summary: Get Metrics
2230+
description: |
2231+
Like [`/health`](#operation/getHealth), but always return `200 OK` as a status.
2232+
responses:
2233+
200:
2234+
description: Metrics
2235+
headers: *default-headers
2236+
content:
2237+
"text/plain;charset=utf-8":
2238+
example: *HealthPrometheusExample
22222239
schema:
22232240
$ref: "#/components/schemas/HealthPrometheus"
2241+
2242+
"application/json;charset=utf-8":
2243+
schema:
2244+
$ref: "#/components/schemas/Health"

src/Kupo/App/Http.hs

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ import qualified Data.Set as Set
176176
import qualified GHC.Clock
177177
import qualified Kupo.Data.Http.Default as Default
178178
import qualified Kupo.Data.Http.Error as Errors
179+
import qualified Network.HTTP.Types.Status as Http
179180
import qualified Network.HTTP.Types.Header as Http
180181
import qualified Network.HTTP.Types.URI as Http
181182
import qualified Network.Wai.Handler.Warp as Warp
@@ -262,6 +263,9 @@ app withDatabase forceRollback fetchBlock patternsVar readHealth req send =
262263
("health" : args) ->
263264
routeHealth (requestMethod req, args)
264265

266+
("metrics" : args) ->
267+
routeMetrics (requestMethod req, args)
268+
265269
("checkpoints" : args) ->
266270
cacheOr readHealth req send $ flip routeCheckpoints (requestMethod req, args)
267271

@@ -286,10 +290,19 @@ app withDatabase forceRollback fetchBlock patternsVar readHealth req send =
286290
_unmatchedRoutes ->
287291
send Errors.notFound
288292

293+
routeMetrics = \case
294+
("GET", []) -> do
295+
health <- readHealth
296+
send =<< handleGetHealth (requestHeaders req) (Just status200) health
297+
("GET", _) ->
298+
send Errors.notFound
299+
(_, _) ->
300+
send Errors.methodNotAllowed
301+
289302
routeHealth = \case
290303
("GET", []) -> do
291304
health <- readHealth
292-
send =<< handleGetHealth (requestHeaders req) health
305+
send =<< handleGetHealth (requestHeaders req) Nothing health
293306
("GET", _) ->
294307
send Errors.notFound
295308
(_, _) ->
@@ -448,9 +461,10 @@ pathParametersToText = \case
448461

449462
handleGetHealth
450463
:: [Http.Header]
464+
-> Maybe Http.Status
451465
-> Health
452466
-> IO Response
453-
handleGetHealth reqHeaders health =
467+
handleGetHealth reqHeaders forcedStatus health =
454468
case findContentType reqHeaders of
455469
Just ct | cTextPlain `BS.isInfixOf` ct -> do
456470
let resHeaders = addCacheHeaders [(hContentType, cTextPlain <> ";charset=utf-8")] health
@@ -467,21 +481,23 @@ handleGetHealth reqHeaders health =
467481
Just{} ->
468482
return $ Errors.unsupportedContentType (prettyContentTypes <$> [cApplicationJson, cTextPlain])
469483
where
470-
status = case connectionStatus of
471-
-- We consider the server 'far away' from another point if it is more than a 'batch' distance
472-
-- from that point. The 'mailboxCapacity' is given in slot, and we consider an active slot
473-
-- coefficient of 1/20 (which has been the default on ALL cardano networks).
474-
--
475-
-- If the distance is lower than that, it means we are one roll-forward away from being
476-
-- synchronized, in which case, we consider the server synchronized. Note that we could
477-
-- theoritically consider 0 here, but this gives us some resilience to rollbacks so long as
478-
-- they aren't longer than 'mailboxCapacity' blocks.
479-
Connected | fromMaybe maxBound d < fromIntegral (mailboxCapacity * 20) ->
480-
status200
481-
Connected ->
482-
status202
483-
Disconnected ->
484-
status503
484+
status = fromMaybe
485+
(case connectionStatus of
486+
-- We consider the server 'far away' from another point if it is more than a 'batch' distance
487+
-- from that point. The 'mailboxCapacity' is given in slot, and we consider an active slot
488+
-- coefficient of 1/20 (which has been the default on ALL cardano networks).
489+
--
490+
-- If the distance is lower than that, it means we are one roll-forward away from being
491+
-- synchronized, in which case, we consider the server synchronized. Note that we could
492+
-- theoritically consider 0 here, but this gives us some resilience to rollbacks so long as
493+
-- they aren't longer than 'mailboxCapacity' blocks.
494+
Connected | fromMaybe maxBound d < fromIntegral (mailboxCapacity * 20) ->
495+
status200
496+
Connected ->
497+
status202
498+
Disconnected ->
499+
status503
500+
) forcedStatus
485501
where
486502
Health{..} = health
487503
d = distanceToSlot
@@ -502,6 +518,7 @@ handleGetHealth reqHeaders health =
502518

503519
prettyContentTypes ct = decodeUtf8 ("'" <> ct <> "'")
504520

521+
505522
--
506523
-- /checkpoints
507524
--

test/Test/Kupo/App/HttpSpec.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ spec = do
160160
res & Wai.assertStatus (Http.statusCode Http.status200)
161161
res & Wai.assertHeader "Access-Control-Allow-Origin" "*"
162162

163+
session specification get "/metrics" $ \assertJson endpoint -> do
164+
let schema = findSchema specification endpoint Http.status200
165+
res <- Wai.request $ Wai.setPath Wai.defaultRequest "/metrics"
166+
res & Wai.assertStatus (Http.statusCode Http.status200)
167+
res & assertJson True schema
168+
163169
session specification get "/health" $ \assertJson endpoint -> do
164170
let schema = findSchema specification endpoint Http.status200
165171
res <- Wai.request $ Wai.setPath Wai.defaultRequest "/health"

0 commit comments

Comments
 (0)