Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-resilience

Resilience pipelines for Pudu: retry, circuit breaker, timeout, fallback, hedging, rate limiting, and chaos injection

0.1.0Apache-2.01

InstallClose

Telemetry.pudu

Pudu107 lines3.6 KB

GitHub ↗
1/** @Resilience.Telemetry.Seam — events strategies report while executing */2module PuduLangResilience.Telemetry34import Std.Option as Option5import PuduLangResilience.Context as Context67/** @Resilience.Telemetry.Severity — how much an event matters */8export type Severity = Silent | Debug | Information | Warning | Error | Critical910/** @Resilience.Telemetry.Source — the pipeline and strategy reporting */11export type Source = { pipelineName: Option[Str], pipelineInstance: Option[Str], strategyName: Option[Str] }1213/** @Resilience.Telemetry.Detail — what each kind of event measured */14export type Detail15  = Executing16  | Executed(Int)17  | Attempted(Int, Int, Bool)18  | Retrying(Int, Int)19  | Opened(Int, Bool)20  | Closed(Bool)21  | HalfOpened22  | FellBack23  | Hedged(Int)24  | TimeoutElapsed(Int)25  | Rejected(Option[Int])26  | FaultInjected27  | OutcomeInjected28  | LatencyInjected(Int)29  | BehaviorInjected3031/** @Resilience.Telemetry.Event — one reported occurrence */32export type Event = {33  name: Str,34  severity: Severity,35  source: Source,36  operationKey: Option[Str],37  outcome: Option[Str],38  detail: Detail39}4041/** @Resilience.Telemetry.Listener — receives every reported event */42export type Listener = fn(Event) -> ()4344/** @Resilience.Telemetry.Reporter — where one strategy's events go */45export type Reporter = { source: Source, emit: fn(Event) -> () }4647/// A reporter for `source` that gives each event the severity `severityOf` answers, when given,48/// and hands every event not `Silent` to each listener in order.49export fn reporter(source: Source, listeners: &Array[Listener], severityOf: &Option[fn(Event) -> Severity]) -> Reporter {50  let targets = *listeners51  let override = *severityOf52  Reporter{source: source, emit: fn(event: Event) -> () {53      let graded = match override {54        case Some(grade) => Event{..event, severity: grade(event)}55        case None => event56      }57      if graded.severity != Silent {58        for listener in targets { listener(graded) }59      }60    } }61}6263/// A reporter that drops every event.64export fn silent() -> Reporter {65  Reporter{source: Source{pipelineName: None, pipelineInstance: None, strategyName: None}, emit: fn(_event: Event) -> () { () }}66}6768/// The same reporter naming another strategy as the source.69export fn forStrategy(base: &Reporter, strategyName: Option[Str]) -> Reporter {70  Reporter{..*base, source: Source{..base.source, strategyName: strategyName}}71}7273/// Reports one event from the reporter's source about the execution holding `context`.74export fn report(target: &Reporter, context: &Context.Context, name: Str, severity: Severity, outcome: Option[Str], detail: Detail) -> () {75  (target.emit)(Event{name: name, severity: severity, source: target.source, operationKey: context.operationKey, outcome: outcome, detail: detail})76}7778/// The severity's name.79export fn severityName(value: &Severity) -> Str {80  match value {81    case Silent => "Silent"82    case Debug => "Debug"83    case Information => "Information"84    case Warning => "Warning"85    case Error => "Error"86    case Critical => "Critical"87  }88}8990/// The rank of a severity, from 0 for `Silent` to 5 for `Critical`.91export fn rank(value: &Severity) -> Int {92  match value {93    case Silent => 094    case Debug => 195    case Information => 296    case Warning => 397    case Error => 498    case Critical => 599  }100}101102/// The source as one dotted name, leaving out the parts it lacks.103export fn sourceName(value: &Source) -> Str {104  let parts = Option.toArray(value.pipelineName).concat(Option.toArray(value.pipelineInstance)).concat(Option.toArray(value.strategyName))105  if parts.isEmpty() { "(unnamed)" } else { parts.join("/") }106}107