Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-log

Structured event logging for Pudu: message templates, enrichment, filtering, formatting, and sinks

0.1.0Apache-2.01

InstallClose

Levels.pudu

Pudu93 lines3.1 KB

GitHub ↗
1/** @Log.Domain.Levels.Module — ranks, names, and monikers of levels */2module PuduLangLog.Domain.Levels34import Std.Char as Char5import Std.List as List6import Std.Map as Map7import Std.Math as Math8import PuduLangLog.Domain.Padding as Padding9import PuduLangLog as Log1011/// Title-case monikers of each level by width; the last is the full name.12const TITLE: Array[Array[Str]] = [13  ["V", "Vb", "Vrb", "Verb", "Verbo", "Verbos", "Verbose"],14  ["D", "De", "Dbg", "Dbug", "Debug"],15  ["I", "In", "Inf", "Info", "Infor", "Inform", "Informa", "Informat", "Informati", "Informatio", "Information"],16  ["W", "Wn", "Wrn", "Warn", "Warni", "Warnin", "Warning"],17  ["E", "Er", "Err", "Eror", "Error"],18  ["F", "Fa", "Ftl", "Fatl", "Fatal"]19]2021/// The rank of each level name and number `parse` accepts, keyed in lower case.22const NAMES: Map[Str, Int] = mapOf([23    ("verbose", 0), ("debug", 1), ("information", 2), ("warning", 3), ("error", 4), ("fatal", 5),24    ("0", 0), ("1", 1), ("2", 2), ("3", 3), ("4", 4), ("5", 5)25  ])2627/// The level's position, from 0 for `Verbose` to 5 for `Fatal`.28export fn rank(level: Log.Level) -> Int {29  match level {30    case Log.Verbose => 031    case Log.Debug => 132    case Log.Information => 233    case Log.Warning => 334    case Log.Error => 435    case Log.Fatal => 536  }37}3839/// Whether an event at `level` is at or above `minimum`.40export fn passes(level: Log.Level, minimum: Log.Level) -> Bool { rank(level) >= rank(minimum) }4142/// The more severe of two levels.43export fn stricter(left: Log.Level, right: Log.Level) -> Log.Level { Log.LEVELS[Math.max(rank(left), rank(right))] }4445/// The level's full name, such as `Information`.46export fn name(level: Log.Level) -> Str { monikerOf(level, 99) }4748/// The level a name or number denotes, ignoring case and surrounding spaces.49export fn parse(text: Str) -> Option[Log.Level] {50  let found = Map.get(&NAMES, text.trim().toLower()) ?51  Some(Log.LEVELS[found])52}5354/// The level rendered under an output format: a case letter `u`, `w`, or `t` followed by a width55/// of one or two digits, or a lone `u` or `w`; any other format gives the full name.56export fn moniker(level: Log.Level, format: &Option[Str]) -> Str {57  let full = name(level)58  let given = match format {59    case Some(text) => text60    case None => { return full }61  }62  let letters = given.chars()63  let width = match widthOf(&letters) {64    case Some(counted) => counted65    case None => { return Padding.cased(full, format) }66  }67  if width < 1 { return "" }68  let short = monikerOf(level, width)69  match List.first(&letters) {70    case Some('u') => short.toUpper()71    case Some('w') => short.toLower()72    case Some('t') => short73    case _ => full74  }75}7677/// The digits after a format's case letter as a width, when there are one or two of them.78fn widthOf(letters: &Array[Char]) -> Option[Int] {79  let digits = List.drop(letters, 1)80  if digits.length() < 1 || digits.length() > 2 { return None }81  var width = 082  for digit in digits {83    width = width * 10 + Char.toDigit(digit) ?84  }85  Some(width)86}8788/// The title-case moniker of at most `width` characters.89fn monikerOf(level: Log.Level, width: Int) -> Str {90  let names = TITLE[rank(level)]91  names[Math.min(width, names.length()) - 1]92}93