better logging for compiling

This commit is contained in:
2026-02-25 16:05:37 -06:00
parent d0bf757d91
commit adcaa92bea
7 changed files with 237 additions and 23 deletions

View File

@@ -464,7 +464,7 @@ Build.compile_file = function(pkg, file, target, opts) {
// Compile
log.shop('compiling ' + file)
log.console('Compiling ' + file)
log.build('Compiling ' + file)
err_path = '/tmp/cell_build_err_' + content_hash(setup.src_path) + '.log'
full_cmd = setup.cmd_str + ' -o "' + obj_path + '" 2>"' + err_path + '"'
ret = os.system(full_cmd)
@@ -603,6 +603,8 @@ Build.build_module_dylib = function(pkg, file, target, opts) {
var post_probe = null
var fallback_probe = null
var _fail_msg2 = null
var link_err_path = null
var link_err_text = null
if (probe && probe.fail) {
_fail_msg2 = probe.fail_path ? text(fd.slurp(probe.fail_path)) : null
@@ -697,10 +699,16 @@ Build.build_module_dylib = function(pkg, file, target, opts) {
cmd_str = text(cmd_parts, ' ')
if (_opts.verbose) log.build('[verbose] link: ' + cmd_str)
log.shop('linking ' + file)
log.console('Linking module ' + file + ' -> ' + fd.basename(dylib_path))
ret = os.system(cmd_str)
log.build('Linking module ' + file + ' -> ' + fd.basename(dylib_path))
link_err_path = '/tmp/cell_link_err_' + content_hash(file) + '.log'
ret = os.system(cmd_str + ' 2>"' + link_err_path + '"')
if (ret != 0) {
log.error('Linking failed: ' + file)
if (fd.is_file(link_err_path))
link_err_text = text(fd.slurp(link_err_path))
if (link_err_text)
log.error('Linking failed: ' + file + '\n' + link_err_text)
else
log.error('Linking failed: ' + file)
return null
}
@@ -745,6 +753,9 @@ Build.build_dynamic = function(pkg, target, buildtype, opts) {
var _opts = opts || {}
var c_files = pkg_tools.get_c_files(pkg, _target, true)
var results = []
var total = length(c_files)
var done = 0
var failed = 0
// Pre-fetch cflags once to avoid repeated TOML reads
var pkg_dir = shop.get_package_dir(pkg)
@@ -760,14 +771,24 @@ Build.build_dynamic = function(pkg, target, buildtype, opts) {
})
}
if (total > 0)
os.print(' Building C modules ')
arrfor(c_files, function(file) {
var sym_name = shop.c_symbol_for_file(pkg, file)
var dylib = Build.build_module_dylib(pkg, file, _target, {buildtype: _buildtype, extra_objects: support_objects, cflags: cached_cflags, verbose: _opts.verbose, force: _opts.force})
if (dylib) {
push(results, {file: file, symbol: sym_name, dylib: dylib})
} else {
failed = failed + 1
}
done = done + 1
os.print('.')
})
if (total > 0)
os.print(` ${text(done)}/${text(total)}${failed > 0 ? `, ${text(failed)} failed` : ''}\n`)
// Write manifest so runtime can find dylibs without the build module
var mpath = manifest_path(pkg)
fd.slurpwrite(mpath, stone(blob(json.encode(results))))

View File

@@ -9,13 +9,16 @@ Logging in ƿit is channel-based. Any `log.X(value)` call writes to channel `"X"
## Channels
Three channels are conventional:
These channels are conventional:
| Channel | Usage |
|---------|-------|
| `log.console(msg)` | General output |
| `log.error(msg)` | Errors and warnings |
| `log.error(msg)` | Errors |
| `log.warn(msg)` | Compiler diagnostics and warnings |
| `log.system(msg)` | Internal system messages |
| `log.build(msg)` | Per-file compile/link output |
| `log.shop(msg)` | Package management internals |
Any name works. `log.debug(msg)` creates channel `"debug"`, `log.perf(msg)` creates `"perf"`, and so on.
@@ -29,16 +32,18 @@ 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. The `error` channel includes a stack trace by default:
With no configuration, a default sink routes `console` and `error` to the terminal in clean format. The `error` channel includes a stack trace by default:
```
[a3f12] [console] server started on port 8080
[a3f12] [error] connection refused
server started on port 8080
error: connection refused
at handle_request (server.ce:42:3)
at main (main.ce:5:1)
```
The format is `[actor_id] [channel] message`. Error stack traces are always on unless you explicitly configure a sink without them.
Clean format prints messages without actor ID or channel prefix. Error messages are prefixed with `error:`. Other formats (`pretty`, `bare`) include actor IDs and are available for debugging. Stack traces are always on for errors unless you explicitly configure a sink without them.
Channels like `warn`, `build`, and `shop` are not routed to the terminal by default. Enable them with `pit log enable <channel>`.
## Configuration
@@ -67,7 +72,7 @@ exclude = ["console"]
| Field | Values | Description |
|-------|--------|-------------|
| `type` | `"console"`, `"file"` | Where output goes |
| `format` | `"pretty"`, `"bare"`, `"json"` | How output is formatted |
| `format` | `"clean"`, `"pretty"`, `"bare"`, `"json"` | How output is formatted |
| `channels` | array of names, or `["*"]` | Which channels this sink receives. Quote `'*'` on the CLI to prevent shell glob expansion. |
| `exclude` | array of names | Channels to skip (useful with `"*"`) |
| `stack` | array of channel names | Channels that capture a stack trace |
@@ -75,6 +80,13 @@ exclude = ["console"]
### Formats
**clean** — CLI-friendly. No actor ID or channel prefix. Error channel messages are prefixed with `error:`. This is the default format.
```
server started on port 8080
error: connection refused
```
**pretty** — human-readable, one line per message. Includes actor ID, channel, source location, and message.
```
@@ -158,7 +170,10 @@ The `pit log` command manages sinks and reads log files. See [CLI — pit log](/
```bash
pit log list # show sinks
pit log add terminal console --format=bare --channels=console
pit log channels # list channels with enabled/disabled status
pit log enable warn # enable a channel on the terminal sink
pit log disable warn # disable a channel
pit log add terminal console --format=clean --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
@@ -166,6 +181,16 @@ pit log read dump --lines=20 --channel=error
pit log tail dump
```
### Channel toggling
The `enable` and `disable` commands modify the terminal sink's channel list without touching other sink configuration. This is the easiest way to opt in to extra output:
```bash
pit log enable warn # see compiler warnings
pit log enable build # see per-file compile/link output
pit log disable warn # hide warnings again
```
## Examples
### Development setup

View File

@@ -259,7 +259,6 @@ if (_native) {
compile_native_cached(_te.name, core_path + '/' + _te.path)
_ti = _ti + 1
}
os.print("bootstrap: native cache seeded\n")
} else {
// Bytecode path: seed cache with everything engine needs
_targets = [
@@ -276,5 +275,4 @@ if (_native) {
compile_and_cache(_te.name, core_path + '/' + _te.path)
_ti = _ti + 1
}
os.print("bootstrap: cache seeded\n")
}

View File

@@ -285,7 +285,7 @@ function analyze(src, filename) {
_i = 0
while (_i < length(folded._diagnostics)) {
e = folded._diagnostics[_i]
os.print(`${filename}:${text(e.line)}:${text(e.col)}: ${e.severity}: ${e.message}\n`)
log.warn(`${filename}:${text(e.line)}:${text(e.col)}: ${e.severity}: ${e.message}`)
_i = _i + 1
}
if (_wm) {
@@ -443,8 +443,12 @@ function run_ast_fn(name, ast, env, pkg) {
_has_errors = false
while (_di < length(optimized._diagnostics)) {
_diag = optimized._diagnostics[_di]
os.print(`${_diag.file}:${text(_diag.line)}:${text(_diag.col)}: ${_diag.severity}: ${_diag.message}\n`)
if (_diag.severity == "error") _has_errors = true
if (_diag.severity == "error") {
log.error(`${_diag.file}:${text(_diag.line)}:${text(_diag.col)}: ${_diag.severity}: ${_diag.message}`)
_has_errors = true
} else {
log.warn(`${_diag.file}:${text(_diag.line)}:${text(_diag.col)}: ${_diag.severity}: ${_diag.message}`)
}
_di = _di + 1
}
if (_has_errors) disrupt
@@ -506,8 +510,12 @@ function compile_user_blob(name, ast, pkg) {
_has_errors = false
while (_di < length(optimized._diagnostics)) {
_diag = optimized._diagnostics[_di]
os.print(`${_diag.file}:${text(_diag.line)}:${text(_diag.col)}: ${_diag.severity}: ${_diag.message}\n`)
if (_diag.severity == "error") _has_errors = true
if (_diag.severity == "error") {
log.error(`${_diag.file}:${text(_diag.line)}:${text(_diag.col)}: ${_diag.severity}: ${_diag.message}`)
_has_errors = true
} else {
log.warn(`${_diag.file}:${text(_diag.line)}:${text(_diag.col)}: ${_diag.severity}: ${_diag.message}`)
}
_di = _di + 1
}
if (_has_errors) disrupt
@@ -920,9 +928,8 @@ function load_log_config() {
sink: {
terminal: {
type: "console",
format: "pretty",
channels: ["*"],
exclude: ["system", "shop", "build"],
format: "clean",
channels: ["console", "error"],
stack: ["error"]
}
}
@@ -964,6 +971,25 @@ function bare_format(rec) {
return out
}
function clean_format(rec) {
var ev = is_text(rec.event) ? rec.event : json.encode(rec.event, false)
var out = null
var i = 0
var fr = null
if (rec.channel == "error") {
out = `error: ${ev}\n`
} else {
out = `${ev}\n`
}
if (rec.stack && length(rec.stack) > 0) {
for (i = 0; i < length(rec.stack); i = i + 1) {
fr = rec.stack[i]
out = out + ` at ${fr.fn} (${fr.file}:${text(fr.line)}:${text(fr.col)})\n`
}
}
return out
}
function sink_excluded(sink, channel) {
var excluded = false
if (!sink.exclude || length(sink.exclude) == 0) return false
@@ -981,6 +1007,8 @@ function dispatch_to_sink(sink, rec) {
os.print(json.encode(rec, false) + "\n")
else if (sink.format == "bare")
os.print(bare_format(rec))
else if (sink.format == "clean")
os.print(clean_format(rec))
else
os.print(pretty_format(rec))
} else if (sink.type == "file") {

View File

@@ -1882,6 +1882,7 @@ Shop.sync_with_deps = function(pkg, opts) {
if (visited[current]) continue
visited[current] = true
log.console(' Fetching ' + current + '...')
Shop.sync(current, opts)
_read_deps = function() {
@@ -2072,6 +2073,12 @@ Shop.build_package_scripts = function(package)
_try()
})
if (length(errors) > 0) {
log.console(' Compiling scripts (' + text(ok) + ' ok, ' + text(length(errors)) + ' errors)')
} else if (ok > 0) {
log.console(' Compiling scripts (' + text(ok) + ' ok)')
}
return {ok: ok, errors: errors, total: length(scripts)}
}

134
log.ce
View File

@@ -2,6 +2,9 @@
//
// Usage:
// cell log list List configured sinks
// cell log channels List channels with status
// cell log enable <channel> Enable 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> file <path> [opts] Add a file sink
// cell log remove <name> Remove a sink
@@ -46,6 +49,9 @@ function print_help() {
log.console("")
log.console("Commands:")
log.console(" list List configured sinks")
log.console(" channels List channels with status")
log.console(" enable <channel> Enable 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> file <path> [opts] Add a file sink")
log.console(" remove <name> Remove a sink")
@@ -328,6 +334,128 @@ function do_tail() {
poll()
}
var known_channels = ["console", "error", "warn", "system", "build", "shop", "compile", "test"]
function find_terminal_sink(config) {
var names = null
var found = null
if (!config || !config.sink) return null
names = array(config.sink)
if (config.sink.terminal) return config.sink.terminal
arrfor(names, function(n) {
if (!found && config.sink[n].type == "console")
found = config.sink[n]
})
return found
}
function do_enable() {
var channel = null
var config = null
var sink = null
var i = 0
var already = false
if (length(args) < 2) {
log.error("Usage: cell log enable <channel>")
return
}
channel = args[1]
config = load_config()
if (!config) config = {sink: {}}
if (!config.sink) config.sink = {}
sink = find_terminal_sink(config)
if (!sink) {
config.sink.terminal = {type: "console", format: "clean", channels: ["console", "error", channel], stack: ["error"]}
save_config(config)
log.console("Enabled channel: " + channel)
return
}
if (is_array(sink.channels) && length(sink.channels) == 1 && sink.channels[0] == "*") {
if (is_array(sink.exclude)) {
var new_exclude = []
arrfor(sink.exclude, function(ex) {
if (ex != channel) push(new_exclude, ex)
})
sink.exclude = new_exclude
}
} else {
if (!is_array(sink.channels)) sink.channels = ["console", "error"]
arrfor(sink.channels, function(ch) {
if (ch == channel) already = true
})
if (!already) sink.channels[] = channel
}
save_config(config)
log.console("Enabled channel: " + channel)
}
function do_disable() {
var channel = null
var config = null
var sink = null
var i = 0
var new_channels = []
if (length(args) < 2) {
log.error("Usage: cell log disable <channel>")
return
}
channel = args[1]
config = load_config()
if (!config || !config.sink) {
log.error("No log configuration found")
return
}
sink = find_terminal_sink(config)
if (!sink) {
log.error("No terminal sink found")
return
}
if (is_array(sink.channels) && length(sink.channels) == 1 && sink.channels[0] == "*") {
if (!is_array(sink.exclude)) sink.exclude = []
var already_excluded = false
arrfor(sink.exclude, function(ex) {
if (ex == channel) already_excluded = true
})
if (!already_excluded) sink.exclude[] = channel
} else {
if (is_array(sink.channels)) {
arrfor(sink.channels, function(ch) {
if (ch != channel) push(new_channels, ch)
})
sink.channels = new_channels
}
}
save_config(config)
log.console("Disabled channel: " + channel)
}
function do_channels() {
var config = load_config()
var sink = null
var is_wildcard = false
var active = {}
if (config) sink = find_terminal_sink(config)
if (sink) {
if (is_array(sink.channels) && length(sink.channels) == 1 && sink.channels[0] == "*") {
is_wildcard = true
arrfor(known_channels, function(ch) { active[ch] = true })
if (is_array(sink.exclude)) {
arrfor(sink.exclude, function(ex) { active[ex] = false })
}
} else if (is_array(sink.channels)) {
arrfor(sink.channels, function(ch) { active[ch] = true })
}
} else {
active.console = true
active.error = true
}
log.console("Channels:")
arrfor(known_channels, function(ch) {
var status = active[ch] ? "enabled" : "disabled"
log.console(" " + ch + ": " + status)
})
}
// Main dispatch
if (length(args) == 0) {
print_help()
@@ -335,6 +463,12 @@ if (length(args) == 0) {
print_help()
} else if (args[0] == 'list') {
do_list()
} else if (args[0] == 'channels') {
do_channels()
} else if (args[0] == 'enable') {
do_enable()
} else if (args[0] == 'disable') {
do_disable()
} else if (args[0] == 'add') {
do_add()
} else if (args[0] == 'remove') {

View File

@@ -3277,7 +3277,8 @@ JS_RaiseDisrupt (JSContext *ctx, const char *fmt, ...) {
va_start (ap, fmt);
vsnprintf (buf, sizeof (buf), fmt, ap);
va_end (ap);
JS_Log (ctx, "error", "%s", buf);
if (ctx->log_callback)
JS_Log (ctx, "error", "%s", buf);
ctx->current_exception = JS_TRUE;
return JS_EXCEPTION;
}