wai-middleware-prometheus 1.0.0 → 1.0.0.1
raw patch · 2 files changed
+56/−9 lines, 2 filesdep ~prometheus-client
Dependency ranges changed: prometheus-client
Files
src/Network/Wai/Middleware/Prometheus.hs view
@@ -8,8 +8,11 @@ , PrometheusSettings(..) , Default.def , instrumentHandlerValue+ , instrumentHandlerValueWithFilter+ , ignoreRawResponses , instrumentApp , instrumentIO+ , observeSeconds , metricsApp ) where @@ -21,6 +24,7 @@ import Data.Text.Encoding (decodeUtf8) import qualified Network.HTTP.Types as HTTP import qualified Network.Wai as Wai+import qualified Network.Wai.Internal as Wai (Response(ResponseRaw)) import qualified Prometheus as Prom import System.Clock (Clock(..), TimeSpec, diffTimeSpec, getTime, toNanoSecs) @@ -60,19 +64,55 @@ -- If you use this function you will likely want to override the default value -- of 'prometheusInstrumentApp' to be false so that your app does not get double -- instrumented.+--+-- WARNING: If you have 'ResponseRaw' values in your API, consider using+-- @instrumentHandlerValueWithFilter ignoreRawResponses@ instead. instrumentHandlerValue :: (Wai.Request -> Text) -- ^ The function used to derive the "handler" value in Prometheus -> Wai.Application -- ^ The app to instrument -> Wai.Application -- ^ The instrumented app-instrumentHandlerValue f app req respond = do+instrumentHandlerValue = instrumentHandlerValueWithFilter Just++-- | A more flexible variant of 'instrumentHandlerValue'. The filter can change some+-- responses, or drop others entirely.+instrumentHandlerValueWithFilter ::+ (Wai.Response -> Maybe Wai.Response) -- ^ Response filter+ -> (Wai.Request -> Text) -- ^ The function used to derive the "handler" value in Prometheus+ -> Wai.Application -- ^ The app to instrument+ -> Wai.Application -- ^ The instrumented app+instrumentHandlerValueWithFilter resFilter f app req respond = do start <- getTime Monotonic app req $ \res -> do- end <- getTime Monotonic- let method = Just $ decodeUtf8 (Wai.requestMethod req)- let status = Just $ T.pack (show (HTTP.statusCode (Wai.responseStatus res)))- observeSeconds (f req) method status start end+ case resFilter res of+ Nothing -> return ()+ Just res' -> do+ end <- getTime Monotonic+ let method = Just $ decodeUtf8 (Wai.requestMethod req)+ let status = Just $ T.pack (show (HTTP.statusCode (Wai.responseStatus res')))+ observeSeconds (f req) method status start end respond res +-- | 'Wai.ResponseRaw' values have two parts: an action that can be executed to construct a+-- 'Wai.Response', and a pure "backup" 'Wai.Response' in case the computation fails. Since+-- the pure selectors like 'Wai.responseStatus' are pure and it makes no sense for them to+-- call the action, they just go to the backup response and pull the value from that:+--+-- @+-- responseStatus (ResponseRaw ...+-- (ResponseBuilder (Status 500 "blargh") ... ...))+-- == Status {statusCode = 500, statusMessage = "blargh"}+-- @+--+-- This is often not what you want. For example, if you have an end-point for establishing+-- websocket connections that has a backup response with status 5xx, every websocket+-- connection request, whether successful or not, will register as an internal server error.+--+-- This helper therefore filters out all raw requests so they won't create any metrics. Use+-- together with 'instrumentHandlerValueWithFilter'.+ignoreRawResponses :: Wai.Response -> Maybe Wai.Response+ignoreRawResponses (Wai.ResponseRaw {}) = Nothing+ignoreRawResponses res = Just res+ -- | Instrument a WAI app with the default WAI metrics. -- -- If you use this function you will likely want to override the default value@@ -102,9 +142,16 @@ observeSeconds label Nothing Nothing start end return result -observeSeconds :: Text -> Maybe Text -> Maybe Text -> TimeSpec -> TimeSpec -> IO ()+-- | Record an event to the middleware metric.+observeSeconds :: Text -- ^ handler label+ -> Maybe Text -- ^ method+ -> Maybe Text -- ^ status+ -> TimeSpec -- ^ start time+ -> TimeSpec -- ^ end time+ -> IO () observeSeconds handler method status start end = do- let latency = fromRational $ toRational (toNanoSecs (end `diffTimeSpec` start) % 1000000000)+ let latency :: Double+ latency = fromRational $ toRational (toNanoSecs (end `diffTimeSpec` start) % 1000000000) Prom.withLabel requestLatency (handler, fromMaybe "" method, fromMaybe "" status) (flip Prom.observe latency)
wai-middleware-prometheus.cabal view
@@ -1,5 +1,5 @@ name: wai-middleware-prometheus-version: 1.0.0+version: 1.0.0.1 synopsis: WAI middlware for exposing http://prometheus.io metrics. description:@@ -29,7 +29,7 @@ , clock , data-default , http-types- , prometheus-client >=1.0.0 && <1.1+ , prometheus-client >=1.0.0 && <1.2 , text >=0.11 , wai >=3.0 ghc-options: -Wall