commit 45290297179b465ddf1a2d1cd098f3946ecb56af
parent 198fc64bac829ec18d3de6832ebc7fc59d21ed23
Author: Rob Wu <rob@robwu.nl>
Date: Mon, 5 Jan 2026 16:09:00 +0000
Bug 2007587 - Gracefully handle clone errors in StructuredLogger r=ahal
Differential Revision: https://phabricator.services.mozilla.com/D277476
Diffstat:
2 files changed, 73 insertions(+), 8 deletions(-)
diff --git a/testing/modules/StructuredLog.sys.mjs b/testing/modules/StructuredLog.sys.mjs
@@ -215,10 +215,18 @@ export class StructuredLogger {
}
if (this.#dumpScope) {
- this.#dumpFun(Cu.cloneInto(allData, this.#dumpScope));
- } else {
- this.#dumpFun(allData);
+ try {
+ allData = Cu.cloneInto(allData, this.#dumpScope);
+ } catch (e) {
+ try {
+ this.error(`Failed to cloneInto: ${e}`);
+ this.warning(`Tried to clone: ${uneval(allData)}`);
+ } catch (e2) {
+ console.error("Failed to handle clone error", e, e2);
+ }
+ }
}
+ this.#dumpFun(allData);
}
#testId(test) {
diff --git a/testing/modules/tests/xpcshell/test_structuredlog.js b/testing/modules/tests/xpcshell/test_structuredlog.js
@@ -1,11 +1,11 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-function run_test() {
- const { StructuredLogger } = ChromeUtils.importESModule(
- "resource://testing-common/StructuredLog.sys.mjs"
- );
+const { StructuredLogger } = ChromeUtils.importESModule(
+ "resource://testing-common/StructuredLog.sys.mjs"
+);
+add_task(function test_StructuredLogger() {
let testBuffer = [];
let appendBuffer = function (msg) {
@@ -144,4 +144,61 @@ function run_test() {
assertLastMsg({
action: "suite_end",
});
-}
+});
+
+add_task(function test_StructuredLogger_with_dumpScope() {
+ let testBuffer = [];
+ let appendBuffer2 = msg => {
+ testBuffer.push(msg);
+ };
+
+ let assertSeenMsg = (expected, checkExtra) => {
+ const { action, message, level, extra } = testBuffer.shift();
+ deepEqual({ action, message, level }, expected);
+ ok(checkExtra(extra), `Extra: ${uneval(extra)}`);
+ };
+
+ // Regression test for bug 2007587: Logger gracefully handles clone failures.
+ let logger2 = new StructuredLogger("test_dump", appendBuffer2, globalThis);
+ const extraNotCloneable = { notCloneable: () => {} };
+ logger2.info("Test non-cloneable", extraNotCloneable);
+
+ assertSeenMsg(
+ {
+ action: "log",
+ message:
+ "Failed to cloneInto: Error: Permission denied to pass a Function via structured clone",
+ level: "ERROR",
+ },
+ extra => extra === undefined
+ );
+
+ testBuffer[0].message = testBuffer[0].message.replace(/time:\d+/, "time:123");
+ assertSeenMsg(
+ {
+ action: "log",
+ message: `Tried to clone: ({action:"log", time:123, thread:null, pid:null, source:"test_dump", level:"INFO", message:"Test non-cloneable", extra:{notCloneable:() => {}}})`,
+ level: "WARNING",
+ },
+ extra => extra === undefined
+ );
+
+ assertSeenMsg(
+ { action: "log", message: "Test non-cloneable", level: "INFO" },
+ extra => extra === extraNotCloneable
+ );
+
+ // Now verify the most common / desired behavior: clone where possible.
+ const extraCloneable = { cloneable: "Yes I am" };
+ logger2.info("Test cloneable", extraCloneable);
+
+ assertSeenMsg(
+ { action: "log", message: "Test cloneable", level: "INFO" },
+ extra => {
+ deepEqual(extra, extraCloneable);
+ return extra !== extraCloneable;
+ }
+ );
+
+ equal(testBuffer.length, 0, "No messages unaccounted for");
+});