Skip to content

Commit 75df11f

Browse files
mraszykclaude
andauthored
feat: canister logs extensions (#252)
Canister logs extensions: * New canister setting `log_memory_limit` bounding the memory used for canister logs: it must be either `0` or a number between `4096` and `2097152` (`2 MiB`), inclusively, with the default value `4096`. The oldest canister logs are purged if the total memory used for canister logs exceeds this value. The memory used by the store holding the canister logs is determined by this setting (it does not depend on the canister logs actually stored) and counted in the canister's memory usage. Hence, raising this setting might require reserving cycles. Changing this setting also resizes the store holding the canister logs, which consumes cycles. The setting is reset if the canister runs out of cycles. * `canister_status` returns the setting `log_memory_limit` and the memory used by the store holding the canister logs in the new field `log_memory_store_size` of `memory_metrics`. * `fetch_canister_logs` can also be called by canisters via replicated (update) calls. It still cannot be called by external users via replicated calls. * New optional `filter` argument of `fetch_canister_logs` restricting the returned logs to a range of log indices (`by_idx`) or timestamps (`by_timestamp_nanos`). * The total size of all logs returned by `fetch_canister_logs` is bounded by an implementation-defined constant chosen so as not to exceed the maximum response size (instead of the previous bound of 4KiB). If the selected logs do not all fit, an unfiltered read trims the oldest logs (so the response ends with the newest log) and a filtered read trims the newest logs (so the response starts with the oldest log satisfying the filter). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 400e986 commit 75df11f

7 files changed

Lines changed: 236 additions & 93 deletions

File tree

docs/guides/canister-management/logs.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ icp canister logs <canister-name> -e ic --since-index 100 --until-index 200
102102

103103
Timestamp and index filters cannot be combined with `--follow`.
104104

105+
Filtering also changes which records are dropped when the selected logs exceed the response size. An unfiltered read trims the oldest records, so it returns the most recent logs (the output ends with the newest record). A filtered read trims the newest records instead, so it returns the oldest records that match the filter (the output starts with the oldest matching record), which lets you page forward through a range by advancing the start of the filter.
106+
105107
To output logs as JSON for programmatic processing:
106108

107109
```bash
@@ -178,6 +180,8 @@ The default log buffer size is 4096 bytes. When the buffer fills up, older log e
178180
icp canister settings update <canister-name> -e ic --log-memory-limit 2mib
179181
```
180182

183+
The limit must be either 0 (no memory for logs) or at least 4096 bytes: values between 1 and 4095 are rejected.
184+
181185
Supported suffixes: `kb` (1,000 bytes), `kib` (1,024 bytes), `mb` (1,000,000 bytes), `mib` (1,048,576 bytes). In `icp.yaml`:
182186

183187
```yaml

docs/guides/canister-management/settings.mdx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,12 @@ settings:
197197

198198
### Log memory limit
199199

200-
{/* Needs human verification: log_memory_limit is exposed by icp-cli but is absent from the canonical ic.did: verify whether this is a management canister setting or an icp-cli layer setting */}
201-
202200
Maximum memory for storing canister logs. Oldest logs are purged when usage exceeds this value.
203201

204202
| Property | Value |
205203
|----------|-------|
206204
| Type | Integer or string with suffix |
207-
| Max | 2 MiB |
205+
| Valid values | 0, or 4096 bytes up to 2 MiB (values between 1 and 4095 are rejected) |
208206
| Default | 4096 bytes |
209207
| icp.yaml key | `log_memory_limit` |
210208

docs/references/ic-interface-spec/abstract-behavior.md

Lines changed: 172 additions & 83 deletions
Large diffs are not rendered by default.

docs/references/ic-interface-spec/changelog.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,27 @@ sidebar:
88

99
## Changelog {#changelog}
1010

11+
### 0.67.0 (2026-08-31) {$0_67_0}
12+
* New canister setting `log_memory_limit` bounding the memory used for canister logs: it must be either `0`
13+
or a number between `4096` and `2097152` (`2 MiB`), inclusively, with the default value `4096`.
14+
The oldest canister logs are purged if the total memory used for canister logs exceeds this value.
15+
The memory used by the store holding the canister logs is determined by this setting
16+
(it does not depend on the canister logs actually stored) and counted in the canister's memory usage.
17+
Hence, raising this setting might require reserving cycles. Changing this setting also resizes
18+
the store holding the canister logs, which consumes cycles.
19+
The setting is reset if the canister runs out of cycles.
20+
* `canister_status` returns the setting `log_memory_limit` and the memory used by the store holding
21+
the canister logs in the new field `log_memory_store_size` of `memory_metrics`.
22+
* `fetch_canister_logs` can also be called by canisters via replicated (update) calls.
23+
It still cannot be called by external users via replicated calls.
24+
* New optional `filter` argument of `fetch_canister_logs` restricting the returned logs to a range of
25+
log indices (`by_idx`) or timestamps (`by_timestamp_nanos`).
26+
* The total size of all logs returned by `fetch_canister_logs` is bounded by an implementation-defined
27+
constant chosen so as not to exceed the maximum response size (instead of the previous bound of 4KiB).
28+
If the selected logs do not all fit, an unfiltered read trims the oldest logs (so the response ends
29+
with the newest log) and a filtered read trims the newest logs (so the response starts with the oldest
30+
log satisfying the filter).
31+
1132
### 0.66.0 (2026-08-17) {$0_66_0}
1233
* Two new paths in the certified state tree, `/canister/<canister_id>/canister_creation_timestamp`
1334
(the time at which the canister was created) and `/canister/<canister_id>/last_install_timestamp`

docs/references/ic-interface-spec/management-canister.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ The optional `settings` parameter can be used to set the following settings:
105105

106106
Default value: `controllers`.
107107

108+
- `log_memory_limit` (`nat`)
109+
110+
Must be either `0` or a number between `4096` and `2097152` (`2 MiB`), inclusively, and indicates the maximum amount of memory used for canister logs.
111+
In particular, values between `1` and `4095`, inclusively, are not allowed.
112+
Oldest canister logs are purged if the total memory used for canister logs exceeds this value.
113+
114+
Default value: `4096`.
115+
108116
- `snapshot_visibility` (`snapshot_visibility`)
109117

110118
Controls who can access the canister's snapshots through the following endpoints of the management canister:
@@ -332,6 +340,8 @@ Regardless of this setting, the canister itself and subnet admins can always req
332340

333341
* `snapshots_size`: Represents the memory consumed by all snapshots that belong to this canister.
334342

343+
* `log_memory_store_size`: Represents the memory used by canister logs of the canister.
344+
335345
All sizes are expressed in bytes.
336346

337347
### IC method `canister_metrics` {#ic-canister_metrics}
@@ -950,13 +960,14 @@ A snapshot may be deleted only by the controllers of the canister that the snaps
950960

951961
### IC method `fetch_canister_logs` {#ic-fetch_canister_logs}
952962

953-
This method can only be called via non-replicated (query) calls: by external users directly and by canisters from composite query methods and their callbacks, i.e., it cannot be called via replicated calls.
963+
This method can be called by canisters via replicated calls, but it cannot be called by external users via replicated (update) calls.
964+
This method can also be called via non-replicated (query) calls: by external users directly and by canisters from composite query methods and their callbacks.
954965
A call from a composite query is executed against the state of the subnet hosting the calling canister and can thus only target canisters hosted by that subnet.
955966

956967
Given a canister ID as input, this method returns a vector of logs of that canister including its trap messages.
957968
The canister logs are *not* collected in canister methods running in non-replicated mode (NRQ, TQ, CQ, CRy, CRt, CC, and F modes, as defined in [Overview of imports](./canister-interface.md#system-api-imports)) and the canister logs are *purged* when the canister is reinstalled or uninstalled.
958-
The total size of all returned logs does not exceed 4KiB.
959-
If new logs are added resulting in exceeding the maximum total log size of 4KiB, the oldest logs will be removed.
969+
The total size of all returned logs does not exceed an implementation-defined constant chosen so as not to exceed the maximum response size.
970+
Oldest canister logs are purged if the total memory used for canister logs exceeds the value `log_memory_limit` in canister settings.
960971
Logs persist across canister upgrades and they are deleted if the canister is reinstalled or uninstalled.
961972

962973
The log visibility is defined in the `log_visibility` field of `canister_settings` and can be one of the following variants:
@@ -971,6 +982,14 @@ A single log is a record with the following fields:
971982
- `timestamp_nanos` (`nat64`): the timestamp as nanoseconds since 1970-01-01 at which the log was recorded;
972983
- `content` (`blob`): the actual content of the log;
973984

985+
To filter canister logs, an optional filter can be provided and has one of the following variants:
986+
- `by_idx` (`record { start : nat64; end : nat64 }`): only logs are returned whose `idx` is within the provided range (`start` is inclusive, but `end` is exclusive);
987+
- `by_timestamp_nanos` (`record { start : nat64; end : nat64 }`): only logs are returned whose `timestamp_nanos` is within the provided range (`start` is inclusive, but `end` is exclusive).
988+
989+
When the logs selected for the response do not all fit within a single response, they are trimmed to fit, and the direction of trimming differs between filtered and unfiltered reads:
990+
- An **unfiltered** read trims the **oldest** log records, so the response ends with the newest log record. This surfaces the most recent activity.
991+
- A **filtered** read trims the **newest** log records, so the response starts with the oldest log record satisfying the filter. This lets a filtered read page forward through logs starting from the beginning of the requested range.
992+
974993
:::warning
975994

976995
The response of a query comes from a single replica, and is therefore not appropriate for security-sensitive applications.

docs/references/management-canister.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Several methods accept or return a `canister_settings` record. The fields are:
2727
| `wasm_memory_limit` | `nat` | `0` | Upper limit on Wasm heap memory in bytes (0 = no limit) |
2828
| `wasm_memory_threshold` | `nat` | `0` | Remaining Wasm memory threshold that triggers the low-memory hook |
2929
| `log_visibility` | `log_visibility` | `controllers` | Who can read canister logs: `controllers`, `public`, or `allowed_viewers(vec principal)` |
30+
| `log_memory_limit` | `nat` | `4096` | Memory in bytes for storing canister logs: either `0` or between `4096` and `2097152` (2 MiB) |
3031
| `snapshot_visibility` | `snapshot_visibility` | `controllers` | Who can list and read canister snapshots: `controllers`, `public`, or `allowed_viewers(vec principal)` |
3132
| `status_visibility` | `status_visibility` | `controllers` | Who can read the canister status: `controllers`, `public`, or `allowed_viewers(vec principal)` |
3233
| `environment_variables` | `opt record` | `null` | Key-value pairs accessible during canister execution |
@@ -129,7 +130,7 @@ Returns detailed information about a canister: status, settings, module hash, cy
129130
- `settings`: the definite canister settings currently in effect
130131
- `module_hash` (`opt blob`): SHA-256 of installed module (`null` if empty)
131132
- `memory_size` (`nat`): total memory consumed
132-
- `memory_metrics`: breakdown by component (Wasm memory, stable memory, globals, binary, custom sections, history, chunk store, snapshots)
133+
- `memory_metrics`: breakdown by component (Wasm memory, stable memory, globals, binary, custom sections, history, chunk store, snapshots, log memory store)
133134
- `cycles` (`nat`): current cycle balance
134135
- `reserved_cycles` (`nat`): reserved cycle balance
135136
- `idle_cycles_burned_per_day` (`nat`): daily idle burn rate
@@ -534,13 +535,17 @@ For Bitcoin integration patterns, see the [Bitcoin guide](../guides/chain-fusion
534535

535536
### `fetch_canister_logs`
536537

537-
Returns the most recent log entries for a canister. Logs are produced by `ic0.debug_print` and trap messages. Logs persist across upgrades but are purged on reinstall or uninstall. Total log size is capped at 4 KiB.
538+
Returns log entries for a canister. Logs are produced by `ic0.debug_print` and trap messages. Logs persist across upgrades but are purged on reinstall or uninstall. The oldest logs are purged once the memory used for canister logs exceeds the `log_memory_limit` canister setting.
538539

539-
- **Caller:** External users via query calls, or canisters from composite queries (not callable via replicated calls)
540-
- **Parameters:** `canister_id` (`principal`)
540+
- **Caller:** Canisters via replicated calls or composite queries, and external users via query calls (external users cannot call it via replicated calls)
541+
- **Parameters:**
542+
- `canister_id` (`principal`)
543+
- `filter` (`opt variant { by_idx : record { start : nat64; end : nat64 }; by_timestamp_nanos : record { start : nat64; end : nat64 } }`): returns only the logs whose `idx` or `timestamp_nanos` falls in the given range (`start` is inclusive, `end` is exclusive)
541544
- **Returns:**
542545
- `canister_log_records` (`vec record { idx : nat64; timestamp_nanos : nat64; content : blob }`)
543546

547+
The total size of the returned logs is bounded by an implementation-defined constant chosen so as not to exceed the maximum response size. When the selected logs do not all fit, an unfiltered read trims the oldest records, so the response ends with the newest log, and a filtered read trims the newest records, so the response starts with the oldest log matching the filter.
548+
544549
Log visibility is controlled by the `log_visibility` canister setting.
545550

546551
For practical usage, see the [canister logs guide](../guides/canister-management/logs.md).

public/references/ic.did

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type canister_settings = record {
3333
reserved_cycles_limit : opt nat;
3434
minimum_incoming_canister_call_cycles : opt nat;
3535
log_visibility : opt log_visibility;
36+
log_memory_limit : opt nat;
3637
snapshot_visibility : opt snapshot_visibility;
3738
status_visibility : opt status_visibility;
3839
wasm_memory_limit : opt nat;
@@ -48,6 +49,7 @@ type definite_canister_settings = record {
4849
reserved_cycles_limit : nat;
4950
minimum_incoming_canister_call_cycles : nat;
5051
log_visibility : log_visibility;
52+
log_memory_limit : nat;
5153
snapshot_visibility : snapshot_visibility;
5254
status_visibility : status_visibility;
5355
wasm_memory_limit : nat;
@@ -310,6 +312,7 @@ type canister_status_result = record {
310312
canister_history_size : nat;
311313
wasm_chunk_store_size : nat;
312314
snapshots_size : nat;
315+
log_memory_store_size : nat;
313316
};
314317
cycles : nat;
315318
reserved_cycles : nat;
@@ -509,6 +512,10 @@ type delete_canister_snapshot_args = record {
509512

510513
type fetch_canister_logs_args = record {
511514
canister_id : canister_id;
515+
filter : opt variant {
516+
by_idx : record { start : nat64; end : nat64 };
517+
by_timestamp_nanos : record { start : nat64; end : nat64 };
518+
};
512519
};
513520

514521
type canister_log_record = record {

0 commit comments

Comments
 (0)