tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 });