///|
pub(all) struct Event {
level : Level
message : String
fields : Array[Field]
timestamp : UInt64
source : String
} derive(Debug)
///|
let global_subscriber : Ref[((Event) -> Unit)?] = { val: None }
///|
let global_min_level : Ref[Level] = { val: Trace }
///|
pub fn set_min_level(level : Level) -> Unit {
global_min_level.val = level
}
///|
pub fn get_min_level() -> Level {
global_min_level.val
}
///|
pub fn set_subscriber(f : (Event) -> Unit) -> Unit {
global_subscriber.val = Some(f)
}
///|
pub fn clear_subscriber() -> Unit {
global_subscriber.val = None
}
///|
fn merge_fields(fields : Array[Field]) -> Array[Field] {
if global_context.val.is_empty() {
fields
} else {
let explicit_keys : Map[String, Bool] = {}
fields.each(fn(f) { explicit_keys.set(f.key, true) })
let merged : Array[Field] = []
global_context.val.each(fn(k, v) {
if !explicit_keys.contains(k) {
merged.push({ key: k, value: v })
}
})
fields.each(fn(f) { merged.push(f) })
merged
}
}
///|
fn dispatch(event : Event) -> Unit {
match global_subscriber.val {
Some(f) => f(event)
None => ()
}
}
///|
fn emit(level : Level, msg : String, fields : Array[Field]) -> Unit {
if level < global_min_level.val {
return
}
match global_subscriber.val {
Some(f) =>
f(Event::{
level,
message: msg,
fields: merge_fields(fields),
timestamp: @env.now(),
source: "",
})
None => ()
}
}
///|
fn emit_with_loc(
level : Level,
msg : String,
fields : Array[Field],
loc : SourceLoc,
) -> Unit {
let source = extract_package(loc)
if !should_log_module(source, level) {
return
}
let event = Event::{
level,
message: msg,
fields: merge_fields(fields),
timestamp: @env.now(),
source,
}
dispatch(event)
}
///|
fn is_span_correlation_field(key : String) -> Bool {
key == "trace_id" || key == "span_id" || key == "parent_span_id"
}
///|
fn span_event_fields(span : Span, fields : Array[Field]) -> Array[Field] {
let merged : Array[Field] = []
fields.each(fn(field) {
if !is_span_correlation_field(field.key) {
merged.push(field)
}
})
merged.push({ key: "trace_id", value: span.trace_id.to_json() })
merged.push({ key: "span_id", value: span.span_id.to_json() })
merged.push({ key: "parent_span_id", value: span.parent_span_id.to_json() })
merged
}
///|
fn emit_in_span_with_loc(
span : Span,
level : Level,
msg : String,
fields : Array[Field],
loc : SourceLoc,
) -> Unit {
let source = extract_package(loc)
if !should_log_module(source, level) {
return
}
let event = Event::{
level,
message: msg,
fields: merge_fields(span_event_fields(span, fields)),
timestamp: @env.now(),
source,
}
dispatch(event)
}
///|
#callsite(autofill(loc))
pub fn event_in_span(
span : Span,
level : Level,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(span, level, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn trace_in_span(
span : Span,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(span, Trace, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn debug_in_span(
span : Span,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(span, Debug, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn info_in_span(
span : Span,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(span, Info, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn warn_in_span(
span : Span,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(span, Warn, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn error_in_span(
span : Span,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(span, Error_, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn Span::event(
self : Span,
level : Level,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(self, level, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn Span::trace(
self : Span,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(self, Trace, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn Span::debug(
self : Span,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(self, Debug, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn Span::info(
self : Span,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(self, Info, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn Span::warn(
self : Span,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(self, Warn, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn Span::error(
self : Span,
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_in_span_with_loc(self, Error_, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn info(
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_with_loc(Info, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn warn(
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_with_loc(Warn, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn error(
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_with_loc(Error_, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn debug(
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_with_loc(Debug, msg, fields, loc)
}
///|
#callsite(autofill(loc))
pub fn trace(
msg : String,
fields? : Array[Field] = [],
loc~ : SourceLoc,
) -> Unit {
emit_with_loc(Trace, msg, fields, loc)
}
///|
pub fn Event::to_json(self : Event) -> Json {
let m : Map[String, Json] = {}
m.set("level", self.level.to_json())
m.set("message", self.message.to_json())
m.set("timestamp", self.timestamp.to_json())
m.set("source", self.source.to_json())
m.set("fields", fields_to_json(self.fields))
m.to_json()
}