Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  telemetry.js

  Sprache: JAVA
 

/* 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 is the telemetry module to report metrics for tools.
 *
 * Comprehensive documentation is in docs/frontend/telemetry.md
 */


"use strict";

const {
  getNthPathExcluding,
} = require("resource://devtools/shared/platform/stack.js");
const { TelemetryEnvironment } = ChromeUtils.importESModule(
  "resource://gre/modules/TelemetryEnvironment.sys.mjs"
);
const WeakMapMap = require("resource://devtools/client/shared/WeakMapMap.js");

// Object to be shared among all instances.
const PENDING_EVENT_PROPERTIES = new WeakMapMap();
const PENDING_EVENTS = new WeakMapMap();

/**
 * Instantiate a new Telemetry helper class.
 *
 * @param {object} options [optional]
 * @param {boolean} options.useSessionId [optional]
 *        If true, this instance will automatically generate a unique "sessionId"
 *        and use it to aggregate all records against this unique session.
 *        This helps aggregate all data coming from a single toolbox instance for ex.
 */

class Telemetry {
  constructor({ useSessionId = false } = {}) {
    // Note that native telemetry APIs expect a string
    this.sessionId = String(
      useSessionId ? parseInt(this.msSinceProcessStart(), 10) : -1
    );

    // Bind pretty much all functions so that callers do not need to.
    this.msSystemNow = this.msSystemNow.bind(this);
    this.recordEvent = this.recordEvent.bind(this);
    this.preparePendingEvent = this.preparePendingEvent.bind(this);
    this.addEventProperty = this.addEventProperty.bind(this);
    this.addEventProperties = this.addEventProperties.bind(this);
    this.toolOpened = this.toolOpened.bind(this);
    this.toolClosed = this.toolClosed.bind(this);
  }

  get osNameAndVersion() {
    const osInfo = TelemetryEnvironment.currentEnvironment.system.os;

    if (!osInfo) {
      return "Unknown OS";
    }

    let osVersion = `${osInfo.name} ${osInfo.version}`;

    if (osInfo.windowsBuildNumber) {
      osVersion += `.${osInfo.windowsBuildNumber}`;
    }

    return osVersion;
  }

  /**
   * Time since the system wide epoch. This is not a monotonic timer but
   * can be used across process boundaries.
   */

  msSystemNow() {
    return Services.telemetry.msSystemNow();
  }

  /**
   * The number of milliseconds since process start using monotonic
   * timestamps (unaffected by system clock changes).
   */

  msSinceProcessStart() {
    return Services.telemetry.msSinceProcessStart();
  }

  /**
   * Telemetry events often need to make use of a number of properties from
   * completely different codepaths. To make this possible we create a
   * "pending event" along with an array of property names that we need to wait
   * for before sending the event.
   *
   * As each property is received via addEventProperty() we check if all
   * properties have been received. Once they have all been received we send the
   * telemetry event.
   *
   * @param {object} obj
   *        The telemetry event or ping is associated with this object, meaning
   *        that multiple events or pings for the same histogram may be run
   *        concurrently, as long as they are associated with different objects.
   * @param {string} method
   *        The telemetry event method (describes the type of event that
   *        occurred e.g. "open")
   * @param {string} object
   *        The telemetry event object name (the name of the object the event
   *        occurred on) e.g. "tools" or "setting"
   * @param {string | null} value
   *        The telemetry event value (a user defined value, providing context
   *        for the event) e.g. "console"
   * @param {Array} expected
   *        An array of the properties needed before sending the telemetry
   *        event e.g.
   *        [
   *          "host",
   *          "width"
   *        ]
   */

  preparePendingEvent(obj, method, object, value, expected = []) {
    const sig = `${method},${object},${value}`;

    if (expected.length === 0) {
      throw new Error(
        `preparePendingEvent() was called without any expected ` +
          `properties.\n` +
          `CALLER: ${getCaller()}`
      );
    }

    const data = {
      extra: {},
      expected: new Set(expected),
    };

    PENDING_EVENTS.set(obj, sig, data);

    const props = PENDING_EVENT_PROPERTIES.get(obj, sig);
    if (props) {
      for (const [name, val] of Object.entries(props)) {
        this.addEventProperty(obj, method, object, value, name, val);
      }
      PENDING_EVENT_PROPERTIES.delete(obj, sig);
    }
  }

