SessionLogger.sys.mjs (4643B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; 6 import { LogManager } from "resource://gre/modules/LogManager.sys.mjs"; 7 // See Bug 1889052 8 // eslint-disable-next-line mozilla/use-console-createInstance 9 import { Log } from "resource://gre/modules/Log.sys.mjs"; 10 11 const lazy = {}; 12 ChromeUtils.defineESModuleGetters(lazy, { 13 AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs", 14 cancelIdleCallback: "resource://gre/modules/Timer.sys.mjs", 15 requestIdleCallback: "resource://gre/modules/Timer.sys.mjs", 16 }); 17 18 XPCOMUtils.defineLazyPreferenceGetter( 19 lazy, 20 "logFlushIntervalSeconds", 21 "browser.sessionstore.logFlushIntervalSeconds", 22 3600 23 ); 24 25 const loggerNames = ["SessionStore"]; 26 27 export const sessionStoreLogger = Log.repository.getLogger("SessionStore"); 28 sessionStoreLogger.manageLevelFromPref("browser.sessionstore.loglevel"); 29 30 class SessionLogManager extends LogManager { 31 #idleCallbackId = null; 32 #startupTime = 0; 33 #isStartingUp = true; 34 #observers = new Set(); 35 36 QueryInterface = ChromeUtils.generateQI([Ci.nsIObserver]); 37 38 constructor(options = {}) { 39 super(options); 40 41 Services.obs.addObserver(this, "sessionstore-windows-restored"); 42 this.#observers.add("sessionstore-windows-restored"); 43 44 if (this._fileAppenderChangeTopic) { 45 Services.obs.addObserver(this, this._fileAppenderChangeTopic); 46 this.#observers.add(this._fileAppenderChangeTopic); 47 } 48 49 lazy.AsyncShutdown.profileBeforeChange.addBlocker( 50 "SessionLogManager: finalize and flush any logs to disk", 51 () => { 52 return this.stop(); 53 } 54 ); 55 } 56 57 get isDebug() { 58 return this.level >= Log.Level.Debug; 59 } 60 61 getLogFilename(reasonPrefix = "success") { 62 if (!this.#startupTime) { 63 this.#startupTime = Services.startup.getStartupInfo().main.getTime(); 64 } 65 // For session restore, we want to append to a single success and error log file for each startup 66 return super.getLogFilename(reasonPrefix, this.#startupTime); 67 } 68 69 async stop() { 70 if (this.#observers.has("sessionstore-windows-restored")) { 71 Services.obs.removeObserver(this, "sessionstore-windows-restored"); 72 this.#observers.delete("sessionstore-windows-restored"); 73 this.#isStartingUp = false; 74 } 75 if ( 76 this._fileAppenderChangeTopic && 77 this.#observers.has(this._fileAppenderChangeTopic) 78 ) { 79 Services.obs.removeObserver(this, this._fileAppenderChangeTopic); 80 this.#observers.delete(this._fileAppenderChangeTopic); 81 } 82 await this.requestLogFlush(true); 83 this.finalize(); 84 } 85 86 observe(subject, topic, _) { 87 switch (topic) { 88 case "sessionstore-windows-restored": 89 // this represents the moment session restore is nominally complete 90 // and is a good time to ensure any log messages are flushed to disk 91 Services.obs.removeObserver(this, "sessionstore-windows-restored"); 92 this.#observers.delete("sessionstore-windows-restored"); 93 this.requestLogFlush(); 94 this.#isStartingUp = false; 95 break; 96 case this._fileAppenderChangeTopic: { 97 let shouldFlush = false; 98 const msSinceLastFlush = Date.now() - this._fileAppender.lastFlushTime; 99 100 if (this._fileAppender.sawError) { 101 shouldFlush = true; 102 } else if ( 103 !this.#isStartingUp && 104 msSinceLastFlush / 1000 >= lazy.logFlushIntervalSeconds 105 ) { 106 // we'll flush when initial startup is complete so ignore appends until then 107 shouldFlush = true; 108 } 109 if (shouldFlush) { 110 this.requestLogFlush(); 111 } 112 break; 113 } 114 } 115 } 116 117 async requestLogFlush(immediate = false) { 118 if (this.#idleCallbackId && !immediate) { 119 return; 120 } 121 if (this.#idleCallbackId) { 122 lazy.cancelIdleCallback(this.#idleCallbackId); 123 this.#idleCallbackId = null; 124 } 125 if (!immediate) { 126 await new Promise(resolve => { 127 this.#idleCallbackId = lazy.requestIdleCallback(resolve); 128 }); 129 this.#idleCallbackId = null; 130 } 131 await this.resetFileLog(); 132 } 133 } 134 135 export const logManager = new SessionLogManager({ 136 prefRoot: "browser.sessionstore.", 137 logNames: loggerNames, 138 logFilePrefix: "sessionrestore", 139 logFileSubDirectoryEntries: ["sessionstore-logs"], 140 testTopicPrefix: "sessionrestore:log-manager:", 141 fileAppenderChangeTopic: "sessionrestore-log-file-append", 142 overwriteFileOnFlush: false, 143 });