Merge branch 'improve_compile_error' into audit_dups

This commit is contained in:
2026-02-20 15:07:51 -06:00
5 changed files with 113 additions and 70 deletions

View File

@@ -317,11 +317,13 @@ Options:
- `--format=pretty|bare|json` — output format (default: `pretty` for console, `json` for file) - `--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). - `--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 `'*'`) - `--exclude=ch1,ch2` — channels to exclude (useful with `'*'`)
- `--stack=ch1,ch2` — channels that capture a full stack trace (default: `error`)
```bash ```bash
pit log add terminal console --format=bare --channels=console pit log add terminal console --format=bare --channels=console
pit log add errors file .cell/logs/errors.jsonl --channels=error 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 dump file .cell/logs/dump.jsonl '--channels=*' --exclude=console
pit log add debug console --channels=error,debug --stack=error,debug
``` ```
### pit log remove ### pit log remove

View File

@@ -29,14 +29,16 @@ Non-text values are JSON-encoded automatically.
## Default Behavior ## 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] [console] server started on port 8080
[a3f12] [error] main.ce:12 connection refused [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 ## Configuration
@@ -114,14 +116,22 @@ File sinks write one JSON-encoded record per line. Console sinks format the reco
## Stack Traces ## 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 ```toml
[sink.terminal] [sink.terminal]
type = "console" type = "console"
format = "bare" format = "bare"
channels = ["console", "error"] channels = ["console", "error", "debug"]
stack = ["error"] stack = ["error", "debug"]
``` ```
Only channels listed in `stack` get stack traces. Other channels on the same sink print without one: 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 list # show sinks
pit log add terminal console --format=bare --channels=console pit log add terminal console --format=bare --channels=console
pit log add dump file .cell/logs/dump.jsonl '--channels=*' --exclude=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 remove terminal
pit log read dump --lines=20 --channel=error pit log read dump --lines=20 --channel=error
pit log tail dump pit log tail dump

View File

@@ -506,20 +506,37 @@ var REPLYTIMEOUT = 60 // seconds before replies are ignored
// --- Logging system (bootstrap phase) --- // --- Logging system (bootstrap phase) ---
// Early log: prints to console before toml/time/json are loaded. // 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). // 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 log_config = null
var channel_sinks = {} var channel_sinks = {}
var wildcard_sinks = [] var wildcard_sinks = []
var warned_channels = {} var warned_channels = {}
var stack_channels = {} var stack_channels = {}
var _log_full = null
var log_quiet_channels = { shop: true } var log_quiet_channels = { shop: true }
function log(name, args) { function log(name, args) {
if (_log_full) return _log_full(name, args)
if (log_quiet_channels[name]) return if (log_quiet_channels[name]) return
var msg = args[0] var msg = args[0]
var stk = null
var i = 0
var fr = null
if (msg == null) msg = "" if (msg == null) msg = ""
os.print(`[${text(_cell.id, 0, 5)}] [${name}]: ${msg}\n`) 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) function actor_die(err)
@@ -648,6 +665,7 @@ function build_sink_routing() {
var names = array(log_config.sink) var names = array(log_config.sink)
arrfor(names, function(name) { arrfor(names, function(name) {
var sink = log_config.sink[name] var sink = log_config.sink[name]
if (!sink || !is_object(sink)) return
sink._name = name sink._name = name
if (!is_array(sink.channels)) sink.channels = [] if (!is_array(sink.channels)) sink.channels = []
if (is_text(sink.exclude)) sink.exclude = [sink.exclude] 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))) 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 = { log_config = {
sink: { sink: {
terminal: { terminal: {
@@ -787,6 +805,10 @@ log = function(name, args) {
// Wire C-level JS_Log through the ƿit log system // Wire C-level JS_Log through the ƿit log system
actor_mod.set_log(log) 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 pronto = use_core('pronto')
var fallback = pronto.fallback var fallback = pronto.fallback
var parallel = pronto.parallel var parallel = pronto.parallel

32
log.ce
View File

@@ -1,12 +1,15 @@
// cell log - Manage and read log sinks // cell log - Manage and read log sinks
// //
// Usage: // Usage:
// cell log list List configured sinks // cell log list List configured sinks
// 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 read <sink> [opts] Read from a file sink
// cell log tail <sink> [--lines=N] Follow 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 toml = use('toml')
var fd = use('fd') 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(" --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: console,error,system)")
log.console(" --exclude=ch1,ch2 Channels to exclude (for wildcard sinks)") 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("")
log.console("Options for read:") log.console("Options for read:")
log.console(" --lines=N Show last N lines (default: all)") log.console(" --lines=N Show last N lines (default: all)")
@@ -80,21 +84,22 @@ function format_entry(entry) {
function do_list() { function do_list() {
var config = load_config() var config = load_config()
var names = null 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("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 return
} }
names = array(config.sink)
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 ch = is_array(s.channels) ? text(s.channels, ', ') : '(none)'
var ex = is_array(s.exclude) ? " exclude=" + text(s.exclude, ',') : "" 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')
if (s.type == 'file') 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 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 format = null
var channels = ["console", "error", "system"] var channels = ["console", "error", "system"]
var exclude = null var exclude = null
var stack_chs = ["error"]
var config = null var config = null
var val = null var val = null
var i = 0 var i = 0
@@ -138,13 +144,15 @@ 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')
if (val) { stack_chs = array(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} config.sink[name] = {type: sink_type, format: format, channels: channels, stack: stack_chs}
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
@@ -165,7 +173,7 @@ function do_remove() {
log.error("Sink not found: " + name) log.error("Sink not found: " + name)
return return
} }
config.sink[name] = null delete config.sink[name]
save_config(config) save_config(config)
log.console("Removed sink: " + name) log.console("Removed sink: " + name)
} }

100
remove.ce
View File

@@ -43,64 +43,64 @@ var run = function() {
return 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 lock = null
var all_packages = null var all_packages = null
var needed = null var needed = null
if (prune) { if (prune) {
// Find packages no longer needed // Find packages no longer needed
// Get all dependencies of remaining packages // Get all dependencies of remaining packages
lock = shop.load_lock() lock = shop.load_lock()
all_packages = shop.list_packages() all_packages = shop.list_packages()
// Build set of all needed packages (excluding target) // Build set of all needed packages (excluding target)
needed = {} needed = {}
arrfor(all_packages, function(p) { arrfor(all_packages, function(p) {
if (p == target_pkg || p == 'core') return if (p == target_pkg || p == 'core') return
// Mark this package and its deps as needed // Mark this package and its deps as needed
needed[p] = true needed[p] = true
var _gather = function() { var _gather = function() {
var deps = pkg.gather_dependencies(p) var deps = pkg.gather_dependencies(p)
arrfor(deps, function(dep) { arrfor(deps, function(dep) {
needed[dep] = true needed[dep] = true
}) })
} disruption { } disruption {
// Skip if can't read deps // Skip if can't read deps
} }
_gather() _gather()
}) })
// Find packages that are NOT needed // Find packages that are NOT needed
arrfor(all_packages, function(p) { arrfor(all_packages, function(p) {
if (p == 'core') return if (p == 'core') return
if (!needed[p] && find(packages_to_remove, p) == null) { if (!needed[p] && find(packages_to_remove, p) == null) {
push(packages_to_remove, p) push(packages_to_remove, p)
} }
}) })
} }
if (dry_run) { if (dry_run) {
log.console("Would remove:") log.console("Would remove:")
arrfor(packages_to_remove, function(p) { arrfor(packages_to_remove, function(p) {
log.console(" " + p) log.console(" " + p)
}) })
} else { } else {
arrfor(packages_to_remove, function(p) { arrfor(packages_to_remove, function(p) {
// Remove any link for this package // Remove any link for this package
if (link.is_linked(p)) { if (link.is_linked(p)) {
link.remove(p) link.remove(p)
} }
// Remove from shop // Remove from shop
shop.remove(p) shop.remove(p)
}) })
log.console("Removed " + text(length(packages_to_remove)) + " package(s).") log.console("Removed " + text(length(packages_to_remove)) + " package(s).")
} }
} }
run() run()