  /**
   * Adds an expected property for either a current or future pending event.
   * This means that if preparePendingEvent() is called before or after sending
   * the event properties they will automatically added to the event.
   *
   * @param {object} obj
   *        The telemetry event or ping is associated with this object, meaning
   *        that multiple events or pings for the same histogram may be run
   *        concurrently, as long as they are associated with different objects.
   * @param {string} method
   *        The telemetry event method (describes the type of event that
   *        occurred e.g. "open")
   * @param {string} object
   *        The telemetry event object name (the name of the object the event
   *        occurred on) e.g. "tools" or "setting"
   * @param {string | null} value
   *        The telemetry event value (a user defined value, providing context
   *        for the event) e.g. "console"
   * @param {string} pendingPropName
   *        The pending property name
   * @param {string} pendingPropValue
   *        The pending property value
   */

  addEventProperty(
    obj,
    method,
    object,
    value,
    pendingPropName,
    pendingPropValue
  ) {
    const sig = `${method},${object},${value}`;
    const events = PENDING_EVENTS.get(obj, sig);

    // If the pending event has not been created add the property to the pending
    // list.
    if (!events) {
      const props = PENDING_EVENT_PROPERTIES.get(obj, sig);

      if (props) {
        props[pendingPropName] = pendingPropValue;
      } else {
        PENDING_EVENT_PROPERTIES.set(obj, sig, {
          [pendingPropName]: pendingPropValue,
        });
      }
      return;
    }

    const { expected, extra } = events;

    if (expected.has(pendingPropName)) {
      extra[pendingPropName] = pendingPropValue;

      if (expected.size === Object.keys(extra).length) {
        this._sendPendingEvent(obj, method, object, value);
      }
    } else {
      // The property was not expected, warn and bail.
      throw new Error(
        `An attempt was made to add the unexpected property ` +
          `"${pendingPropName}" to a telemetry event with the ` +
          `signature "${sig}"\n` +
          `CALLER: ${getCaller()}`
      );
    }
  }

  /**
   * Adds expected properties for either a current or future pending event.
   * This means that if preparePendingEvent() is called before or after sending
   * the event properties they will automatically added to the event.
   *
   * @param {object} obj
   *        The telemetry event or ping is associated with this object, meaning
   *        that multiple events or pings for the same histogram may be run
   *        concurrently, as long as they are associated with different objects.
   * @param {string} method
   *        The telemetry event method (describes the type of event that
   *        occurred e.g. "open")
   * @param {string} object
   *        The telemetry event object name (the name of the object the event
   *        occurred on) e.g. "tools" or "setting"
   * @param {string | null} value
   *        The telemetry event value (a user defined value, providing context
   *        for the event) e.g. "console"
   * @param {string} pendingObject
   *        An object containing key, value pairs that should be added to the
   *        event as properties.
   */

  addEventProperties(obj, method, object, value, pendingObject) {
    for (const [key, val] of Object.entries(pendingObject)) {
      this.addEventProperty(obj, method, object, value, key, val);
    }
  }

  /**
   * A private method that is not to be used externally. This method is used to
   * prepare a pending telemetry event for sending and then send it via
   * recordEvent().
   *
   * @param {object} obj
   *        The telemetry event or ping is associated with this object, meaning
   *        that multiple events or pings for the same histogram may be run
   *        concurrently, as long as they are associated with different objects.
   * @param {string} method
   *        The telemetry event method (describes the type of event that
   *        occurred e.g. "open")
   * @param {string} object
   *        The telemetry event object name (the name of the object the event
   *        occurred on) e.g. "tools" or "setting"
   * @param {string | null} value
   *        The telemetry event value (a user defined value, providing context
   *        for the event) e.g. "console"
   */

