-
-
Notifications
You must be signed in to change notification settings - Fork 0
Web Timing And Bridge
Web.RequestLogging writes one completion event per HTTP request — middleware for a route chain, handler when the handler adds properties or a failure through a Web.Diagnostic collector. Options choose the message, the level, extra enrichment, and whether the query stays in the path; a traceparent header sets the trace and span.
Web.Correlation gives each request one identifier: middleware keeps the one a header brings or generates one, loggerFor hands a handler a logger carrying it, and enrich adds it to the completion event.
Timing.begin measures an operation and writes one event when it is completed or abandoned; run and runResult wrap an action; warnAfter, at, enrichWith, completeWith, abandonWith, and cancel refine it.
Bridge.standard turns a Std.Log logger's lines into events, so libraries that log through the standard library join the program's pipeline.
module Ex09Web
import Std.Http.Server.Route as Route
import Std.Http as Web
import Std.Io as Io
import Std.Log as Standard
import Std.Map as Map
import Std.Sync as Sync
import PuduLangLog.Bridge as Bridge
import PuduLangLog.Configuration as Configuration
import PuduLangLog.Event as Event
import PuduLangLog.Failure as Failure
import PuduLangLog as Log
import PuduLangLog.Logger as Logger
import PuduLangLog.Sinks.Memory as Memory
import PuduLangLog.Timing as Timing
import PuduLangLog.Value as Value
import PuduLangLog.Web.Correlation as Correlation
import PuduLangLog.Web.Diagnostic as Diagnostic
import PuduLangLog.Web.RequestLogging as RequestLogging
fn request(target: Str, headers: Array[(Str, Str)]) -> Route.Request {
Route.Request{message: Web.Request{..Web.request(Web.Get, target), headers: headers}, path: Route.pathOf(target), params: Map.empty(), query: Route.queryOf(target), peer: "127.0.0.1"}
}
export fn main() -> Int {
let memory = Memory.create()
let logger = Configuration.create().writeTo(Memory.sink(&memory)).createLogger()
let correlation = Correlation.defaults()
let logging = RequestLogging.handler(&logger, RequestLogging.Options{..RequestLogging.defaults(), enrich: Correlation.enrich(correlation)}, fn(incoming: Route.Request, collector: &Diagnostic.Collector) -> Web.Response {
Correlation.loggerFor(&logger, &incoming, &correlation).information("Looking up \{Path\}", [Value.text(incoming.path)])
Diagnostic.set(collector, "UserId", Value.int(7))
if incoming.path == "/fail" { Diagnostic.setFailure(collector, Failure.of("LookupError", "no such order")) }
Web.Response{status: Web.status(if incoming.path == "/fail" { 500 } else { 200 }), headers: [], body: "", binaryBody: None}
})
let chain = Correlation.middleware(correlation)
let answered = chain(request("/orders/7?page=2", [("x-correlation-id", "abc-123")]), logging)
let _failed = chain(request("/fail", [("x-correlation-id", "def-456")]), logging)
let _echo = Io.writeLine("response header: " + show(Web.header(&answered.headers, "x-correlation-id")))
let ticks = Sync.counter(0)
let clock = fn() -> Int {
match Sync.increment(&ticks, 250) {
case Ok(now) => now
case Err(_) => 0
}
}
Timing.beginWith(&logger, "Rebuilding index \{Index\}", [Value.text("orders")], clock).complete()
Timing.beginWith(&logger, "Importing \{File\}", [Value.text("prices.csv")], clock).abandon()
let standard = Bridge.standard(&logger)
let _bridged = Standard.warn(&standard.named("legacy.billing").carrying("invoice", "INV-9"), "retrying \{invoice\}")
for event in Memory.events(&memory) {
let correlated = match Event.property(&event, "CorrelationId") {
case Some(Log.Scalar(Log.Text(id))) => " [" + id + "]"
case _ => ""
}
let _line = Io.writeLine(show(event.level) + ": " + Event.message(&event) + correlated)
}
Logger.close(&logger)
0
}
Check it and run it:
pudu check src/Ex09Web.pudu
pudu run src/Ex09Web.puduOutput (the request times vary from run to run):
response header: Some("abc-123")
Information: Looking up "/orders/7" [abc-123]
Information: HTTP "GET" "/orders/7" responded 200 in 4.0000 ms [abc-123]
Information: Looking up "/fail" [def-456]
Error: HTTP "GET" "/fail" responded 500 in 1.0000 ms [def-456]
Information: Rebuilding index "orders" completed in 250.0 ms
Warning: Importing "prices.csv" abandoned in 250.0 ms
Warning: retrying {invoice}