improved logging
This commit is contained in:
@@ -316,31 +316,19 @@ var nota = use_core('nota')
|
||||
var ENETSERVICE = 0.1
|
||||
var REPLYTIMEOUT = 60 // seconds before replies are ignored
|
||||
|
||||
function caller_data(depth)
|
||||
{
|
||||
return {file: "nofile", line: 0}
|
||||
}
|
||||
// --- Logging system (bootstrap phase) ---
|
||||
// Early log: prints to console before toml/time/json are loaded.
|
||||
// Upgraded to full sink-based system after config loads (see load_log_config below).
|
||||
|
||||
function console_rec(line, file, msg) {
|
||||
return `[${text(_cell.id, 0, 5)}] [${file}:${line}]: ${msg}\n`
|
||||
// time: [${time.text("mb d yyyy h:nn:ss")}]
|
||||
}
|
||||
var log_config = null
|
||||
var channel_sinks = {}
|
||||
var wildcard_sinks = []
|
||||
var warned_channels = {}
|
||||
|
||||
function log(name, args) {
|
||||
var caller = caller_data(1)
|
||||
var msg = args[0]
|
||||
|
||||
if (name == 'console') {
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
} else if (name == 'error') {
|
||||
if (msg == null) msg = "error"
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
} else if (name == 'system') {
|
||||
msg = "[SYSTEM] " + msg
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
} else {
|
||||
log.console(`unknown log type: ${name}`)
|
||||
}
|
||||
if (msg == null) msg = ""
|
||||
os.print(`[${text(_cell.id, 0, 5)}] [${name}]: ${msg}\n`)
|
||||
}
|
||||
|
||||
function actor_die(err)
|
||||
@@ -428,6 +416,134 @@ core_extras.native_mode = native_mode
|
||||
var shop = use_core('internal/shop')
|
||||
if (native_mode) use_core('build')
|
||||
var time = use_core('time')
|
||||
var toml = use_core('toml')
|
||||
|
||||
// --- Logging system (full version) ---
|
||||
// Now that toml, time, fd, and json are available, upgrade the log function
|
||||
// from the bootstrap version to a configurable sink-based system.
|
||||
|
||||
function ensure_log_dir(path) {
|
||||
var parts = array(path, '/')
|
||||
var current = starts_with(path, '/') ? '/' : ''
|
||||
var i = 0
|
||||
// ensure parent dir (skip last element which is the filename)
|
||||
for (i = 0; i < length(parts) - 1; i++) {
|
||||
if (parts[i] == '') continue
|
||||
current = current + parts[i] + '/'
|
||||
if (!fd.is_dir(current)) fd.mkdir(current)
|
||||
}
|
||||
}
|
||||
|
||||
function build_sink_routing() {
|
||||
channel_sinks = {}
|
||||
wildcard_sinks = []
|
||||
var names = array(log_config.sink)
|
||||
arrfor(names, function(name) {
|
||||
var sink = log_config.sink[name]
|
||||
sink._name = name
|
||||
if (!is_array(sink.channels)) sink.channels = []
|
||||
if (is_text(sink.exclude)) sink.exclude = [sink.exclude]
|
||||
if (!is_array(sink.exclude)) sink.exclude = []
|
||||
if (sink.type == "file" && sink.path) ensure_log_dir(sink.path)
|
||||
arrfor(sink.channels, function(ch) {
|
||||
if (ch == "*") {
|
||||
wildcard_sinks[] = sink
|
||||
return
|
||||
}
|
||||
if (!channel_sinks[ch]) channel_sinks[ch] = []
|
||||
channel_sinks[ch][] = sink
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function load_log_config() {
|
||||
var log_path = null
|
||||
if (shop_path) {
|
||||
log_path = shop_path + '/log.toml'
|
||||
if (fd.is_file(log_path)) {
|
||||
log_config = toml.decode(text(fd.slurp(log_path)))
|
||||
}
|
||||
}
|
||||
if (!log_config || !log_config.sink) {
|
||||
log_config = {
|
||||
sink: {
|
||||
terminal: {
|
||||
type: "console",
|
||||
format: "pretty",
|
||||
channels: ["console", "error", "system"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
build_sink_routing()
|
||||
}
|
||||
|
||||
function pretty_format(rec) {
|
||||
var aid = text(rec.actor_id, 0, 5)
|
||||
var src = ""
|
||||
if (rec.source && rec.source.file)
|
||||
src = rec.source.file + ":" + text(rec.source.line)
|
||||
var ev = is_text(rec.event) ? rec.event : json.encode(rec.event, false)
|
||||
return `[${aid}] [${rec.channel}] ${src} ${ev}\n`
|
||||
}
|
||||
|
||||
function bare_format(rec) {
|
||||
var aid = text(rec.actor_id, 0, 5)
|
||||
var ev = is_text(rec.event) ? rec.event : json.encode(rec.event, false)
|
||||
return `[${aid}] ${ev}\n`
|
||||
}
|
||||
|
||||
function sink_excluded(sink, channel) {
|
||||
var excluded = false
|
||||
if (!sink.exclude || length(sink.exclude) == 0) return false
|
||||
arrfor(sink.exclude, function(ex) {
|
||||
if (ex == channel) excluded = true
|
||||
})
|
||||
return excluded
|
||||
}
|
||||
|
||||
function dispatch_to_sink(sink, rec) {
|
||||
var line = null
|
||||
if (sink_excluded(sink, rec.channel)) return
|
||||
if (sink.type == "console") {
|
||||
if (sink.format == "json")
|
||||
os.print(json.encode(rec, false) + "\n")
|
||||
else if (sink.format == "bare")
|
||||
os.print(bare_format(rec))
|
||||
else
|
||||
os.print(pretty_format(rec))
|
||||
} else if (sink.type == "file") {
|
||||
line = json.encode(rec, false) + "\n"
|
||||
fd.slurpappend(sink.path, stone(blob(line)))
|
||||
}
|
||||
}
|
||||
|
||||
load_log_config()
|
||||
|
||||
log = function(name, args) {
|
||||
var sinks = channel_sinks[name]
|
||||
var event = args[0]
|
||||
|
||||
if (!sinks && length(wildcard_sinks) == 0) {
|
||||
if (!warned_channels[name]) {
|
||||
warned_channels[name] = true
|
||||
os.print(`[warn] log channel '${name}' has no sinks configured\n`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var caller = caller_info(2)
|
||||
var rec = {
|
||||
actor_id: _cell.id,
|
||||
timestamp: time.number(),
|
||||
channel: name,
|
||||
event: event,
|
||||
source: caller
|
||||
}
|
||||
|
||||
if (sinks) arrfor(sinks, function(sink) { dispatch_to_sink(sink, rec) })
|
||||
arrfor(wildcard_sinks, function(sink) { dispatch_to_sink(sink, rec) })
|
||||
}
|
||||
|
||||
var pronto = use_core('pronto')
|
||||
var fallback = pronto.fallback
|
||||
@@ -438,6 +554,7 @@ var sequence = pronto.sequence
|
||||
runtime_env.actor = actor
|
||||
runtime_env.log = log
|
||||
runtime_env.send = send
|
||||
runtime_env.shop_path = shop_path
|
||||
runtime_env.fallback = fallback
|
||||
runtime_env.parallel = parallel
|
||||
runtime_env.race = race
|
||||
|
||||
Reference in New Issue
Block a user