  _sendPendingEvent(obj, method, object, value) {
    const sig = `${method},${object},${value}`;
    const { extra } = PENDING_EVENTS.get(obj, sig);

    PENDING_EVENTS.delete(obj, sig);
    PENDING_EVENT_PROPERTIES.delete(obj, sig);
    this.recordEvent(method, object, value, extra);
  }

  /**
   * Send a telemetry event.
   *
   * @param {string} method
   *        The telemetry event method (describes the type of event that
   *        occurred e.g. "open")
   * @param {string} object
   *        The telemetry event object name (the name of the object the event
   *        occurred on) e.g. "tools" or "setting"
   * @param {string | null} [value]
   *        Optional telemetry event value (a user defined value, providing
   *        context for the event) e.g. "console"
   * @param {object} [extra]
   *        Optional telemetry event extra object containing the properties that
   *        will be sent with the event e.g.
   *        {
   *          host: "bottom",
   *          width: "1024"
   *        }
   */

  recordEvent(method, object, value = null, extra = null) {
    // Using the Glean API directly insteade of doing string manipulations
    // would be better. See bug 1921793.
    const eventName = `${method}_${object}`.replace(/(_[a-z])/g, c =>
      c[1].toUpperCase()
    );

    if (extra) {
      extra = Telemetry.sanitizeEventExtras(extra, `devtoolsMain.${eventName}`);
    } else {
      extra = {};
    }

    // Automatically flag the record with the session ID
    // if the current Telemetry instance relates to a toolbox
    // so that data can be aggregated per toolbox instance.
    // Note that we also aggregate data per about:debugging instance.
    extra.session_id = this.sessionId;

    if (value !== null) {
      extra.value = value;
    }

    Glean.devtoolsMain[eventName]?.record(extra);
  }

  /**
   * Sanitize all extra keys intended to be used with a Glean event.
   * All values will be converted to string and capped to 80 characters by
   * default. Will return a copy of the object with sanitized values.
   *
   * @param {object} extras
   *        The extras object to sanitize.
   * @param {string} eventName
   *        The name of the Glean event (used for logging purposes).
   * @param {object=} options
   * @param {number} options.limit
   *        Optional maximum size (in bytes) for each value. Defaults to 80.
   * @returns {object}
   *          The sanitized extras.
   */

  static sanitizeEventExtras(extras, eventName, options = {}) {
    const { limit = 80 } = options;
    if (limit > 500) {
      // Glean event extra values can contain up to 500 bytes.
      // https://mozilla.github.io/glean/book/reference/metrics/event.html?highlight=extra_keys#recorded-errors
      throw new Error(
        `Expected "options.limit" to be a number <= 500, got ${limit}`
      );
    }

    const sanitized = {};
    for (let [name, value] of Object.entries(extras)) {
      // Only string values are allowed so cast all values to strings.
      value = value + "";

      if (value.length > limit) {
        dump(
          `Glean event "${eventName}" extra key "${name}" cropped to ${limit} characters\nCALLER: ${getCaller()}\n`
        );
        value = value.substring(0, limit);
      }

      sanitized[name] = value;
    }

    return sanitized;
  }

  /**
   * Sends telemetry pings to indicate that a tool has been opened.
   *
   * @param {string} id
   *        The ID of the tool opened.
   * @param {object} obj
   *        The telemetry event or ping is associated with this object, meaning
   *        that multiple events or pings for the same histogram may be run
   *        concurrently, as long as they are associated with different objects.
   *
   * NOTE: This method is designed for tools that send multiple probes on open,
   *       one of those probes being a counter and the other a timer. If you
   *       only have one probe you should be using another method.
   */

  toolOpened(id, obj) {
    const charts = getChartsFromToolId(id);

    if (!charts) {
      return;
    }

    if (charts.useTimedEvent) {
      this.preparePendingEvent(obj, "tool_timer", id, null, [
        "os",
        "time_open",
      ]);
      this.addEventProperty(
        obj,
        "tool_timer",
        id,
        null,
        "time_open",
        this.msSystemNow()
      );
    }
    if (charts.gleanTimingDist) {
      if (!obj._timerIDs) {
        obj._timerIDs = new Map();
      }
      if (!obj._timerIDs.has(id)) {
        obj._timerIDs.set(id, charts.gleanTimingDist.start());
      }
    }
    if (charts.gleanCounter) {
      charts.gleanCounter.add(1);
    }
  }

