From 7e4c1aa8382c78afcf404013af81ae07deb80d59 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Wed, 20 Nov 2024 09:50:28 +0100 Subject: [PATCH] feat: add support for fetching the canister logs (#755) # Motivation Add support for [fetch_canister_logs](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-fetch_canister_logs). # Changes - Implement function `fetchCanisterLogs` in `ic-management`. --------- Signed-off-by: David Dal Busco Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 1 + packages/ic-management/README.md | 41 ++++++++------ .../src/ic-management.canister.spec.ts | 53 ++++++++++++++++++- .../src/ic-management.canister.ts | 18 ++++++- .../src/types/ic-management.responses.ts | 5 ++ 5 files changed, 101 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b215f3311..4b23b1c71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Added `memoToNeuronSubaccount` and `memoToNeuronAccountIdentifier`. - Support new neuron field `voting_power_refreshed_timestamp_seconds`. +- Add support for fetching the canister logs in `@dfinity@ic-management`. - Add `nowInBigIntNanoSeconds` to `@dfinity/utils`, a trivial function that is actually used across all our dapps. ## Build diff --git a/packages/ic-management/README.md b/packages/ic-management/README.md index 7d06a9b85..3c96930a9 100644 --- a/packages/ic-management/README.md +++ b/packages/ic-management/README.md @@ -54,7 +54,7 @@ const { status, memory_size, ...rest } = await canisterStatus(YOUR_CANISTER_ID); ### :factory: ICManagementCanister -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L30) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L33) #### Methods @@ -72,6 +72,7 @@ const { status, memory_size, ...rest } = await canisterStatus(YOUR_CANISTER_ID); - [canisterStatus](#gear-canisterstatus) - [deleteCanister](#gear-deletecanister) - [provisionalCreateCanisterWithCycles](#gear-provisionalcreatecanisterwithcycles) +- [fetchCanisterLogs](#gear-fetchcanisterlogs) ##### :gear: create @@ -79,7 +80,7 @@ const { status, memory_size, ...rest } = await canisterStatus(YOUR_CANISTER_ID); | -------- | ---------------------------------------------------------------- | | `create` | `(options: ICManagementCanisterOptions) => ICManagementCanister` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L35) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L38) ##### :gear: createCanister @@ -89,7 +90,7 @@ Create a new canister | ---------------- | ------------------------------------------------------------------------------------- | | `createCanister` | `({ settings, senderCanisterVersion, }?: CreateCanisterParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L75) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L78) ##### :gear: updateSettings @@ -99,7 +100,7 @@ Update canister settings | ---------------- | ------------------------------------------------------------------------------------------- | | `updateSettings` | `({ canisterId, senderCanisterVersion, settings, }: UpdateSettingsParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L96) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L99) ##### :gear: installCode @@ -109,7 +110,7 @@ Install code to a canister | ------------- | ----------------------------------------------------------------------------------------------------- | | `installCode` | `({ mode, canisterId, wasmModule, arg, senderCanisterVersion, }: InstallCodeParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L118) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L121) ##### :gear: uploadChunk @@ -124,7 +125,7 @@ Parameters: - `params.canisterId`: The canister in which the chunks will be stored. - `params.chunk`: A chunk of Wasm module. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L143) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L146) ##### :gear: clearChunkStore @@ -138,7 +139,7 @@ Parameters: - `params.canisterId`: The canister in which the chunks are stored. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L163) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L166) ##### :gear: storedChunks @@ -152,7 +153,7 @@ Parameters: - `params.canisterId`: The canister in which the chunks are stored. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L182) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L185) ##### :gear: installChunkedCode @@ -172,7 +173,7 @@ Parameters: - `params.storeCanisterId`: Specifies the canister in whose chunk storage the chunks are stored (this parameter defaults to target_canister if not specified). - `params.wasmModuleHash`: The Wasm module hash as hex string. Used to check that the SHA-256 hash of wasm_module is equal to the wasm_module_hash parameter and can calls install_code with parameters. -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L207) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L210) ##### :gear: uninstallCode @@ -182,7 +183,7 @@ Uninstall code from a canister | --------------- | -------------------------------------------------------------------------------- | | `uninstallCode` | `({ canisterId, senderCanisterVersion, }: UninstallCodeParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L240) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L243) ##### :gear: startCanister @@ -192,7 +193,7 @@ Start a canister | --------------- | ------------------------------------------ | | `startCanister` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L255) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L258) ##### :gear: stopCanister @@ -202,7 +203,7 @@ Stop a canister | -------------- | ------------------------------------------ | | `stopCanister` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L264) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L267) ##### :gear: canisterStatus @@ -212,7 +213,7 @@ Get canister details (memory size, status, etc.) | ---------------- | ------------------------------------------------------------ | | `canisterStatus` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L273) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L276) ##### :gear: deleteCanister @@ -222,7 +223,7 @@ Deletes a canister | ---------------- | ------------------------------------------ | | `deleteCanister` | `(canisterId: Principal) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L284) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L287) ##### :gear: provisionalCreateCanisterWithCycles @@ -232,7 +233,17 @@ Creates a canister. Only available on development instances. | ------------------------------------- | ------------------------------------------------------------------------------------------------------- | | `provisionalCreateCanisterWithCycles` | `({ settings, amount, canisterId, }?: ProvisionalCreateCanisterWithCyclesParams) => Promise` | -[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L296) +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L299) + +##### :gear: fetchCanisterLogs + +Given a canister ID as input, this method returns a vector of logs of that canister including its trap messages. The canister logs are not collected in canister methods running in non-replicated mode (NRQ, CQ, CRy, CRt, CC, and F modes, as defined in Overview of imports). The total size of all returned logs does not exceed 4KiB. If new logs are added resulting in exceeding the maximum total log size of 4KiB, the oldest logs will be removed. Logs persist across canister upgrades and they are deleted if the canister is reinstalled or uninstalled. + +| Method | Type | +| ------------------- | ---------------------------------------------------------------- | +| `fetchCanisterLogs` | `(canisterId: Principal) => Promise` | + +[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ic-management/src/ic-management.canister.ts#L321) diff --git a/packages/ic-management/src/ic-management.canister.spec.ts b/packages/ic-management/src/ic-management.canister.spec.ts index baf0c06e7..55b48bda3 100644 --- a/packages/ic-management/src/ic-management.canister.spec.ts +++ b/packages/ic-management/src/ic-management.canister.spec.ts @@ -25,7 +25,10 @@ import { type StoredChunksParams, type UploadChunkParams, } from "./types/ic-management.params"; -import { CanisterStatusResponse } from "./types/ic-management.responses"; +import { + CanisterStatusResponse, + type FetchCanisterLogsResponse, +} from "./types/ic-management.responses"; describe("ICManagementCanister", () => { const mockAgent: HttpAgent = mock(); @@ -667,4 +670,52 @@ describe("ICManagementCanister", () => { expect(call).rejects.toThrowError(Error); }); }); + + describe("fetchCanisterLogs", () => { + it("returns canister logs when success", async () => { + const settings = { + freezing_threshold: BigInt(2), + controllers: [mockPrincipal], + memory_allocation: BigInt(4), + compute_allocation: BigInt(10), + reserved_cycles_limit: BigInt(11), + log_visibility: { controllers: null }, + wasm_memory_limit: BigInt(500_00), + }; + const response: FetchCanisterLogsResponse = { + canister_log_records: [ + { + idx: 123n, + content: [1, 2, 3], + timestamp_nanos: 12345n, + }, + { + idx: 456n, + content: [9, 8, 7], + timestamp_nanos: 12346n, + }, + ], + }; + const service = mock(); + service.fetch_canister_logs.mockResolvedValue(response); + + const icManagement = await createICManagement(service); + + const res = await icManagement.fetchCanisterLogs(mockCanisterId); + + expect(res).toEqual(response); + }); + + it("throws Error", async () => { + const error = new Error("Test"); + const service = mock(); + service.fetch_canister_logs.mockRejectedValue(error); + + const icManagement = await createICManagement(service); + + const call = () => icManagement.fetchCanisterLogs(mockCanisterId); + + expect(call).rejects.toThrowError(Error); + }); + }); }); diff --git a/packages/ic-management/src/ic-management.canister.ts b/packages/ic-management/src/ic-management.canister.ts index b10c19e12..4cb9f0e33 100644 --- a/packages/ic-management/src/ic-management.canister.ts +++ b/packages/ic-management/src/ic-management.canister.ts @@ -25,7 +25,10 @@ import { type UpdateSettingsParams, type UploadChunkParams, } from "./types/ic-management.params"; -import type { CanisterStatusResponse } from "./types/ic-management.responses"; +import type { + CanisterStatusResponse, + FetchCanisterLogsResponse, +} from "./types/ic-management.responses"; export class ICManagementCanister { private constructor(private readonly service: IcManagementService) { @@ -308,4 +311,17 @@ export class ICManagementCanister { return canister_id; }; + + /** + * Given a canister ID as input, this method returns a vector of logs of that canister including its trap messages. The canister logs are not collected in canister methods running in non-replicated mode (NRQ, CQ, CRy, CRt, CC, and F modes, as defined in Overview of imports). The total size of all returned logs does not exceed 4KiB. If new logs are added resulting in exceeding the maximum total log size of 4KiB, the oldest logs will be removed. Logs persist across canister upgrades and they are deleted if the canister is reinstalled or uninstalled. + * + * @param {Principal} canisterId + * @returns {Promise} + */ + fetchCanisterLogs = ( + canisterId: Principal, + ): Promise => + this.service.fetch_canister_logs({ + canister_id: canisterId, + }); } diff --git a/packages/ic-management/src/types/ic-management.responses.ts b/packages/ic-management/src/types/ic-management.responses.ts index 37204781d..2d06b8ef0 100644 --- a/packages/ic-management/src/types/ic-management.responses.ts +++ b/packages/ic-management/src/types/ic-management.responses.ts @@ -5,3 +5,8 @@ export type CanisterStatusResponse = ServiceResponse< IcManagementService, "canister_status" >; + +export type FetchCanisterLogsResponse = ServiceResponse< + IcManagementService, + "fetch_canister_logs" +>;