Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-httpclient

Named HTTP clients for Pudu: a client factory, delegating handlers, pooled keep-alive connections, handler lifetimes, logging, and resilience

0.1.1Apache-2.01

InstallClose

Metrics.pudu

Pudu66 lines2.7 KB

GitHub ↗
1/** @Handlers.Metrics.Module — counts and durations of the requests through a pipeline */2module PuduLangHttpClient.Handlers.Metrics34import Std.Concurrent.Cancel as Cancel5import Std.Json as Json6import Std.Math as Math7import PuduLangHttpClient.Handler as Handler8import PuduLangHttpClient as HttpClient9import PuduLangHttpClient.Request as Request10import PuduLangHttpClient.Response as Response11import PuduLangHttpClient.Utils.Shared as Shared1213/** @Handlers.Metrics.Snapshot — what the requests measured so far add up to */14export type Snapshot = {15  requests: Int,16  active: Int,17  informational: Int,18  successful: Int,19  redirected: Int,20  clientErrors: Int,21  serverErrors: Int,22  failed: Int,23  totalMillis: Int,24  longestMillis: Int25} derives Json.Encode2627/** @Handlers.Metrics.Meter — a snapshot shared by every request a handler measures */28export type Meter = { held: Shared.Shared[Snapshot] }2930/// A meter with nothing measured.31export fn meter() -> Meter {32  Meter{held: Shared.shared(Snapshot{requests: 0, active: 0, informational: 0, successful: 0, redirected: 0, clientErrors: 0, serverErrors: 0, failed: 0, totalMillis: 0, longestMillis: 0})}33}3435/// A handler counting every request into the meter: started, in flight, finished by status class36/// or failed, and how long each took.37export fn handler(target: &Meter) -> Handler.Handler {38  let held = *target39  fn(request: Request.Request, token: Cancel.Token, next: Handler.Send) -> HttpClient.Outcome[Response.Response] {40    Shared.update(&held.held, |now: Snapshot| Snapshot{..now, requests: now.requests + 1, active: now.active + 1})41    let began = clock()42    let outcome = next(request, token)43    let took = clock() - began44    let code = match outcome {45      case Ok(response) => response.status.code46      case Err(_) => 047    }48    Shared.update(&held.held, |now: Snapshot| counted(&now, code, took))49    outcome50  }51}5253/// The measurements so far.54export fn snapshot(source: &Meter) -> Snapshot { Shared.current(&source.held) }5556/// The snapshot with one finished request added: its status code, or 0 for a failure.57fn counted(now: &Snapshot, code: Int, took: Int) -> Snapshot {58  let timed = Snapshot{..*now, active: now.active - 1, totalMillis: now.totalMillis + took, longestMillis: Math.max(took, now.longestMillis)}59  if code == 0 { Snapshot{..timed, failed: timed.failed + 1} }60    else if code < 200 { Snapshot{..timed, informational: timed.informational + 1} }61    else if code < 300 { Snapshot{..timed, successful: timed.successful + 1} }62    else if code < 400 { Snapshot{..timed, redirected: timed.redirected + 1} }63    else if code < 500 { Snapshot{..timed, clientErrors: timed.clientErrors + 1} }64    else { Snapshot{..timed, serverErrors: timed.serverErrors + 1} }65}66