  /**
   * Sends telemetry pings to indicate that a tool has been closed.
   *
   * @param {string} id
   *        The ID of the tool opened.
   * @param {object} obj
   *        The telemetry event or ping is associated with this object, meaning
   *        that multiple events or pings for the same histogram may be run
   *        concurrently, as long as they are associated with different objects.
   *
   * NOTE: This method is designed for tools that send multiple probes on open,
   *       one of those probes being a counter and the other a timer. If you
   *       only have one probe you should be using another method.
   */

  toolClosed(id, obj) {
    const charts = getChartsFromToolId(id);

    if (!charts) {
      return;
    }

    if (charts.useTimedEvent) {
      const sig = `tool_timer,${id},null`;
      const event = PENDING_EVENTS.get(obj, sig);
      const time = this.msSystemNow() - event.extra.time_open;

      this.addEventProperties(obj, "tool_timer", id, null, {
        time_open: time,
        os: this.osNameAndVersion,
      });
    }

    if (charts.gleanTimingDist && obj._timerIDs) {
      const timerID = obj._timerIDs.get(id);
      if (timerID) {
        charts.gleanTimingDist.stopAndAccumulate(timerID);
        obj._timerIDs.delete(id);
      }
    }
  }
}

/**
 * Returns the telemetry charts for a specific tool.
 *
 * @param {string} id
 *        The ID of the tool that has been opened.
 */

// eslint-disable-next-line complexity
function getChartsFromToolId(id) {
  if (!id) {
    return null;
  }

  let useTimedEvent = null;
  let gleanCounter = null;
  let gleanTimingDist = null;

  if (id === "performance") {
    id = "jsprofiler";
  }

  switch (id) {
    case "aboutdebugging":
    case "browserconsole":
    case "dom":
    case "inspector":
    case "jsbrowserdebugger":
    case "jsdebugger":
    case "jsprofiler":
    case "memory":
    case "netmonitor":
    case "options":
    case "responsive":
    case "storage":
    case "styleeditor":
    case "toolbox":
    case "webconsole":
      gleanTimingDist = Glean.devtools[`${id}TimeActive`];
      gleanCounter = Glean.devtools[`${id}OpenedCount`];
      break;
    case "accessibility":
      gleanTimingDist = Glean.devtools.accessibilityTimeActive;
      gleanCounter = Glean.devtoolsAccessibility.openedCount;
      break;
    case "accessibility_picker":
      gleanTimingDist = Glean.devtools.accessibilityPickerTimeActive;
      gleanCounter = Glean.devtoolsAccessibility.pickerUsedCount;
      break;
    case "changesview":
      gleanTimingDist = Glean.devtools.changesviewTimeActive;
      gleanCounter = Glean.devtoolsChangesview.openedCount;
      break;
    case "animationinspector":
    case "compatibilityview":
    case "computedview":
    case "fontinspector":
    case "layoutview":
    case "ruleview":
      useTimedEvent = true;
      gleanTimingDist = Glean.devtools[`${id}TimeActive`];
      gleanCounter = Glean.devtools[`${id}OpenedCount`];
      break;
    case "flexbox_highlighter":
      gleanTimingDist = Glean.devtools.flexboxHighlighterTimeActive;
      break;
    case "grid_highlighter":
      gleanTimingDist = Glean.devtools.gridHighlighterTimeActive;
      break;
    default:
      gleanTimingDist = Glean.devtools.customTimeActive;
      gleanCounter = Glean.devtools.customOpenedCount;
  }

  return {
    useTimedEvent,
    gleanCounter,
    gleanTimingDist,
  };
}

/**
 * Displays the first caller and calling line outside of this file in the
 * event of an error. This is the line that made the call that produced the
 * error.
 */

function getCaller() {
  return getNthPathExcluding(0"/telemetry.js");
}

module.exports = Telemetry;

Messung V0.5 in Prozent
C=92 H=83 G=87

¤ Dauer der Verarbeitung: 0.12 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002