OpenTelemetry for MoonBit.
fn traced_request(rand : (Int32) -> Bytes, at : Int64) -> @trace.Span raise {
let tracer = @trace.Tracer::new(
rand~,
sink=@trace.Recorder::new(),
sampler=@trace.parent_based(root=@trace.always),
)
let span = tracer.start("GET /orders/{id}", kind=@trace.Server, at~)
span.set_attribute("http.response.status_code", @trace.Int(200L))
span.finish(at=at + 1_000_000L)
span
}moon add moonbitstack/moontel#tel.span("orders.place", kind=server, attr="item")
fn place(item : String) -> Int raise Sold {
...
}let receipt = place_traced(tracer, "tea", now=clock)
let nested = place_traced(tracer, "tea", now=clock, parent=span.context())rule(name: "span", command: "moonx moonbitstack/moontel/cmd/span -C \"$mod_dir\" \"$input\" -o \"$output\"")dev_build(rule: "span", input: "orders.mbt", output: "orders.g.mbt")
formatter(ignore: [ "orders.g.mbt" ])struct Printer {}
impl @trace.Sink for Printer with fn accept(self, span) {
ignore(self)
println("\{span.name} took \{span.duration()} ns")
}
fn traced_with_printer(rand : (Int32) -> Bytes, at : Int64) -> Unit raise {
let tracer = @trace.Tracer::new(rand~, sink=Printer::{}, sampler=@trace.always)
let span = tracer.start("GET /orders/{id}", at~)
span.finish(at=at + 1L)
}| Sampler | What it decides |
|---|---|
| always | Everything is sampled. |
| never | Nothing is sampled. |
| ratio(ratio=0.1) | A tenth of traces, chosen by the trace id. |
| parent_based(root=...) | The parent's decision, or root when there is no parent. The other four rows, a remote or local parent sampled or not, are arguments too. |
struct Everything {}
impl @trace.Sampler for Everything with fn should_sample(_, _) {
@trace.RecordAndSample
}
impl @trace.Sampler for Everything with fn description(_) {
"Everything"
}fn trace_on_request(headers : Array[(String, String)]) -> @trace.TraceContext? {
@propagate.extract(headers).context
}
fn inbound_headers() -> Array[(String, String)] {
[
("traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"),
("tracestate", "vendor=abc"),
("baggage", "userId=alice"),
]
}fn forward(
headers : Array[(String, String)],
span : @trace.Span,
baggage : @propagate.Baggage,
) -> Array[(String, String)] {
@propagate.inject(headers, { context: Some(span.context()), baggage })
}fn layer(tracer : @trace.Tracer, now : () -> Int64) -> @asgi.Tracing {
@asgi.Tracing::new(tracer~, now~)
}| Item | Signature |
|---|---|
| Tracer::new | (rand~ : (Int32) -> Bytes, sink~ : &Sink, sampler~ : &Sampler, limits? : Limits) -> Tracer |
| Tracer::start | (name, parent? : TraceContext, kind? : SpanKind, at~ : Int64, attributes?, links?) -> Span raise Refused |
| Span::finish | (at~ : Int64) -> Unit |
| Span::set_attribute | (key : String, value : Value) -> Unit |
| Span::set_attributes | (Array[(String, Value)]) -> Unit |
| Span::add_event | (name, attributes?, at~ : Int64) -> Unit |
| Span::record_exception | (message~, at~, type_?, stacktrace?, attributes?) -> Unit |
| Span::set_status | (Status) -> Unit |
| Span::set_name | (String) -> Unit |
| Span::add_link | (Link) -> Unit |
| Span::context | () -> TraceContext |
| Span::is_recording, is_ended | () -> Bool |
| SpanData | context, parent, name, kind, start, end, attributes, events, links, status, dropped |
| SpanData::sampled, duration | () -> Bool, () -> Int64 |
| TraceContext | trace_id : Bytes, span_id : Bytes, flags : Byte, state : TraceState |
| TraceContext::to_traceparent | () -> String |
| TraceState::parse | (StringView) -> TraceState raise Refused |
| TraceState::to_header | () -> String |
| TraceState::get, insert, delete | read a member, add one closest to this hop, drop one |
| Sampler | should_sample(SampleRequest) -> Decision, description() -> String |
| Sink | accept(SpanData) -> Unit |
| Limits | attributes, events, links, attributes_per_event, attribute_value_length |
| Status | Unset, Ok, Error(String) |
| SpanKind | Internal, Server, Client, Producer, Consumer |
| Value | Bool, Int, Double, Str, and the four typed arrays |
| Item | Signature |
|---|---|
| extract | (Array[(String, String)]) -> Extracted |
| inject | (Array[(String, String)], Extracted) -> Array[(String, String)] |
| parse | (traceparent : StringView, tracestate? : StringView) -> TraceContext raise Refused |
| parse_traceparent | (StringView) -> TraceContext raise Refused |
| parse_tracestate | (StringView) -> TraceState raise Refused |
| Baggage | members : Array[Member], with parse, to_header, get, len, add |
| Item | Signature |
|---|---|
| Tracing::new | (tracer~, now~ : () -> Int64, name?, baggage? : Bool, ignore? : Array[String]) -> Tracing |
| Tracing::wrap | (AsgiApp) -> AsgiApp, and Layer for stacking with other middleware |
moon run src/examples/00-hello # a root span, a child, attributes and an event
moon run src/examples/01-propagate # reading a request's context, writing the next one's
moon run src/examples/02-asgi # the middleware over an applicationlet baggage_byte_limit : Intlet baggage_member_limit : Intfn method_and_path(http_method : String, path : String) -> Stringversion(2)-trace-id(32)-parent-id(16)-trace-flags(2)let random_flag : Bytelet sampled_flag : Bytelet traceparent_header : Stringlet tracestate_header : Stringlet tracestate_limit : IntInstall
Download zipOpenTelemetry for MoonBit.