Bypassing Prometheus SDK push model
I have talked in an earlier log that I have set up business monitoring which has led to interesting conversations with the stakeholders.
I have used prometheus-client
which is a great start providing Counter
/Gauge
/Summaries
/Histograms
but they are push-based, which means: you declare them, and you act on them
(increasing/decreasing/etc.) so they are ready when they are requested.
I have tried to set them on a stateful element (for reference, we receive
requests, which have a step associated to them, there are ~20 different
steps, a typical request going through 30-40 steps, the goal was to have
counter) as a Gauge
(increasing and decreasing floating point number).
The status changes were event sourced, so I thought: each time a status change
occurs, decrease the previous status' Gauge
and increase the new status' Gauge
.
I have tried really hard, but I was ending up with negative Gauge
and incorrect counts.
More over, I had to maintain a double-accounting piece of code to have coherent counters
(unsuccessfully).
So, I ended up creating a custom Metric
which does the count once:
registerMetrics requestsRef =
void $ register $ Metric $ return ((), mkSampleGroups)
where
mkSampleGroups = do
requests <- readIORef requestsRef
let samples =
map (\(labels, counts) -> Sample "requests" labels (BS.fromString $ show $ int2Float counts)) $
Map.toList $
foldl (flip $ Map.alter (Just . maybe 1 (+ 1))) Map.empty $
map mkLabels $
Map.toList requests
return
[ SampleGroup
(Info "requests" "requests")
GaugeType
samples
]
Which made my Metric
only relying on my state, and not on a temporal state.