Logging
Overview
The canister logging feature is designed to provide developers with insight into their canister's behavior and assist with scenarios where their canister traps. This feature supports log messages for:
Heartbeats.
Timers.
Pre/post upgrade and
canister_init
.Update calls.
Queries, only if called in replicated mode.
A canister's controller is able to retrieve the canister's logs, even when executions trap, using dfx canister logs
.
Non-replicated calls are not currently supported for logging.
Log visibility
By default, a canister's logs are only visible to the controllers of the canister. A canister's logs can be made public with the --log-visibility
flag:
dfx canister update-settings CANISTER_ID --log-visibility public
Log viewer allow lists
Canister log allow lists enable principals that are not controllers of the canister to view the logs without the logs being public. Log allow lists are supported in dfx
versions 0.24.2
and newer.
To add a principal to the canister's log allow list, update the canister's settings with the --set-log-viewer
or --add-log-viewer
flags:
dfx canister update-settings CANISTER_ID --set-log-viewer PRINCIPAL_ID // Set a single principal as a log viewer
dfx canister update-settings CANISTER_ID --add-log-viewer PRINCIPAL_ID // Add a principal to a list of log viewers
You can also configure a principal as a log viewer when you create a canister with:
dfx canister create CANISTER_NAME --log-viewer PRINCIPAL_ID
Or, you can set the canisters[].initialization_values.log_visibility.allowed_viewers
configuration setting in your project's dfx.json
file.
To remove a principal from the canister's log allow list, use the --remove-log-viewer
flag:
dfx canister update-settings CANISTER_ID --remove-log-viewer PRINCIPAL_ID
Using dfx
A canister's logs can be viewed using the dfx canister logs
command.
As an example, consider a Rust canister with the following code:
use std::cell::RefCell;
use candid::{Nat, Principal};
use ic_cdk::{
export_candid, println,
};
thread_local! {
// create a cell that holds a counter
static COUNTER: std::cell::RefCell<u32> = RefCell::default();
}
#[ic_cdk::update]
fn produce_logs() {
// print the counter value
COUNTER.with_borrow_mut(|counter| {
println!("counter: {}", *counter);
*counter += 1;
});
}
export_candid!();
This simple canister provides an incremental counter method that includes a print function that will return output to the command line and the canister's logs.
Note that this example uses update calls, as non-replicated query calls do not currently support logging.
You can call the canister's method and view the associated logged output for each call. For example, if this canister is deployed as logs_backend
, you can call the canister's produce_logs
method a few times to increment the counter and get the following command line output:
dfx canister call logs_backend produce_logs
2024-06-04 21:14:25.882276 UTC: [Canister br5f7-7uaaa-aaaaa-qaaca-cai] counter: 0
()
dfx canister call logs_backend produce_logs
2024-06-04 21:18:18.094003 UTC: [Canister br5f7-7uaaa-aaaaa-qaaca-cai] counter: 1
()
dfx canister call logs_backend produce_logs
2024-06-04 21:18:46.036022 UTC: [Canister br5f7-7uaaa-aaaaa-qaaca-cai] counter: 2
()
Then, you can call the canister's logs, which in this case will return the same output that was logged to the command line:
dfx canister logs logs_backend
[0. 2024-06-04T21:14:25.882276Z]: counter: 0
[1. 2024-06-04T21:18:18.094003Z]: counter: 1
[2. 2024-06-04T21:18:46.036022Z]: counter: 2