better log
This commit is contained in:
@@ -900,7 +900,11 @@ function build_sink_routing() {
|
|||||||
if (!is_array(sink.exclude)) sink.exclude = []
|
if (!is_array(sink.exclude)) sink.exclude = []
|
||||||
if (is_text(sink.stack)) sink.stack = [sink.stack]
|
if (is_text(sink.stack)) sink.stack = [sink.stack]
|
||||||
if (!is_array(sink.stack)) sink.stack = []
|
if (!is_array(sink.stack)) sink.stack = []
|
||||||
if (sink.type == "file" && sink.path) ensure_log_dir(sink.path)
|
if (sink.type == "file" && sink.path) {
|
||||||
|
ensure_log_dir(sink.path)
|
||||||
|
if (sink.mode == "overwrite")
|
||||||
|
fd.slurpwrite(sink.path, stone(_make_blob("")))
|
||||||
|
}
|
||||||
arrfor(sink.stack, function(ch) {
|
arrfor(sink.stack, function(ch) {
|
||||||
stack_channels[ch] = true
|
stack_channels[ch] = true
|
||||||
})
|
})
|
||||||
@@ -936,6 +940,12 @@ function load_log_config() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
build_sink_routing()
|
build_sink_routing()
|
||||||
|
var names = array(log_config.sink)
|
||||||
|
arrfor(names, function(name) {
|
||||||
|
var sink = log_config.sink[name]
|
||||||
|
if (sink.type == "file")
|
||||||
|
os.print("[log] " + name + " -> " + sink.path + "\n")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function pretty_format(rec) {
|
function pretty_format(rec) {
|
||||||
@@ -1001,6 +1011,7 @@ function sink_excluded(sink, channel) {
|
|||||||
|
|
||||||
function dispatch_to_sink(sink, rec) {
|
function dispatch_to_sink(sink, rec) {
|
||||||
var line = null
|
var line = null
|
||||||
|
var st = null
|
||||||
if (sink_excluded(sink, rec.channel)) return
|
if (sink_excluded(sink, rec.channel)) return
|
||||||
if (sink.type == "console") {
|
if (sink.type == "console") {
|
||||||
if (sink.format == "json")
|
if (sink.format == "json")
|
||||||
@@ -1013,7 +1024,12 @@ function dispatch_to_sink(sink, rec) {
|
|||||||
os.print(pretty_format(rec))
|
os.print(pretty_format(rec))
|
||||||
} else if (sink.type == "file") {
|
} else if (sink.type == "file") {
|
||||||
line = json.encode(rec, false) + "\n"
|
line = json.encode(rec, false) + "\n"
|
||||||
fd.slurpappend(sink.path, stone(blob(line)))
|
if (sink.max_size) {
|
||||||
|
st = fd.stat(sink.path)
|
||||||
|
if (st && st.size > sink.max_size)
|
||||||
|
fd.slurpwrite(sink.path, stone(_make_blob("")))
|
||||||
|
}
|
||||||
|
fd.slurpappend(sink.path, stone(_make_blob(line)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
366
log.ce
366
log.ce
@@ -1,18 +1,17 @@
|
|||||||
// cell log - Manage and read log sinks
|
// cell log - Manage log sink configuration
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// cell log list List configured sinks
|
// cell log list Show sinks and channel routing
|
||||||
// cell log channels List channels with status
|
// cell log channels List channels with status
|
||||||
// cell log enable <channel> Enable a channel on terminal
|
// cell log enable <channel> Enable a channel on terminal
|
||||||
// cell log disable <channel> Disable a channel on terminal
|
// cell log disable <channel> Disable a channel on terminal
|
||||||
// cell log add <name> console [opts] Add a console sink
|
// cell log add <name> console [opts] Add a console sink
|
||||||
// cell log add <name> file <path> [opts] Add a file sink
|
// cell log add <name> file <path> [opts] Add a file sink
|
||||||
// cell log remove <name> Remove a sink
|
// cell log remove <name> Remove a sink
|
||||||
// cell log read <sink> [opts] Read from a file sink
|
// cell log route <channel> <sink> Route a channel to a sink
|
||||||
// cell log tail <sink> [--lines=N] Follow a file sink
|
// cell log unroute <channel> <sink> Remove a channel from a sink
|
||||||
//
|
// cell log stack <channel> Enable stack traces on a channel
|
||||||
// The --stack option controls which channels capture a stack trace.
|
// cell log unstack <channel> Disable stack traces on a channel
|
||||||
// Default: --stack=error (errors always show a stack trace).
|
|
||||||
|
|
||||||
var toml = use('toml')
|
var toml = use('toml')
|
||||||
var fd = use('fd')
|
var fd = use('fd')
|
||||||
@@ -21,9 +20,8 @@ var json = use('json')
|
|||||||
var log_path = shop_path + '/log.toml'
|
var log_path = shop_path + '/log.toml'
|
||||||
|
|
||||||
function load_config() {
|
function load_config() {
|
||||||
if (fd.is_file(log_path)) {
|
if (fd.is_file(log_path))
|
||||||
return toml.decode(text(fd.slurp(log_path)))
|
return toml.decode(text(fd.slurp(log_path)))
|
||||||
}
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,26 +46,24 @@ function print_help() {
|
|||||||
log.console("Usage: cell log <command> [options]")
|
log.console("Usage: cell log <command> [options]")
|
||||||
log.console("")
|
log.console("")
|
||||||
log.console("Commands:")
|
log.console("Commands:")
|
||||||
log.console(" list List configured sinks")
|
log.console(" list Show sinks and channel routing")
|
||||||
log.console(" channels List channels with status")
|
log.console(" channels List channels with status")
|
||||||
log.console(" enable <channel> Enable a channel on terminal")
|
log.console(" enable <channel> Enable a channel on terminal")
|
||||||
log.console(" disable <channel> Disable a channel on terminal")
|
log.console(" disable <channel> Disable a channel on terminal")
|
||||||
log.console(" add <name> console [opts] Add a console sink")
|
log.console(" add <name> console [opts] Add a console sink")
|
||||||
log.console(" add <name> file <path> [opts] Add a file sink")
|
log.console(" add <name> file <path> [opts] Add a file sink")
|
||||||
log.console(" remove <name> Remove a sink")
|
log.console(" remove <name> Remove a sink")
|
||||||
log.console(" read <sink> [opts] Read from a file sink")
|
log.console(" route <channel> <sink> Route a channel to a sink")
|
||||||
log.console(" tail <sink> [--lines=N] Follow a file sink")
|
log.console(" unroute <channel> <sink> Remove a channel from a sink")
|
||||||
|
log.console(" stack <channel> Enable stack traces on a channel")
|
||||||
|
log.console(" unstack <channel> Disable stack traces on a channel")
|
||||||
log.console("")
|
log.console("")
|
||||||
log.console("Options for add:")
|
log.console("Options for add:")
|
||||||
log.console(" --format=pretty|bare|json Output format (default: pretty for console, json for file)")
|
log.console(" --format=pretty|bare|json Output format (default: pretty for console, json for file)")
|
||||||
log.console(" --channels=ch1,ch2 Channels to subscribe (default: console,error,system)")
|
log.console(" --channels=ch1,ch2 Channels to subscribe (default: *)")
|
||||||
log.console(" --exclude=ch1,ch2 Channels to exclude (for wildcard sinks)")
|
log.console(" --exclude=ch1,ch2 Channels to exclude")
|
||||||
log.console(" --stack=ch1,ch2 Channels that capture a stack trace (default: error)")
|
log.console(" --mode=append|overwrite File write mode (default: append)")
|
||||||
log.console("")
|
log.console(" --max_size=N Max file size in bytes before truncation")
|
||||||
log.console("Options for read:")
|
|
||||||
log.console(" --lines=N Show last N lines (default: all)")
|
|
||||||
log.console(" --channel=X Filter by channel")
|
|
||||||
log.console(" --since=timestamp Only show entries after timestamp")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parse_opt(arg, prefix) {
|
function parse_opt(arg, prefix) {
|
||||||
@@ -77,36 +73,85 @@ function parse_opt(arg, prefix) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function format_entry(entry) {
|
// Collect all stack channels across all sinks
|
||||||
var aid = text(entry.actor_id, 0, 5)
|
function collect_stack_channels(config) {
|
||||||
var src = ""
|
var stack_chs = {}
|
||||||
var ev = null
|
var names = array(config.sink)
|
||||||
if (entry.source && entry.source.file)
|
arrfor(names, function(n) {
|
||||||
src = entry.source.file + ":" + text(entry.source.line)
|
var s = config.sink[n]
|
||||||
ev = is_text(entry.event) ? entry.event : json.encode(entry.event)
|
if (is_array(s.stack)) {
|
||||||
return "[" + aid + "] [" + entry.channel + "] " + src + " " + ev
|
arrfor(s.stack, function(ch) { stack_chs[ch] = true })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return stack_chs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find which sinks a stack channel is declared on (for modification)
|
||||||
|
function find_stack_sink(config, channel) {
|
||||||
|
var names = array(config.sink)
|
||||||
|
var found = null
|
||||||
|
arrfor(names, function(n) {
|
||||||
|
if (found) return
|
||||||
|
var s = config.sink[n]
|
||||||
|
if (is_array(s.stack)) {
|
||||||
|
arrfor(s.stack, function(ch) {
|
||||||
|
if (ch == channel) found = n
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
function do_list() {
|
function do_list() {
|
||||||
var config = load_config()
|
var config = load_config()
|
||||||
var names = null
|
var names = null
|
||||||
|
var channel_routing = {}
|
||||||
|
var stack_chs = null
|
||||||
names = (config && config.sink) ? array(config.sink) : []
|
names = (config && config.sink) ? array(config.sink) : []
|
||||||
if (length(names) == 0) {
|
if (length(names) == 0) {
|
||||||
log.console("No log sinks configured.")
|
log.console("No log sinks configured.")
|
||||||
log.console("Default: console pretty for console/error/system (stack traces on error)")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show sinks
|
||||||
|
log.console("Sinks:")
|
||||||
arrfor(names, function(n) {
|
arrfor(names, function(n) {
|
||||||
var s = config.sink[n]
|
var s = config.sink[n]
|
||||||
var ch = is_array(s.channels) ? text(s.channels, ', ') : '(none)'
|
|
||||||
var ex = is_array(s.exclude) ? " exclude=" + text(s.exclude, ',') : ""
|
|
||||||
var stk = is_array(s.stack) ? " stack=" + text(s.stack, ',') : ""
|
|
||||||
var fmt = s.format || (s.type == 'file' ? 'json' : 'pretty')
|
var fmt = s.format || (s.type == 'file' ? 'json' : 'pretty')
|
||||||
|
var mode = s.mode ? " mode=" + s.mode : ""
|
||||||
|
var maxsz = s.max_size ? " max_size=" + text(s.max_size) : ""
|
||||||
|
var ex = is_array(s.exclude) ? " exclude=" + text(s.exclude, ',') : ""
|
||||||
if (s.type == 'file')
|
if (s.type == 'file')
|
||||||
log.console(" " + n + ": " + s.type + " -> " + s.path + " [" + ch + "] format=" + fmt + ex + stk)
|
log.console(" " + n + ": file -> " + s.path + " format=" + fmt + mode + maxsz)
|
||||||
else
|
else
|
||||||
log.console(" " + n + ": " + s.type + " [" + ch + "] format=" + fmt + ex + stk)
|
log.console(" " + n + ": console format=" + fmt + ex)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Build channel -> sinks map
|
||||||
|
arrfor(names, function(n) {
|
||||||
|
var s = config.sink[n]
|
||||||
|
var chs = is_array(s.channels) ? s.channels : []
|
||||||
|
arrfor(chs, function(ch) {
|
||||||
|
if (!channel_routing[ch]) channel_routing[ch] = []
|
||||||
|
channel_routing[ch][] = n
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Show routing
|
||||||
|
log.console("")
|
||||||
|
log.console("Routing:")
|
||||||
|
var channels = array(channel_routing)
|
||||||
|
arrfor(channels, function(ch) {
|
||||||
|
log.console(" " + ch + " -> " + text(channel_routing[ch], ', '))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Show stack traces
|
||||||
|
stack_chs = collect_stack_channels(config)
|
||||||
|
var stack_list = array(stack_chs)
|
||||||
|
if (length(stack_list) > 0) {
|
||||||
|
log.console("")
|
||||||
|
log.console("Stack traces on: " + text(stack_list, ', '))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function do_add() {
|
function do_add() {
|
||||||
@@ -114,14 +159,15 @@ function do_add() {
|
|||||||
var sink_type = null
|
var sink_type = null
|
||||||
var path = null
|
var path = null
|
||||||
var format = null
|
var format = null
|
||||||
var channels = ["console", "error", "system"]
|
var channels = ["*"]
|
||||||
var exclude = null
|
var exclude = null
|
||||||
var stack_chs = ["error"]
|
var mode = null
|
||||||
|
var max_size = null
|
||||||
var config = null
|
var config = null
|
||||||
var val = null
|
var val = null
|
||||||
var i = 0
|
var i = 0
|
||||||
if (length(args) < 3) {
|
if (length(args) < 3) {
|
||||||
log.error("Usage: cell log add <name> console|file [path] [options]")
|
log.console("Usage: cell log add <name> console|file [path] [options]")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
name = args[1]
|
name = args[1]
|
||||||
@@ -129,7 +175,7 @@ function do_add() {
|
|||||||
|
|
||||||
if (sink_type == 'file') {
|
if (sink_type == 'file') {
|
||||||
if (length(args) < 4) {
|
if (length(args) < 4) {
|
||||||
log.error("Usage: cell log add <name> file <path> [options]")
|
log.console("Usage: cell log add <name> file <path> [options]")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
path = args[3]
|
path = args[3]
|
||||||
@@ -139,7 +185,7 @@ function do_add() {
|
|||||||
format = "pretty"
|
format = "pretty"
|
||||||
i = 3
|
i = 3
|
||||||
} else {
|
} else {
|
||||||
log.error("Unknown sink type: " + sink_type + " (use 'console' or 'file')")
|
log.console("Unknown sink type: " + sink_type + " (use 'console' or 'file')")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,17 +196,21 @@ function do_add() {
|
|||||||
if (val) { channels = array(val, ','); continue }
|
if (val) { channels = array(val, ','); continue }
|
||||||
val = parse_opt(args[i], 'exclude')
|
val = parse_opt(args[i], 'exclude')
|
||||||
if (val) { exclude = array(val, ','); continue }
|
if (val) { exclude = array(val, ','); continue }
|
||||||
val = parse_opt(args[i], 'stack')
|
val = parse_opt(args[i], 'mode')
|
||||||
if (val) { stack_chs = array(val, ','); continue }
|
if (val) { mode = val; continue }
|
||||||
|
val = parse_opt(args[i], 'max_size')
|
||||||
|
if (val) { max_size = number(val); continue }
|
||||||
}
|
}
|
||||||
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
if (!config) config = {}
|
if (!config) config = {}
|
||||||
if (!config.sink) config.sink = {}
|
if (!config.sink) config.sink = {}
|
||||||
|
|
||||||
config.sink[name] = {type: sink_type, format: format, channels: channels, stack: stack_chs}
|
config.sink[name] = {type: sink_type, format: format, channels: channels}
|
||||||
if (path) config.sink[name].path = path
|
if (path) config.sink[name].path = path
|
||||||
if (exclude) config.sink[name].exclude = exclude
|
if (exclude) config.sink[name].exclude = exclude
|
||||||
|
if (mode) config.sink[name].mode = mode
|
||||||
|
if (max_size) config.sink[name].max_size = max_size
|
||||||
|
|
||||||
save_config(config)
|
save_config(config)
|
||||||
log.console("Added sink: " + name)
|
log.console("Added sink: " + name)
|
||||||
@@ -170,13 +220,13 @@ function do_remove() {
|
|||||||
var name = null
|
var name = null
|
||||||
var config = null
|
var config = null
|
||||||
if (length(args) < 2) {
|
if (length(args) < 2) {
|
||||||
log.error("Usage: cell log remove <name>")
|
log.console("Usage: cell log remove <name>")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
name = args[1]
|
name = args[1]
|
||||||
config = load_config()
|
config = load_config()
|
||||||
if (!config || !config.sink || !config.sink[name]) {
|
if (!config || !config.sink || !config.sink[name]) {
|
||||||
log.error("Sink not found: " + name)
|
log.console("Sink not found: " + name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
delete config.sink[name]
|
delete config.sink[name]
|
||||||
@@ -184,154 +234,120 @@ function do_remove() {
|
|||||||
log.console("Removed sink: " + name)
|
log.console("Removed sink: " + name)
|
||||||
}
|
}
|
||||||
|
|
||||||
function do_read() {
|
function do_route() {
|
||||||
var name = null
|
var channel = null
|
||||||
var max_lines = 0
|
var sink_name = null
|
||||||
var filter_channel = null
|
|
||||||
var since = 0
|
|
||||||
var config = null
|
var config = null
|
||||||
var sink = null
|
var sink = null
|
||||||
var content = null
|
var already = false
|
||||||
var lines = null
|
if (length(args) < 3) {
|
||||||
var entries = []
|
log.console("Usage: cell log route <channel> <sink>")
|
||||||
var entry = null
|
|
||||||
var val = null
|
|
||||||
var i = 0
|
|
||||||
|
|
||||||
if (length(args) < 2) {
|
|
||||||
log.error("Usage: cell log read <sink_name> [options]")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
name = args[1]
|
channel = args[1]
|
||||||
|
sink_name = args[2]
|
||||||
for (i = 2; i < length(args); i++) {
|
|
||||||
val = parse_opt(args[i], 'lines')
|
|
||||||
if (val) { max_lines = number(val); continue }
|
|
||||||
val = parse_opt(args[i], 'channel')
|
|
||||||
if (val) { filter_channel = val; continue }
|
|
||||||
val = parse_opt(args[i], 'since')
|
|
||||||
if (val) { since = number(val); continue }
|
|
||||||
}
|
|
||||||
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
if (!config || !config.sink || !config.sink[name]) {
|
if (!config || !config.sink || !config.sink[sink_name]) {
|
||||||
log.error("Sink not found: " + name)
|
log.console("Sink not found: " + sink_name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sink = config.sink[name]
|
sink = config.sink[sink_name]
|
||||||
if (sink.type != 'file') {
|
if (!is_array(sink.channels)) sink.channels = []
|
||||||
log.error("Can only read from file sinks")
|
arrfor(sink.channels, function(ch) {
|
||||||
return
|
if (ch == channel) already = true
|
||||||
}
|
|
||||||
if (!fd.is_file(sink.path)) {
|
|
||||||
log.console("Log file does not exist yet: " + sink.path)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
content = text(fd.slurp(sink.path))
|
|
||||||
lines = array(content, '\n')
|
|
||||||
|
|
||||||
arrfor(lines, function(line) {
|
|
||||||
var parse_fn = null
|
|
||||||
if (length(line) == 0) return
|
|
||||||
parse_fn = function() {
|
|
||||||
entry = json.decode(line)
|
|
||||||
} disruption {
|
|
||||||
entry = null
|
|
||||||
}
|
|
||||||
parse_fn()
|
|
||||||
if (!entry) return
|
|
||||||
if (filter_channel && entry.channel != filter_channel) return
|
|
||||||
if (since > 0 && entry.timestamp < since) return
|
|
||||||
entries[] = entry
|
|
||||||
})
|
|
||||||
|
|
||||||
if (max_lines > 0 && length(entries) > max_lines)
|
|
||||||
entries = array(entries, length(entries) - max_lines, length(entries))
|
|
||||||
|
|
||||||
arrfor(entries, function(e) {
|
|
||||||
log.console(format_entry(e))
|
|
||||||
})
|
})
|
||||||
|
if (already) {
|
||||||
|
log.console(channel + " already routed to " + sink_name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sink.channels[] = channel
|
||||||
|
save_config(config)
|
||||||
|
log.console(channel + " -> " + sink_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
function do_tail() {
|
function do_unroute() {
|
||||||
var name = null
|
var channel = null
|
||||||
var tail_lines = 10
|
var sink_name = null
|
||||||
var config = null
|
var config = null
|
||||||
var sink = null
|
var sink = null
|
||||||
var last_size = 0
|
var found = false
|
||||||
var val = null
|
if (length(args) < 3) {
|
||||||
var i = 0
|
log.console("Usage: cell log unroute <channel> <sink>")
|
||||||
|
|
||||||
if (length(args) < 2) {
|
|
||||||
log.error("Usage: cell log tail <sink_name> [--lines=N]")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
name = args[1]
|
channel = args[1]
|
||||||
|
sink_name = args[2]
|
||||||
for (i = 2; i < length(args); i++) {
|
|
||||||
val = parse_opt(args[i], 'lines')
|
|
||||||
if (val) { tail_lines = number(val); continue }
|
|
||||||
}
|
|
||||||
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
if (!config || !config.sink || !config.sink[name]) {
|
if (!config || !config.sink || !config.sink[sink_name]) {
|
||||||
log.error("Sink not found: " + name)
|
log.console("Sink not found: " + sink_name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sink = config.sink[name]
|
sink = config.sink[sink_name]
|
||||||
if (sink.type != 'file') {
|
if (!is_array(sink.channels)) sink.channels = []
|
||||||
log.error("Can only tail file sinks")
|
sink.channels = filter(sink.channels, function(ch) { return ch != channel })
|
||||||
|
save_config(config)
|
||||||
|
log.console(channel + " removed from " + sink_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_stack() {
|
||||||
|
var channel = null
|
||||||
|
var config = null
|
||||||
|
var names = null
|
||||||
|
var added = false
|
||||||
|
if (length(args) < 2) {
|
||||||
|
log.console("Usage: cell log stack <channel>")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!fd.is_file(sink.path))
|
channel = args[1]
|
||||||
log.console("Waiting for log file: " + sink.path)
|
config = load_config()
|
||||||
|
if (!config || !config.sink) {
|
||||||
function poll() {
|
log.console("No sinks configured")
|
||||||
var st = null
|
|
||||||
var poll_content = null
|
|
||||||
var poll_lines = null
|
|
||||||
var start = 0
|
|
||||||
var poll_entry = null
|
|
||||||
var old_line_count = 0
|
|
||||||
var idx = 0
|
|
||||||
var parse_fn = null
|
|
||||||
if (!fd.is_file(sink.path)) {
|
|
||||||
$delay(poll, 1)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st = fd.stat(sink.path)
|
// Add to first sink that already has a stack array, or first sink overall
|
||||||
if (st.size == last_size) {
|
names = array(config.sink)
|
||||||
$delay(poll, 1)
|
arrfor(names, function(n) {
|
||||||
|
var s = config.sink[n]
|
||||||
|
var already = false
|
||||||
|
if (added) return
|
||||||
|
if (is_array(s.stack)) {
|
||||||
|
arrfor(s.stack, function(ch) { if (ch == channel) already = true })
|
||||||
|
if (!already) s.stack[] = channel
|
||||||
|
added = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!added && length(names) > 0) {
|
||||||
|
config.sink[names[0]].stack = [channel]
|
||||||
|
added = true
|
||||||
|
}
|
||||||
|
if (added) {
|
||||||
|
save_config(config)
|
||||||
|
log.console("Stack traces enabled on: " + channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_unstack() {
|
||||||
|
var channel = null
|
||||||
|
var config = null
|
||||||
|
var names = null
|
||||||
|
if (length(args) < 2) {
|
||||||
|
log.console("Usage: cell log unstack <channel>")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
channel = args[1]
|
||||||
poll_content = text(fd.slurp(sink.path))
|
config = load_config()
|
||||||
poll_lines = array(poll_content, '\n')
|
if (!config || !config.sink) {
|
||||||
|
log.console("No sinks configured")
|
||||||
if (last_size == 0 && length(poll_lines) > tail_lines) {
|
return
|
||||||
start = length(poll_lines) - tail_lines
|
|
||||||
} else if (last_size > 0) {
|
|
||||||
old_line_count = length(array(text(poll_content, 0, last_size), '\n'))
|
|
||||||
start = old_line_count
|
|
||||||
}
|
}
|
||||||
|
names = array(config.sink)
|
||||||
last_size = st.size
|
arrfor(names, function(n) {
|
||||||
for (idx = start; idx < length(poll_lines); idx++) {
|
var s = config.sink[n]
|
||||||
if (length(poll_lines[idx]) == 0) continue
|
if (is_array(s.stack))
|
||||||
parse_fn = function() {
|
s.stack = filter(s.stack, function(ch) { return ch != channel })
|
||||||
poll_entry = json.decode(poll_lines[idx])
|
})
|
||||||
} disruption {
|
save_config(config)
|
||||||
poll_entry = null
|
log.console("Stack traces disabled on: " + channel)
|
||||||
}
|
|
||||||
parse_fn()
|
|
||||||
if (!poll_entry) continue
|
|
||||||
os.print(format_entry(poll_entry) + "\n")
|
|
||||||
}
|
|
||||||
$delay(poll, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
poll()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var known_channels = ["console", "error", "warn", "system", "build", "shop", "compile", "test"]
|
var known_channels = ["console", "error", "warn", "system", "build", "shop", "compile", "test"]
|
||||||
@@ -473,12 +489,16 @@ if (length(args) == 0) {
|
|||||||
do_add()
|
do_add()
|
||||||
} else if (args[0] == 'remove') {
|
} else if (args[0] == 'remove') {
|
||||||
do_remove()
|
do_remove()
|
||||||
} else if (args[0] == 'read') {
|
} else if (args[0] == 'route') {
|
||||||
do_read()
|
do_route()
|
||||||
} else if (args[0] == 'tail') {
|
} else if (args[0] == 'unroute') {
|
||||||
do_tail()
|
do_unroute()
|
||||||
|
} else if (args[0] == 'stack') {
|
||||||
|
do_stack()
|
||||||
|
} else if (args[0] == 'unstack') {
|
||||||
|
do_unstack()
|
||||||
} else {
|
} else {
|
||||||
log.error("Unknown command: " + args[0])
|
log.console("Unknown command: " + args[0])
|
||||||
print_help()
|
print_help()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user