Merge branch 'improve_compile_error' into audit_dups
This commit is contained in:
@@ -317,11 +317,13 @@ Options:
|
||||
- `--format=pretty|bare|json` — output format (default: `pretty` for console, `json` for file)
|
||||
- `--channels=ch1,ch2` — channels to subscribe (default: `console,error,system`). Use `'*'` for all channels (quote to prevent shell glob expansion).
|
||||
- `--exclude=ch1,ch2` — channels to exclude (useful with `'*'`)
|
||||
- `--stack=ch1,ch2` — channels that capture a full stack trace (default: `error`)
|
||||
|
||||
```bash
|
||||
pit log add terminal console --format=bare --channels=console
|
||||
pit log add errors file .cell/logs/errors.jsonl --channels=error
|
||||
pit log add dump file .cell/logs/dump.jsonl '--channels=*' --exclude=console
|
||||
pit log add debug console --channels=error,debug --stack=error,debug
|
||||
```
|
||||
|
||||
### pit log remove
|
||||
|
||||
@@ -29,14 +29,16 @@ Non-text values are JSON-encoded automatically.
|
||||
|
||||
## Default Behavior
|
||||
|
||||
With no configuration, a default sink routes `console`, `error`, and `system` to the terminal in pretty format:
|
||||
With no configuration, a default sink routes `console`, `error`, and `system` to the terminal in pretty format. The `error` channel includes a stack trace by default:
|
||||
|
||||
```
|
||||
[a3f12] [console] main.ce:5 server started on port 8080
|
||||
[a3f12] [error] main.ce:12 connection refused
|
||||
[a3f12] [console] server started on port 8080
|
||||
[a3f12] [error] connection refused
|
||||
at handle_request (server.ce:42:3)
|
||||
at main (main.ce:5:1)
|
||||
```
|
||||
|
||||
The format is `[actor_id] [channel] file:line message`.
|
||||
The format is `[actor_id] [channel] message`. Error stack traces are always on unless you explicitly configure a sink without them.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -114,14 +116,22 @@ File sinks write one JSON-encoded record per line. Console sinks format the reco
|
||||
|
||||
## Stack Traces
|
||||
|
||||
Add a `stack` field to a sink to capture a full call stack for specific channels. The value is an array of channel names.
|
||||
The `error` channel captures stack traces by default. To enable stack traces for other channels, add a `stack` field to a sink — an array of channel names that should include a call stack.
|
||||
|
||||
Via the CLI:
|
||||
|
||||
```bash
|
||||
pit log add terminal console --channels=console,error,debug --stack=error,debug
|
||||
```
|
||||
|
||||
Or in `log.toml`:
|
||||
|
||||
```toml
|
||||
[sink.terminal]
|
||||
type = "console"
|
||||
format = "bare"
|
||||
channels = ["console", "error"]
|
||||
stack = ["error"]
|
||||
channels = ["console", "error", "debug"]
|
||||
stack = ["error", "debug"]
|
||||
```
|
||||
|
||||
Only channels listed in `stack` get stack traces. Other channels on the same sink print without one:
|
||||
@@ -150,6 +160,7 @@ The `pit log` command manages sinks and reads log files. See [CLI — pit log](/
|
||||
pit log list # show sinks
|
||||
pit log add terminal console --format=bare --channels=console
|
||||
pit log add dump file .cell/logs/dump.jsonl '--channels=*' --exclude=console
|
||||
pit log add debug console --channels=error,debug --stack=error,debug
|
||||
pit log remove terminal
|
||||
pit log read dump --lines=20 --channel=error
|
||||
pit log tail dump
|
||||
|
||||
@@ -506,20 +506,37 @@ var REPLYTIMEOUT = 60 // seconds before replies are ignored
|
||||
// --- 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).
|
||||
// The bootstrap log forwards to _log_full once the full system is ready, so that
|
||||
// modules loaded early (like shop.cm) get full logging even though they captured
|
||||
// the bootstrap function reference.
|
||||
|
||||
var log_config = null
|
||||
var channel_sinks = {}
|
||||
var wildcard_sinks = []
|
||||
var warned_channels = {}
|
||||
var stack_channels = {}
|
||||
var _log_full = null
|
||||
|
||||
var log_quiet_channels = { shop: true }
|
||||
|
||||
function log(name, args) {
|
||||
if (_log_full) return _log_full(name, args)
|
||||
if (log_quiet_channels[name]) return
|
||||
var msg = args[0]
|
||||
var stk = null
|
||||
var i = 0
|
||||
var fr = null
|
||||
if (msg == null) msg = ""
|
||||
os.print(`[${text(_cell.id, 0, 5)}] [${name}]: ${msg}\n`)
|
||||
if (name == "error") {
|
||||
stk = os.stack(2)
|
||||
if (stk && length(stk) > 0) {
|
||||
for (i = 0; i < length(stk); i = i + 1) {
|
||||
fr = stk[i]
|
||||
os.print(` at ${fr.fn} (${fr.file}:${text(fr.line)}:${text(fr.col)})\n`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function actor_die(err)
|
||||
@@ -648,6 +665,7 @@ function build_sink_routing() {
|
||||
var names = array(log_config.sink)
|
||||
arrfor(names, function(name) {
|
||||
var sink = log_config.sink[name]
|
||||
if (!sink || !is_object(sink)) return
|
||||
sink._name = name
|
||||
if (!is_array(sink.channels)) sink.channels = []
|
||||
if (is_text(sink.exclude)) sink.exclude = [sink.exclude]
|
||||
@@ -677,7 +695,7 @@ function load_log_config() {
|
||||
log_config = toml.decode(text(fd.slurp(log_path)))
|
||||
}
|
||||
}
|
||||
if (!log_config || !log_config.sink) {
|
||||
if (!log_config || !log_config.sink || length(array(log_config.sink)) == 0) {
|
||||
log_config = {
|
||||
sink: {
|
||||
terminal: {
|
||||
@@ -787,6 +805,10 @@ log = function(name, args) {
|
||||
// Wire C-level JS_Log through the ƿit log system
|
||||
actor_mod.set_log(log)
|
||||
|
||||
// Let the bootstrap log forward to the full system — modules loaded early
|
||||
// (before the full log was ready) captured the bootstrap function reference.
|
||||
_log_full = log
|
||||
|
||||
var pronto = use_core('pronto')
|
||||
var fallback = pronto.fallback
|
||||
var parallel = pronto.parallel
|
||||
|
||||
32
log.ce
32
log.ce
@@ -1,12 +1,15 @@
|
||||
// cell log - Manage and read log sinks
|
||||
//
|
||||
// Usage:
|
||||
// cell log list List configured sinks
|
||||
// cell log add <name> console [opts] Add a console sink
|
||||
// cell log list List configured sinks
|
||||
// cell log add <name> console [opts] Add a console sink
|
||||
// cell log add <name> file <path> [opts] Add a file sink
|
||||
// cell log remove <name> Remove a sink
|
||||
// cell log read <sink> [opts] Read from a file sink
|
||||
// cell log tail <sink> [--lines=N] Follow a file sink
|
||||
// cell log remove <name> Remove a sink
|
||||
// cell log read <sink> [opts] Read from a file sink
|
||||
// cell log tail <sink> [--lines=N] Follow a file sink
|
||||
//
|
||||
// The --stack option controls which channels capture a stack trace.
|
||||
// Default: --stack=error (errors always show a stack trace).
|
||||
|
||||
var toml = use('toml')
|
||||
var fd = use('fd')
|
||||
@@ -53,6 +56,7 @@ function print_help() {
|
||||
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(" --exclude=ch1,ch2 Channels to exclude (for wildcard sinks)")
|
||||
log.console(" --stack=ch1,ch2 Channels that capture a stack trace (default: error)")
|
||||
log.console("")
|
||||
log.console("Options for read:")
|
||||
log.console(" --lines=N Show last N lines (default: all)")
|
||||
@@ -80,21 +84,22 @@ function format_entry(entry) {
|
||||
function do_list() {
|
||||
var config = load_config()
|
||||
var names = null
|
||||
if (!config || !config.sink) {
|
||||
names = (config && config.sink) ? array(config.sink) : []
|
||||
if (length(names) == 0) {
|
||||
log.console("No log sinks configured.")
|
||||
log.console("Default: console pretty for console/error/system")
|
||||
log.console("Default: console pretty for console/error/system (stack traces on error)")
|
||||
return
|
||||
}
|
||||
names = array(config.sink)
|
||||
arrfor(names, function(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')
|
||||
if (s.type == 'file')
|
||||
log.console(" " + n + ": " + s.type + " -> " + s.path + " [" + ch + "] format=" + fmt + ex)
|
||||
log.console(" " + n + ": " + s.type + " -> " + s.path + " [" + ch + "] format=" + fmt + ex + stk)
|
||||
else
|
||||
log.console(" " + n + ": " + s.type + " [" + ch + "] format=" + fmt + ex)
|
||||
log.console(" " + n + ": " + s.type + " [" + ch + "] format=" + fmt + ex + stk)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -105,6 +110,7 @@ function do_add() {
|
||||
var format = null
|
||||
var channels = ["console", "error", "system"]
|
||||
var exclude = null
|
||||
var stack_chs = ["error"]
|
||||
var config = null
|
||||
var val = null
|
||||
var i = 0
|
||||
@@ -138,13 +144,15 @@ function do_add() {
|
||||
if (val) { channels = array(val, ','); continue }
|
||||
val = parse_opt(args[i], 'exclude')
|
||||
if (val) { exclude = array(val, ','); continue }
|
||||
val = parse_opt(args[i], 'stack')
|
||||
if (val) { stack_chs = array(val, ','); continue }
|
||||
}
|
||||
|
||||
config = load_config()
|
||||
if (!config) config = {}
|
||||
if (!config.sink) config.sink = {}
|
||||
|
||||
config.sink[name] = {type: sink_type, format: format, channels: channels}
|
||||
config.sink[name] = {type: sink_type, format: format, channels: channels, stack: stack_chs}
|
||||
if (path) config.sink[name].path = path
|
||||
if (exclude) config.sink[name].exclude = exclude
|
||||
|
||||
@@ -165,7 +173,7 @@ function do_remove() {
|
||||
log.error("Sink not found: " + name)
|
||||
return
|
||||
}
|
||||
config.sink[name] = null
|
||||
delete config.sink[name]
|
||||
save_config(config)
|
||||
log.console("Removed sink: " + name)
|
||||
}
|
||||
|
||||
100
remove.ce
100
remove.ce
@@ -43,64 +43,64 @@ var run = function() {
|
||||
return
|
||||
}
|
||||
|
||||
target_pkg = shop.resolve_locator(target_pkg)
|
||||
target_pkg = shop.resolve_locator(target_pkg)
|
||||
|
||||
var packages_to_remove = [target_pkg]
|
||||
var packages_to_remove = [target_pkg]
|
||||
|
||||
var lock = null
|
||||
var all_packages = null
|
||||
var needed = null
|
||||
if (prune) {
|
||||
// Find packages no longer needed
|
||||
// Get all dependencies of remaining packages
|
||||
lock = shop.load_lock()
|
||||
all_packages = shop.list_packages()
|
||||
var lock = null
|
||||
var all_packages = null
|
||||
var needed = null
|
||||
if (prune) {
|
||||
// Find packages no longer needed
|
||||
// Get all dependencies of remaining packages
|
||||
lock = shop.load_lock()
|
||||
all_packages = shop.list_packages()
|
||||
|
||||
// Build set of all needed packages (excluding target)
|
||||
needed = {}
|
||||
arrfor(all_packages, function(p) {
|
||||
if (p == target_pkg || p == 'core') return
|
||||
// Build set of all needed packages (excluding target)
|
||||
needed = {}
|
||||
arrfor(all_packages, function(p) {
|
||||
if (p == target_pkg || p == 'core') return
|
||||
|
||||
// Mark this package and its deps as needed
|
||||
needed[p] = true
|
||||
var _gather = function() {
|
||||
var deps = pkg.gather_dependencies(p)
|
||||
arrfor(deps, function(dep) {
|
||||
needed[dep] = true
|
||||
})
|
||||
} disruption {
|
||||
// Skip if can't read deps
|
||||
}
|
||||
_gather()
|
||||
})
|
||||
// Mark this package and its deps as needed
|
||||
needed[p] = true
|
||||
var _gather = function() {
|
||||
var deps = pkg.gather_dependencies(p)
|
||||
arrfor(deps, function(dep) {
|
||||
needed[dep] = true
|
||||
})
|
||||
} disruption {
|
||||
// Skip if can't read deps
|
||||
}
|
||||
_gather()
|
||||
})
|
||||
|
||||
// Find packages that are NOT needed
|
||||
arrfor(all_packages, function(p) {
|
||||
if (p == 'core') return
|
||||
if (!needed[p] && find(packages_to_remove, p) == null) {
|
||||
push(packages_to_remove, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
// Find packages that are NOT needed
|
||||
arrfor(all_packages, function(p) {
|
||||
if (p == 'core') return
|
||||
if (!needed[p] && find(packages_to_remove, p) == null) {
|
||||
push(packages_to_remove, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (dry_run) {
|
||||
log.console("Would remove:")
|
||||
arrfor(packages_to_remove, function(p) {
|
||||
log.console(" " + p)
|
||||
})
|
||||
} else {
|
||||
arrfor(packages_to_remove, function(p) {
|
||||
// Remove any link for this package
|
||||
if (link.is_linked(p)) {
|
||||
link.remove(p)
|
||||
}
|
||||
if (dry_run) {
|
||||
log.console("Would remove:")
|
||||
arrfor(packages_to_remove, function(p) {
|
||||
log.console(" " + p)
|
||||
})
|
||||
} else {
|
||||
arrfor(packages_to_remove, function(p) {
|
||||
// Remove any link for this package
|
||||
if (link.is_linked(p)) {
|
||||
link.remove(p)
|
||||
}
|
||||
|
||||
// Remove from shop
|
||||
shop.remove(p)
|
||||
})
|
||||
// Remove from shop
|
||||
shop.remove(p)
|
||||
})
|
||||
|
||||
log.console("Removed " + text(length(packages_to_remove)) + " package(s).")
|
||||
}
|
||||
log.console("Removed " + text(length(packages_to_remove)) + " package(s).")
|
||||
}
|
||||
}
|
||||
run()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user