Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  HealthPing.sys.mjs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * This module collects data on send failures and other critical issues with Telemetry submissions.
 */

import { TelemetryUtils } from "resource://gre/modules/TelemetryUtils.sys.mjs";

import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  Log: "resource://gre/modules/Log.sys.mjs",
  TelemetryController: "resource://gre/modules/TelemetryController.sys.mjs",
  clearTimeout: "resource://gre/modules/Timer.sys.mjs",
  setTimeout: "resource://gre/modules/Timer.sys.mjs",
});

const Utils = TelemetryUtils;

const MS_IN_A_MINUTE = 60 * 1000;
// Send health ping every hour
const SEND_TICK_DELAY = 60 * MS_IN_A_MINUTE;

// Send top 10 discarded pings only to minimize health ping size
const MAX_SEND_DISCARDED_PINGS = 10;

const LOGGER_NAME = "Toolkit.Telemetry";
const LOGGER_PREFIX = "TelemetryHealthPing::";

export var Policy = {
  setSchedulerTickTimeout: (callback, delayMs) =>
    lazy.setTimeout(callback, delayMs),
  clearSchedulerTickTimeout: id => lazy.clearTimeout(id),
};

export var TelemetryHealthPing = {
  Reason: Object.freeze({
    IMMEDIATE: "immediate", // Ping was sent immediately after recording with no delay.
    DELAYED: "delayed", // Recorded data was sent after a delay.
    SHUT_DOWN: "shutdown", // Recorded data was sent on shutdown.
  }),

  FailureType: Object.freeze({
    DISCARDED_FOR_SIZE: "pingDiscardedForSize",
    SEND_FAILURE: "sendFailure",
  }),

  OsInfo: Object.freeze({
    name: Services.appinfo.OS,
    version:
      Services.sysinfo.get("kernel_version") || Services.sysinfo.get("version"),
  }),

  HEALTH_PING_TYPE: "health",

  _logger: null,

  // The health ping is sent every every SEND_TICK_DELAY.
  // Initialize this so that first failures are sent immediately.
  _lastSendTime: -SEND_TICK_DELAY,

  /**
   * This stores reported send failures with the following structure:
   * {
   *  type1: {
   *    subtype1: value,
   *    ...
   *    subtypeN: value
   *  },
   *  ...
   * }
   */
  _failures: {},
  _timeoutId: null,

  /**
   * Record a failure to send a ping out.
   * @param {String} failureType The type of failure (e.g. "timeout", ...).
   * @returns {Promise} Test-only, resolved when the ping is stored or sent.
   */
  recordSendFailure(failureType) {
    return this._addToFailure(this.FailureType.SEND_FAILURE, failureType);
  },

  /**
   * Record that a ping was discarded and its type.
   * @param {String} pingType The type of discarded ping (e.g. "main", ...).
   * @returns {Promise} Test-only, resolved when the ping is stored or sent.
   */
  recordDiscardedPing(pingType) {
    return this._addToFailure(this.FailureType.DISCARDED_FOR_SIZE, pingType);
  },

  /**
   * Assemble payload.
   * @param {String} reason A string indicating the triggering reason (e.g. "immediate", "delayed", "shutdown").
   * @returns {Object} The assembled payload.
   */
  _assemblePayload(reason) {
    this._log.trace("_assemblePayload()");
    let payload = {
      os: this.OsInfo,
      reason,
    };

    for (let key of Object.keys(this._failures)) {
      if (key === this.FailureType.DISCARDED_FOR_SIZE) {
        payload[key] = this._getTopDiscardFailures(this._failures[key]);
      } else {
        payload[key] = this._failures[key];
      }
    }

    return payload;
  },

  /**
   * Sort input dictionary descending by value.
   * @param {Object} failures A dictionary of failures subtype and count.
   * @returns {Object} Sorted failures by value.
   */
  _getTopDiscardFailures(failures) {
    this._log.trace("_getTopDiscardFailures()");
    let sortedItems = Object.entries(failures).sort((first, second) => {
      return second[1] - first[1];
    });

    let result = {};
    sortedItems.slice(0, MAX_SEND_DISCARDED_PINGS).forEach(([key, value]) => {
      result[key] = value;
    });

    return result;
  },

  /**
   * Assemble the failure information and submit it.
   * @param {String} reason A string indicating the triggering reason (e.g. "immediate", "delayed", "shutdown").
   * @returns {Promise} Test-only promise that resolves when the ping was stored or sent (if any).
   */
  _submitPing(reason) {
    if (!TelemetryHealthPing.enabled || !this._hasDataToSend()) {
      return Promise.resolve();
    }

    this._log.trace("_submitPing(" + reason + ")");
    let payload = this._assemblePayload(reason);
    this._clearData();
    this._lastSendTime = Utils.monotonicNow();

    let options = {
      addClientId: true,
      usePingSender: reason === this.Reason.SHUT_DOWN,
    };

    return new Promise(r =>
      // If we submit the health ping immediately, the send task would be triggered again
      // before discarding oversized pings from the queue.
      // To work around this, we send the ping on the next tick.
      Services.tm.dispatchToMainThread(() =>
        r(
          lazy.TelemetryController.submitExternalPing(
            this.HEALTH_PING_TYPE,
            payload,
            options
          )
        )
      )
    );
  },

  /**
   * Accumulate failure information and trigger a ping immediately or on timeout.
   * @param {String} failureType The type of failure (e.g. "timeout", ...).
   * @param {String} failureSubType The subtype of failure (e.g. ping type, ...).
   * @returns {Promise} Test-only, resolved when the ping is stored or sent.
   */
  _addToFailure(failureType, failureSubType) {
    this._log.trace(
      "_addToFailure() - with type and subtype: " +
        failureType +
        " : " +
        failureSubType
    );

    if (!(failureType in this._failures)) {
      this._failures[failureType] = {};
    }

    let current = this._failures[failureType][failureSubType] || 0;
    this._failures[failureType][failureSubType] = current + 1;

    const now = Utils.monotonicNow();
    if (now - this._lastSendTime >= SEND_TICK_DELAY) {
      return this._submitPing(this.Reason.IMMEDIATE);
    }

    let submissionDelay = SEND_TICK_DELAY - now - this._lastSendTime;
    this._timeoutId = Policy.setSchedulerTickTimeout(
      () => TelemetryHealthPing._submitPing(this.Reason.DELAYED),
      submissionDelay
    );
    return Promise.resolve();
  },

  /**
   * @returns {boolean} Check the availability of recorded failures data.
   */
  _hasDataToSend() {
    return Object.keys(this._failures).length !== 0;
  },

  /**
   * Clear recorded failures data.
   */
  _clearData() {
    this._log.trace("_clearData()");
    this._failures = {};
  },

  /**
   * Clear and reset timeout.
   */
  _resetTimeout() {
    if (this._timeoutId) {
      Policy.clearSchedulerTickTimeout(this._timeoutId);
      this._timeoutId = null;
    }
  },

  /**
   * Submit latest ping on shutdown.
   * @returns {Promise} Test-only, resolved when the ping is stored or sent.
   */
  shutdown() {
    this._log.trace("shutdown()");
    this._resetTimeout();
    return this._submitPing(this.Reason.SHUT_DOWN);
  },

  /**
   * Test-only, restore to initial state.
   */
  testReset() {
    this._lastSendTime = -SEND_TICK_DELAY;
    this._clearData();
    this._resetTimeout();
  },

  get _log() {
    if (!this._logger) {
      this._logger = lazy.Log.repository.getLoggerWithMessagePrefix(
        LOGGER_NAME,
        LOGGER_PREFIX + "::"
      );
    }

    return this._logger;
  },
};

XPCOMUtils.defineLazyPreferenceGetter(
  TelemetryHealthPing,
  "enabled",
  TelemetryUtils.Preferences.HealthPingEnabled,
  true
);

[ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge