stack trace in logging toml

This commit is contained in:
2026-02-18 12:22:33 -06:00
parent 037fdbfd2c
commit a1d1e721b6
2 changed files with 29 additions and 13 deletions

View File

@@ -68,6 +68,7 @@ exclude = ["console"]
| `format` | `"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 |
| `path` | file path | Output file (file sinks only) |
### Formats
@@ -113,33 +114,33 @@ File sinks write one JSON-encoded record per line. Console sinks format the reco
## Stack Traces
Set `stack = true` at the top level of `.cell/log.toml` to capture a full call stack with every log record.
Add a `stack` field to a sink to capture a full call stack for specific channels. The value is an array of channel names.
```toml
stack = true
[sink.terminal]
type = "console"
format = "pretty"
format = "bare"
channels = ["console", "error"]
stack = ["error"]
```
With pretty format, the stack is printed indented below each message:
Only channels listed in `stack` get stack traces. Other channels on the same sink print without one:
```
[a3f12] [error] server.ce:42 connection failed
[a3f12] server started
[a3f12] connection failed
at handle_request (server.ce:42:3)
at process (router.ce:18:5)
at main (main.ce:5:1)
```
With JSON format, a `stack` array is added to the record:
With JSON format, a `stack` array is added to the record for channels that have stack capture enabled:
```json
{"actor_id":"a3f12...","channel":"error","event":"connection failed","source":{"file":"server.ce","line":42,"col":3,"fn":"handle_request"},"stack":[{"fn":"handle_request","file":"server.ce","line":42,"col":3},{"fn":"process","file":"router.ce","line":18,"col":5},{"fn":"main","file":"main.ce","line":5,"col":1}]}
```
When `stack` is not set or `false`, no stack is captured and the `stack` field is omitted from records. Capturing stacks adds overhead — enable it for debugging, not production.
Channels without `stack` configuration produce no stack field. Capturing stacks adds overhead — enable it for debugging, not production.
## CLI

View File

@@ -324,7 +324,7 @@ var log_config = null
var channel_sinks = {}
var wildcard_sinks = []
var warned_channels = {}
var log_stack_enabled = false
var stack_channels = {}
function log(name, args) {
var msg = args[0]
@@ -438,6 +438,7 @@ function ensure_log_dir(path) {
function build_sink_routing() {
channel_sinks = {}
wildcard_sinks = []
stack_channels = {}
var names = array(log_config.sink)
arrfor(names, function(name) {
var sink = log_config.sink[name]
@@ -445,7 +446,12 @@ function build_sink_routing() {
if (!is_array(sink.channels)) sink.channels = []
if (is_text(sink.exclude)) sink.exclude = [sink.exclude]
if (!is_array(sink.exclude)) sink.exclude = []
if (is_text(sink.stack)) sink.stack = [sink.stack]
if (!is_array(sink.stack)) sink.stack = []
if (sink.type == "file" && sink.path) ensure_log_dir(sink.path)
arrfor(sink.stack, function(ch) {
stack_channels[ch] = true
})
arrfor(sink.channels, function(ch) {
if (ch == "*") {
wildcard_sinks[] = sink
@@ -471,13 +477,13 @@ function load_log_config() {
terminal: {
type: "console",
format: "pretty",
channels: ["console", "error", "system"]
channels: ["console", "error", "system"],
stack: ["error"]
}
}
}
}
build_sink_routing()
log_stack_enabled = log_config.stack == true
}
function pretty_format(rec) {
@@ -503,7 +509,16 @@ function pretty_format(rec) {
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`
var out = `[${aid}] ${ev}\n`
var i = 0
var fr = null
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) {
@@ -549,7 +564,7 @@ log = function(name, args) {
}
caller = caller_info(2)
if (log_stack_enabled) stack = os.stack(1)
if (stack_channels[name]) stack = os.stack(1)
rec = {
actor_id: _cell.id,
timestamp: time.number(),