Repository navigation
Logging Propagation And Metrics
Handlers.Logging.observer(logger) writes pudu-lang-log events where a request enters and leaves
the pipeline (PuduLangHttpClient.<name>.LogicalHandler) and where it reaches the primary handler
(PuduLangHttpClient.<name>.ClientHandler), with headers at the verbose level. Observers never see
the values of authorization, proxy-authorization, cookie, or set-cookie, nor of the headers
named with redactingHeaders or matched by redactingWhen.
module Ex09Observability
import Std.Http as Http
import Std.Io as Io
import Std.Json as Json
import PuduLangLog.Configuration as Configuration
import PuduLangLog as Log
import PuduLangLog.Sinks.Memory as Memory
import PuduLangHttpClient as HttpClient
import PuduLangHttpClient.Client as Client
import PuduLangHttpClient.Factory as Factory
import PuduLangHttpClient.Factory.Builder as Builder
import PuduLangHttpClient.Handlers.Authorization as Authorization
import PuduLangHttpClient.Handlers.Logging as Logging
import PuduLangHttpClient.Handlers.Metrics as Metrics
import PuduLangHttpClient.Handlers.Propagation as Propagation
import PuduLangHttpClient.Request as Request
import PuduLangHttpClient.Response as Response
import PuduLangHttpClient.Stub as Stub
export fn main() -> Int {
let memory = Memory.create()
let logger = Configuration.create().minimumLevel(Log.Verbose).writeTo(Memory.sink(&memory)).createLogger()
let meter = Metrics.meter()
let stub = Stub.create()
Stub.when(&stub, |request: Request.Request| Request.header(&request, "authorization") == Some("Bearer fresh"), |request: Request.Request| Ok(Response.answer(&request, 200).withText(match Request.header(&request, "x-correlation-id") {
case Some(found) => found
case None => "none"
})))
Stub.when(&stub, |_request: Request.Request| true, |request: Request.Request| Ok(Response.answer(&request, 401)))
let factory = match Factory.build([
Builder.defaults().observedBy(Logging.observer(logger)).redactingHeaders(&["x-api-key"]),
Builder.named("orders").withBaseAddress("https://orders.test/").withPrimary(|| Stub.send(&stub)).withDefaultHeader("x-api-key", "k-123")
.withHandler(Metrics.handler(&meter))
.withHandler(Propagation.handler(&["x-correlation-id"], || [("x-correlation-id", "order-77")]))
.withHandler(Authorization.bearer(|refresh: Bool| Ok(if refresh { "fresh" } else { "expired" })))
]) {
case Ok(built) => built
case Err(invalid) => panic(Factory.explain(&invalid))
}
let answered = Client.getText(&Factory.createClient(&factory, "orders"), "status")
let snapshot = Metrics.snapshot(&meter)
let _a = Io.writeLine(show(answered) + " " + Json.encode(&snapshot.encode()))
for line in Memory.messages(&memory) { let _said = Io.writeLine(line) }
if answered == Ok("order-77") && snapshot.successful == 1 && Memory.messages(&memory).filter(|line: Str| line.contains("k-123")).isEmpty() { 0 } else { 1 }
}
Check it and run it:
pudu check src/Ex09Observability.pudu
pudu run src/Ex09Observability.puduOutput:
Ok("order-77") {"requests":1,"active":0,"informational":0,"successful":1,"redirected":0,"clientErrors":0,"serverErrors":0,"failed":0,"totalMillis":3,"longestMillis":3}
Start processing HTTP request "GET" "https://orders.test/status"
Request headers: "x-api-key: *"
Sending HTTP request "GET" "https://orders.test/status"
Request headers: "x-api-key: *, x-correlation-id: order-77, authorization: *"
Received HTTP response headers after 1 ms - 401
Response headers: ""
Sending HTTP request "GET" "https://orders.test/status"
Request headers: "x-api-key: *, x-correlation-id: order-77, authorization: *"
Received HTTP response headers after 0 ms - 200
Response headers: "content-type: text/plain; charset=utf-8"
End processing HTTP request after 3 ms - 200
Response headers: "content-type: text/plain; charset=utf-8"
Handlers.Propagation.handler(names, source) copies headers of the operation in progress onto every
outgoing request that lacks them, and renaming copies them under another name.
Handlers.Authorization.basic and bearer add credentials; a bearer token is fetched again once
when the server answers 401. Handlers.Metrics counts requests by status class, failures, requests
in flight, and durations into a snapshot that derives Json.Encode.
- Previous: Resilience
- Next: Endpoints And JSON
- Reference Index