
Settings.pudu
Pudu144 lines7.4 KB
1/** @Log.Settings.Loader — a configuration read from settings */2module PuduLangLog.Settings34import Std.Env as Env5import Std.Io as Io6import Std.List as List7import PuduLangLog.Configuration as Configuration8import PuduLangLog.Domain.Settings as Plan9import PuduLangLog.Expressions as Expressions10import PuduLangLog.LevelSwitch as LevelSwitch11import PuduLangLog as Log12import PuduLangLog.Settings.Registry as Registry13import PuduLangLog.Sink as Sink1415/** @Log.Settings.Loaded — a configuration and the level switches it declared */16export type Loaded = { configuration: Configuration.Configuration, switches: Array[(Str, LevelSwitch.LevelSwitch)] }171819const MINIMUM_ARGUMENT: Str = "restrictedToMinimumLevel"20212223242526272829303132333435363738export fn apply(base: &Configuration.Configuration, pairs: &Array[(Str, Str)], registry: &Registry.Registry) -> Result[Loaded, Array[Str]] {39 let read = Plan.read(pairs)40 let plan = read.plan41 var problems = read.problems42 let switches = plan.switches.map(|entry: (Str, Log.Level)| (entry[0], LevelSwitch.create(entry[1])))43 var configuration = *base44 if let Some(level) = plan.minimum { configuration = configuration.minimumLevel(level) }45 if let Some(name) = plan.controlledBy {46 if let Some(control) = switchOf(&switches, name) { configuration = configuration.controlledBy(&control) }47 }48 for (source, chosen) in plan.overrides {49 match (Plan.levelOf(chosen), switchOf(&switches, Plan.switchName(chosen))) {50 case (Some(level), _) => { configuration = configuration.overrideLevel(source, level) }51 case (None, Some(control)) => { configuration = configuration.overrideSwitch(source, &control) }52 case _ => {}53 }54 }55 for (name, text) in plan.properties { configuration = configuration.enrichWithProperty(name, Log.Scalar(Log.Text(text))) }56 for target in plan.enrichers {57 match Registry.enricherNamed(registry, target.name) {58 case Some(factory) => match factory(&target.arguments) {59 case Ok(enricher) => { configuration = configuration.enrichWith(enricher) }60 case Err(problem) => { problems = problems.push("The enricher `" + target.name + "`: " + problem) }61 }62 case None => { problems = problems.push("No enricher is registered as `" + target.name + "`") }63 }64 }65 for filter in plan.filters {66 match Expressions.predicate(filter.expression) {67 case Ok(test) => { configuration = if filter.including { configuration.filterIncludingOnly(test) } else { configuration.filterExcluding(test) } }68 case Err(problem) => { problems = problems.push("The filter `" + filter.expression + "`: " + problem) }69 }70 }71 if let Some(depth) = plan.maximumDepth { configuration = configuration.maximumDepth(depth) }72 if let Some(length) = plan.maximumStringLength { configuration = configuration.maximumStringLength(length) }73 if let Some(size) = plan.maximumCollectionCount { configuration = configuration.maximumCollectionCount(size) }74 for tag in plan.scalars { configuration = configuration.destructureAsScalar(tag) }75 for (audit, targets) in [(false, plan.sinks), (true, plan.audits)] {76 for target in targets {77 match sinkOf(&target, registry, &switches) {78 case Ok(sink) => { configuration = if audit { configuration.auditTo(sink) } else { configuration.writeTo(sink) } }79 case Err(problem) => { problems = problems.push(problem) }80 }81 }82 }83 if !problems.isEmpty() { return Err(problems) }84 Ok(Loaded{configuration: configuration, switches: switches})85}868788fn sinkOf(target: &Plan.Target, registry: &Registry.Registry, switches: &Array[(Str, LevelSwitch.LevelSwitch)]) -> Result[Sink.Sink, Str] {89 let factory = match Registry.sinkNamed(registry, target.name) {90 case Some(found) => found91 case None => { return Err("No sink is registered as `" + target.name + "`") }92 }93 let own = target.arguments.filter(|entry: (Str, Str)| entry[0].toLower() != MINIMUM_ARGUMENT.toLower() && entry[0].toLower() != Plan.LEVEL_SWITCH_ARGUMENT.toLower())94 var sink = match factory(&own) {95 case Ok(built) => built96 case Err(problem) => { return Err("The sink `" + target.name + "`: " + problem) }97 }98 if let Some(text) = Plan.argument(target, MINIMUM_ARGUMENT) {99 match Plan.levelOf(text) {100 case Some(level) => { sink = Sink.restricted(sink, level) }101 case None => { return Err("The sink `" + target.name + "`: `" + text + "` is not a level") }102 }103 }104 if let Some(name) = Plan.argument(target, Plan.LEVEL_SWITCH_ARGUMENT) {105 if let Some(control) = switchOf(switches, Plan.switchName(name)) { sink = Sink.controlled(sink, &control) }106 }107 Ok(sink)108}109110111fn switchOf(switches: &Array[(Str, LevelSwitch.LevelSwitch)], name: Str) -> Option[LevelSwitch.LevelSwitch] {112 let found = List.find(switches, |entry: (Str, LevelSwitch.LevelSwitch)| entry[0] == name) ?113 Some(found[1])114}115116117118export fn switchNamed(loaded: &Loaded, name: Str) -> Option[LevelSwitch.LevelSwitch] { switchOf(&loaded.switches, Plan.switchName(name)) }119120121122export fn fromJson(text: Str, section: Str) -> Result[Array[(Str, Str)], Str] { Plan.fromJson(text, section) }123124125export fn fromJsonFile(path: Str, section: Str) -> Result[Array[(Str, Str)], Str] {126 match Io.read(path) {127 case Ok(text) => fromJson(text, section)128 case Err(problem) => Err("Cannot read `" + path + "`: " + show(problem))129 }130}131132133134export fn fromEnvironment(prefix: Str) -> Array[(Str, Str)] {135 var found: Array[(Str, Str)] = []136 for (name, value) in Env.variables() {137 if let Some(key) = Plan.environmentKey(name, prefix) { found = found.push((key, value)) }138 }139 found140}141142143export fn argument(arguments: &Array[(Str, Str)], name: Str) -> Option[Str] { Plan.argument(&Plan.Target{name: "", arguments: *arguments}, name) }144