///|
pub(all) enum SpanLinkKind {
SpanLinked
SpanFollowsFrom
SpanChildOf
} derive(Eq, Compare, Debug)
///|
pub(all) struct SpanLink {
trace_id : String
span_id : String
kind : SpanLinkKind
fields : Array[Field]
trace_state : String
} derive(Debug)
///|
pub(all) struct SpanLifecycleObserver {
on_created : (Span) -> Unit
on_entered : (Span) -> Unit
on_recorded : (Span, Field) -> Unit
on_exited : (Span) -> Unit
on_closed : (Span) -> Unit
on_linked : (Span, SpanLink) -> Unit
}
///|
let global_span_observer : Ref[SpanLifecycleObserver?] = { val: None }
///|
fn span_snapshot(span : Span) -> Span {
{
name: span.name,
fields: span.fields.copy(),
links: span.links.map(fn(link) { link_snapshot(link) }),
trace_id: span.trace_id,
span_id: span.span_id,
parent_span_id: span.parent_span_id,
trace_flags: span.trace_flags,
trace_state: copy_trace_state(span.trace_state),
kind: span.kind,
start_time: span.start_time,
end_time: span.end_time,
active: span.active,
status: span.status,
status_message: span.status_message,
}
}
///|
fn link_snapshot(link : SpanLink) -> SpanLink {
{
trace_id: link.trace_id,
span_id: link.span_id,
kind: link.kind,
fields: link.fields.copy(),
trace_state: link.trace_state,
}
}
///|
pub fn span_lifecycle_observer(
on_created? : (Span) -> Unit = fn(_span : Span) { },
on_entered? : (Span) -> Unit = fn(_span : Span) { },
on_recorded? : (Span, Field) -> Unit = fn(_span : Span, _field : Field) { },
on_exited? : (Span) -> Unit = fn(_span : Span) { },
on_closed? : (Span) -> Unit = fn(_span : Span) { },
on_linked? : (Span, SpanLink) -> Unit = fn(_span : Span, _link : SpanLink) {
},
) -> SpanLifecycleObserver {
{ on_created, on_entered, on_recorded, on_exited, on_closed, on_linked }
}
///|
pub fn completed_span_observer(
on_completed : (Span) -> Unit,
) -> SpanLifecycleObserver {
span_lifecycle_observer(on_closed=on_completed)
}
///|
pub fn compose_span_observers(
observers : Array[SpanLifecycleObserver],
) -> SpanLifecycleObserver {
span_lifecycle_observer(
on_created=fn(span) {
observers.each(fn(observer) { (observer.on_created)(span_snapshot(span)) })
},
on_entered=fn(span) {
observers.each(fn(observer) { (observer.on_entered)(span_snapshot(span)) })
},
on_recorded=fn(span, field) {
observers.each(fn(observer) {
(observer.on_recorded)(span_snapshot(span), field)
})
},
on_exited=fn(span) {
observers.each(fn(observer) { (observer.on_exited)(span_snapshot(span)) })
},
on_closed=fn(span) {
observers.each(fn(observer) { (observer.on_closed)(span_snapshot(span)) })
},
on_linked=fn(span, link) {
observers.each(fn(observer) {
(observer.on_linked)(span_snapshot(span), link_snapshot(link))
})
},
)
}
///|
pub fn set_span_observer(observer : SpanLifecycleObserver) -> Unit {
global_span_observer.val = Some(observer)
}
///|
pub fn clear_span_observer() -> Unit {
global_span_observer.val = None
}
///|
pub fn span_link(
target : Span,
kind? : SpanLinkKind = SpanLinked,
fields? : Array[Field] = [],
) -> SpanLink {
{
trace_id: target.trace_id,
span_id: target.span_id,
kind,
fields: fields.copy(),
trace_state: "",
}
}
///|
pub fn span_link_from_context(
ctx : SpanContext,
kind? : SpanLinkKind = SpanLinked,
fields? : Array[Field] = [],
) -> SpanLink {
{
trace_id: ctx.trace_id,
span_id: ctx.span_id,
kind,
fields: fields.copy(),
trace_state: ctx.trace_state.to_header(),
}
}
///|
pub fn Span::link(
self : Span,
target : Span,
fields? : Array[Field] = [],
) -> Unit {
let link = span_link(target, fields~)
self.links.push(link)
notify_span_linked(self, link)
}
///|
pub fn Span::follows_from(
self : Span,
target : Span,
fields? : Array[Field] = [],
) -> Unit {
let link = span_link(target, kind=SpanFollowsFrom, fields~)
self.links.push(link)
notify_span_linked(self, link)
}
///|
pub fn Span::link_context(
self : Span,
ctx : SpanContext,
fields? : Array[Field] = [],
) -> Unit {
let link = span_link_from_context(ctx, fields~)
self.links.push(link)
notify_span_linked(self, link)
}
///|
pub fn Span::follows_from_context(
self : Span,
ctx : SpanContext,
fields? : Array[Field] = [],
) -> Unit {
let link = span_link_from_context(ctx, kind=SpanFollowsFrom, fields~)
self.links.push(link)
notify_span_linked(self, link)
}
///|
pub impl Show for SpanLinkKind with fn output(self, logger) {
logger.write_string(self.to_string())
}
///|
pub fn SpanLinkKind::to_string(self : SpanLinkKind) -> String {
match self {
SpanLinked => "LINK"
SpanFollowsFrom => "FOLLOWS_FROM"
SpanChildOf => "CHILD_OF"
}
}
///|
pub fn SpanLinkKind::to_json(self : SpanLinkKind) -> Json {
self.to_string().to_json()
}
///|
pub fn SpanLink::to_json(self : SpanLink) -> Json {
let m : Map[String, Json] = {}
m.set("trace_id", self.trace_id.to_json())
m.set("span_id", self.span_id.to_json())
m.set("kind", self.kind.to_json())
m.set("fields", fields_to_json(self.fields))
if !self.trace_state.is_empty() {
m.set("trace_state", self.trace_state.to_json())
}
m.to_json()
}
///|
fn notify_span_created(span : Span) -> Unit {
match global_span_observer.val {
Some(observer) => (observer.on_created)(span_snapshot(span))
None => ()
}
}
///|
fn notify_span_entered(span : Span) -> Unit {
match global_span_observer.val {
Some(observer) => (observer.on_entered)(span_snapshot(span))
None => ()
}
}
///|
fn notify_span_recorded(span : Span, field : Field) -> Unit {
match global_span_observer.val {
Some(observer) => (observer.on_recorded)(span_snapshot(span), field)
None => ()
}
}
///|
fn notify_span_exited(span : Span) -> Unit {
match global_span_observer.val {
Some(observer) => (observer.on_exited)(span_snapshot(span))
None => ()
}
}
///|
fn notify_span_closed(span : Span) -> Unit {
match global_span_observer.val {
Some(observer) => (observer.on_closed)(span_snapshot(span))
None => ()
}
}
///|
fn notify_span_linked(span : Span, link : SpanLink) -> Unit {
match global_span_observer.val {
Some(observer) =>
(observer.on_linked)(span_snapshot(span), link_snapshot(link))
None => ()
}
}