Logging
The logging example demonstrates how to log for the purpose of debugging.
Logs in contracts are only visible in tests, or when executing contracts using stellar-cli. Logs are only compiled into the contract if the debug-assertions Rust compiler option is enabled.
Logs are not a substitute for step-through debugging. Rust tests for Soroban can be step-through debugged in your Rust-enabled IDE. See testing for more details.
Logs are not accessible by dapps and other applications. See the events example for how to produce structured events.
Run the Example
First go through the Setup process to get your development environment configured, then clone the v23.0.0 tag of soroban-examples repository:
git clone -b v23.0.0 https://github.com/stellar/soroban-examples
Or, skip the development environment setup and open this example in GitHub Codespaces or Code Anywhere.
To run the tests for the example, navigate to the logging directory, and use cargo test.
cd logging
cargo test -- --nocapture
You should see the output:
running 1 test
[Diagnostic Event] contract:CAAA..., topics:[log], data:["Hello {}", Dev]
[Diagnostic Event] contract:CAAA..., topics:[log], data:["Hello {}", Dev]
Writing test snapshot file for test "test::test" to "test_snapshots/test/test.1.json".
test test::test ... ok
Code
[profile.release-with-logs]
inherits = "release"
debug-assertions = true
#![no_std]
use soroban_sdk::{contract, contractimpl, log, Env, Symbol};
#[contract]
pub struct Contract;
#[contractimpl]
impl Contract {
pub fn hello(env: Env, value: Symbol) {
log!(&env, "Hello {}", value);
}
}
Ref: https://github.com/stellar/soroban-examples/tree/v23.0.0/logging
How it Works
The log! macro logs a string. Any logs that occur during execution are outputted to stdout in stellar-cli and available for tests to assert on or print.
Logs are only outputted if the contract is built with the debug-assertions compiler option enabled. This makes them efficient to leave in code permanently since a regular release build will omit them.
Logs are only recorded in Soroban environments that have logging enabled. The only Soroban environments where logging is enabled is in Rust tests, and in the stellar-cli.
Open the files above to follow along.
Cargo.toml Profile
Logs are only outputted if the contract is built with the debug-assertions compiler option enabled.
The test profile that is activated when running cargo test has debug-assertions enabled, so when running tests logs are enabled by default.
A new release-with-logs profile is added to Cargo.toml that inherits from the release profile, and enables debug-assertions. It can be used to build a .wasm file that has logs enabled.
[profile.release-with-logs]
inherits = "release"
debug-assertions = true
To build without logs use the --release or --profile release option.
To build with logs use the --profile release-with-logs option.
Using the log! Macro
The log! macro builds a string from the format string, and a list of arguments. Arguments are substituted wherever the {} value appears in the format string.
log!(&env, "Hello {}", value);
The above log will render as follows if value is a Symbol containing "Dev".
Hello Symbol(Dev)
The values outputted are currently relatively limited. While primitive values like u32, u64, bool, and Symbols will render clearly in the log output, Bytes, Vec, Map, and custom types will render only their handle number. Logging capabilities are in early development.
Tests
Open the logging/src/test.rs file to follow along.
extern crate std;
#[test]
fn test() {
let env = Env::default();
let addr = Address::from_str(
&env,
"CAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQMCJ",
);
let contract_id = env.register_at(&addr, Contract, ());
let client = ContractClient::new(&env, &contract_id);
client.hello(&symbol_short!("Dev"));
let logs = env.logs().all();
assert_eq!(logs, std::vec!["[Diagnostic Event] contract:CAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQMCJ, topics:[log], data:[\"Hello {}\", Dev]"]);
std::println!("{}", logs.join("\n"));
}
The std crate, which contains the Rust standard library, is imported so that the test can use the std::vec! and std::println! macros. Since contracts are required to use #![no_std], tests in contracts must manually import std to use std functionality like printing to stdout.
extern crate std;
In any test the first thing that is always required is an Env, which is the Soroban environment that the contract will run in.
let env = Env::default();
The contract is registered with the environment using the contract type. We're specifying precisely which contract address the contract should be deployed to in the test, using the env.register_at function. This makes it easier to ensure the logging output is coming from the relevant contract address.
let addr = Address::from_str(
&env,
"CAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQMCJ",
);
let contract_id = env.register_at(&addr, Contract, ());
All public functions within an impl block that is annotated with the #[contractimpl] attribute have a corresponding function generated in a generated client type. The client type will be named the same as the contract type with Client appended. For example, in our contract the contract type is HelloContract, and the client is named HelloContractClient.
let client = ContractClient::new(&env, &contract_id);
client.hello(&symbol_short!("Dev"));
Logs are available in tests via the environment.
let logs = env.logs().all();
They can be asserted on like any other value.
assert_eq!(logs, std::vec!["[Diagnostic Event] contract:CAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQMCJ, topics:[log], data:[\"Hello {}\", Dev]"]);
They can also be printed to stdout.
std::println!("{}", logs.join("\n"));
Build the Contract
To build the contract, use the stellar contract build command.
Without Logs
To build the contract without logs, use the --release option.
stellar contract build
A .wasm file should be outputted in the target directory, in the release subdirectory:
target/wasm32v1-none/release/soroban_logging_contract.wasm
With Logs
To build the contract with logs, use the --profile release-with-logs option.
stellar contract build --profile release-with-logs
A .wasm file should be outputted in the target directory, in the release-with-logs subdirectory:
target/wasm32v1-none/release-with-logs/soroban_logging_contract.wasm
Run the Contract
If you have stellar-cli installed, you can invoke contract functions in the using it. Specify the -v option to enable verbose logs.
- macOS/Linux
- Windows (PowerShell)
stellar -v contract invoke \
--wasm target/wasm32v1-none/release-with-logs/soroban_logging_contract.wasm \
--id 1 \
-- \
hello \
--value friend
stellar -v contract invoke `
--wasm target/wasm32v1-none/release-with-logs/soroban_logging_contract.wasm `
--id 1 `
-- `
hello `
--value friend
The output should include the following line.
📔 CAAA... - Success - Log: {"vec":[{"string":"Hello {}"},{"symbol":"friend"}]}