Enable debug logging in predicates and scripts using ECAL (#3059)
## Linked Issues/PRs
VM changes done in https://github.com/FuelLabs/fuel-vm/pull/963 ,
waiting for the next `fuel-vm` release.
See
https://github.com/FuelLabs/fuel-vm/pull/945#issuecomment-2829271997.
Replaces https://github.com/FuelLabs/fuel-core/pull/2973.
## Description
Accomodates https://github.com/FuelLabs/fuel-vm/pull/963. Allows setting
`--allow-syscall` flag, that in turn allows logging from scripts and
predicates using `__dbg!` macro in Sway, which uses the new "ecal
syscall api" defined here.
Currently the API looks like this: `ECAL a b c d` where:
* `a`: Syscall number, set to `1000` for debug log output
* `b`: File descriptor, set to `1` for stdout
* `c`: Address of (pointer to) the message to log
* `d`: Length of the message to log.
### Open questions
* Should we print receipts or at least indicate if the tx reverts in the
console output?
### Resolved
* Should we test the log output somehow? I attempted to use the
[tracing-test crate](https://crates.io/crates/tracing-test), but it
cannot read logs from the fuel-core. [See this
issue](https://github.com/dbrgn/tracing-test/issues/22). I tried to
implement my own, but it turns out it's complicated. Code below.
* Done for now using a separate process and reading stderr.
<details>
<summary>How to capture logs</summary>
Doesn't work since global subscriber cannot be added for multiple
simultaneous tests. Would need to force single thread and filter based
on thread id, or somehow inject per-test unique span to fuel-core.
```rust
use std::{
array, io, sync::{
Arc,
Mutex,
}
};
use tracing::Dispatch;
use tracing_subscriber::{
FmtSubscriber,
fmt::MakeWriter,
};
/// A struct to capture tracing logs for testing purposes.
/// Inspired by the `tracing-test` crate, which turned out to
/// be insufficient for this.
#[derive(Clone)]
pub struct CaptureLogs(Arc<Mutex<String>>);
impl CaptureLogs {
pub fn init() -> Self {
let buffer = Arc::new(Mutex::new(String::new()));
let this = Self(buffer.clone());
let subscriber: Dispatch = FmtSubscriber::builder()
.with_writer(this.clone())
.with_level(true)
.with_ansi(false)
.into();
tracing::dispatcher::set_global_default(subscriber)
.expect("Could not set global tracing subscriber");
this
}
/// Finds expected part of logs and discards everything before it. Panics if the expected part is not found.
pub fn expect(&self, expected: &str) {
// Just reuse the regex logic
self.expect_regex(®ex::escape(expected))
}
/// Like `expect`, but the next part must follow immediately after the previous one.
pub fn expect_follows(&self, expected: &str) {
// Again, reuse the regex logic
self.expect_regex(&format!("^{}", regex::escape(expected)))
}
/// Finds expected part of logs and discards everything before it. Panics if the expected part is not found.
pub fn expect_regex(&self, regex: &str) {
let mut data = self.0.lock().expect("Mutex poisoned");
let Some(m) = regex::Regex::new(regex)
.expect("Failed to compile regex")
.find(&data) else {
println!("Captured logs: {}", data);
panic!(
"Expected pattern `{}` not found in logs.",
regex,
);
};
*data = data.split_at(m.end()).1.to_owned();
}
}
impl io::Write for CaptureLogs {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut target = self.0.lock().expect("Mutex poisoned");
target.push_str(&String::from_utf8_lossy(buf));
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl MakeWriter<'_> for CaptureLogs {
type Writer = Self;
fn make_writer(&self) -> Self::Writer {
self.clone()
}
}
```
</details>
## Checklist
- [ ] Breaking changes are clearly marked as such in the PR description
and changelog
- [x] New behavior is reflected in tests - only partially, see
discussion above
- [x] [The specification](https://github.com/FuelLabs/fuel-specs/)
matches the implemented behavior (link update PR if changes are needed)
### Before requesting review
- [x] I have reviewed the code myself
- [x] I have created follow-up issues caused by this PR and linked them
here
### After merging, notify other teams
[Add or remove entries as needed]
- [ ] [Rust SDK](https://github.com/FuelLabs/fuels-rs/)
- [ ] [Sway compiler](https://github.com/FuelLabs/sway/)
- [ ] [Platform
documentation](https://github.com/FuelLabs/devrel-requests/issues/new?assignees=&labels=new+request&projects=&template=NEW-REQUEST.yml&title=%5BRequest%5D%3A+)
(for out-of-organization contributors, the person merging the PR will do
this)
- [ ] Someone else?
---------
Co-authored-by: Mitchell Turner <james.mitchell.turner@gmail.com>
Co-authored-by: Green Baneling <XgreenX9999@gmail.com> H
Hannes Karppila committed
68d263ce84cff5e5076251921b20e10a9b25cc2a
Parent: baa805f
Committed by GitHub <noreply@github.com>
on 9/18/2025, 2:02:20 PM