Bug 842393 - Documentation for Firefox Health Report; r=rnewman

DONTBUILD (docs only)
This commit is contained in:
Gregory Szorc 2013-02-18 16:11:50 -08:00
parent 49d70bf739
commit 173f54b831
4 changed files with 268 additions and 10 deletions

View File

@ -0,0 +1,28 @@
======================
Data Reporting Service
======================
This directory contains files related to data collection and reporting
within Gecko applications.
The important files in this directory are:
DataReportingService.js
An XPCOM service that coordinates collection and reporting of data.
policy.jsm
A module containing the logic for coordinating and driving collection
and upload of data.
sessions.jsm
Records Gecko application session history. This is loaded as part of
the XPCOM service because it needs to capture state from very early in
the application lifecycle. Bug 841561 tracks implementing this in C++.
There is other code in the tree that collects and uploads data. The
original intent of this directory and XPCOM service was to serve as a
focal point for the coordination of all this activity so that it could
all be done consistently and properly. This vision may or may not be fully
realized.

View File

@ -11,31 +11,49 @@ metrics and periodically submits them to a central server.
Implementation Notes
====================
The XPCOM service powering FHR is defined in HealthReportService.js. It
simply instantiates an instance of HealthReporter from healthreporter.jsm.
healthreporter.jsm contains the main interface for FHR, the
*HealthReporter* type. An instance of this is created by the data
reporting service. See /services/datareporting/.
All the logic for enforcing the privacy policy and for scheduling data
submissions lives in policy.jsm.
providers.jsm contains numerous Metrics.Provider and Metrics.Measurement
used for collecting application metrics. If you are looking for the FHR
probes, this is where they are.
Preferences
===========
Preferences controlling behavior of Firefox Health Report live in the
*healthreport.* branch.
*datareporting.healthreport.* branch.
Some important preferences are:
* **healthreport.serviceEnabled** - Controls whether the entire health report
* **service.enabled** - Controls whether the entire health report
service runs. The overall service performs data collection, storing, and
submission.
submission. This is the primary kill switch for Firefox Health Report
outside of the build system variable. i.e. if you are using an
official Firefox build and wish to disable FHR, this is what you
should change.
* **healthreport.policy.dataSubmissionEnabled** - Controls whether data
submission is enabled. If this is *false*, data will still be collected
and stored - it just won't ever be submitted to a remote server.
* **uploadEnabled** - Whether uploading of data is enabled. This
is the preference the checkbox in the UI reflects. If this is
disabled, FHR still collects data - it just doesn't upload it.
If the entire service is disabled, you lose data collection. This means that
data analysis won't be available because there is no data to analyze!
Registering Providers
=====================
Firefox Health Report providers are registered via the category manager.
See HealthReportComponents.manifest for providers defined in this
directory.
Essentially, the category manager receives the name of a JS type and the
URI of a JSM to import that exports this symbol. At run-time, the
providers registered in the category manager are instantiated. This
allows for a loose coupling of providers which in turns makes managing
the code behind the providers much simpler.
Other Notes
===========

109
services/metrics/README.rst Normal file
View File

@ -0,0 +1,109 @@
============================
Metrics Collection Framework
============================
This directory contains generic code for collecting and persisting
metrics data for Gecko applications.
Overview
========
Metrics by itself doesn't do much. It simply provides a framework for
collecting data. It is up to users of this framework to hook up the
individual components into a system that makes sense for their purposes.
An example consumer of Metrics is Firefox Health Report (see
/services/healthreport).
Relationship to Telemetry
-------------------------
Telemetry provides similar features to code in this directory. The two
may be unified in the future.
Type Overview
=============
This directory defines a number of JavaScript *types*/*classes*:
Metrics.Provider
An entity that collects and manages data. A provider is typically
domain-specific. e.g. AddonsProvider, SearchesProvider.
Metrics.Measurement
Represents a collection of related pieces/fields of data. Instances of
these are essentially data structure descriptors.
Metrics.Storage
Persistent SQLite-backed store for collected metrics data and state.
Metrics.Collector
High-level entity coordinating activity among several Metrics.Provider
instances.
SQLite Storage
==============
*Metrics.Storage* provides an interface for persisting metrics data to a
SQLite database.
The storage API organizes values by fields. A field is a named member of
a Measurement that has specific type and retention characteristics. Some
example field types include:
* Last text value
* Last numeric value for a given day
* Discrete text values for a given day
See storage.jsm for more.
While SQLite is used under the hood, this implementation detail is
hidden from the consumer.
Providers and Measurements
==========================
The most important types in this framework are *Metrics.Provider* and
*Metrics.Measurement*, henceforth known as *Provider* and *Measurement*,
respectively. As you will see, these two types go hand in hand.
A Provider is an entity that *provides* data about a specific subsystem
or feature. They do this by recording data to specific Measurement
types. Both Provider and Measurement are abstract base types.
A Measurement implementation defines a name and version. More
importantly, it also defines its storage requirements and how
previously-stored values are serialized.
Storage allocation is performed by communicating with the SQLite
backend. There is a startup function that tells SQLite what fields the
measurement is recording. The storage backend then registers these in
the database. Internally, this is creating a new primary key for
individual fields so later storage operations can directly reference
these primary keys in order to retrieve data without having to perform
complicated joins.
A Provider can be thought of as a collection of Measurement
implementations. e.g. an Addons provider may consist of a measurement
for all *current* add-ons as well as a separate measurement for
historical counts of add-ons. A provider's primary role is to take
metrics data and write it to various measurements. This effectively
persists the data to SQLite.
Data is emitted from providers in either a push or pull based mechanism.
In push-based scenarios, the provider likely subscribes to external
events (e.g. observer notifications). An event of interest can occur at
any time. When it does, the provider immediately writes the event of
interest to storage or buffers it for eventual writing. In pull-based
scenarios, the provider is periodically queried and asked to populate
data.
Usage
=====
To use the code in this directory, import Metrics.jsm. e.g.
Components.utils.import("resource://gre/modules/Metrics.jsm");
This exports a *Metrics* object which holds references to the main JS
types and functions provided by this feature.

View File

@ -135,10 +135,26 @@ Measurement.prototype = Object.freeze({
return this._serializers[format];
},
/**
* Whether this measurement contains the named field.
*
* @param name
* (string) Name of field.
*
* @return bool
*/
hasField: function (name) {
return this._fieldsByName.has(name);
},
/**
* The unique identifier for a named field.
*
* This will throw if the field is not known.
*
* @param name
* (string) Name of field.
*/
fieldID: function (name) {
let entry = this._fieldsByName.get(name);
@ -192,41 +208,123 @@ Measurement.prototype = Object.freeze({
return deferred.promise;
},
//---------------------------------------------------------------------------
// Data Recording Functions
//
// Functions in this section are used to record new values against this
// measurement instance.
//
// Generally speaking, these functions will throw if the specified field does
// not exist or if the storage function requested is not appropriate for the
// type of that field. These functions will also return a promise that will
// be resolved when the underlying storage operation has completed.
//---------------------------------------------------------------------------
/**
* Increment a daily counter field in this measurement by 1.
*
* By default, the counter for the current day will be incremented.
*
* If the field is not known or is not a daily counter, this will throw.
*
*
*
* @param field
* (string) The name of the field whose value to increment.
* @param date
* (Date) Day on which to increment the counter.
* @return Promise<>
*/
incrementDailyCounter: function (field, date=new Date()) {
return this.storage.incrementDailyCounterFromFieldID(this.fieldID(field),
date);
},
/**
* Record a new numeric value for a daily discrete numeric field.
*
* @param field
* (string) The name of the field to append a value to.
* @param value
* (Number) Number to append.
* @param date
* (Date) Day on which to append the value.
*
* @return Promise<>
*/
addDailyDiscreteNumeric: function (field, value, date=new Date()) {
return this.storage.addDailyDiscreteNumericFromFieldID(
this.fieldID(field), value, date);
},
/**
* Record a new text value for a daily discrete text field.
*
* This is like `addDailyDiscreteNumeric` but for daily discrete text fields.
*/
addDailyDiscreteText: function (field, value, date=new Date()) {
return this.storage.addDailyDiscreteTextFromFieldID(
this.fieldID(field), value, date);
},
/**
* Record the last seen value for a last numeric field.
*
* @param field
* (string) The name of the field to set the value of.
* @param value
* (Number) The value to set.
* @param date
* (Date) When this value was recorded.
*
* @return Promise<>
*/
setLastNumeric: function (field, value, date=new Date()) {
return this.storage.setLastNumericFromFieldID(this.fieldID(field), value,
date);
},
/**
* Record the last seen value for a last text field.
*
* This is like `setLastNumeric` except for last text fields.
*/
setLastText: function (field, value, date=new Date()) {
return this.storage.setLastTextFromFieldID(this.fieldID(field), value,
date);
},
/**
* Record the most recent value for a daily last numeric field.
*
* @param field
* (string) The name of a daily last numeric field.
* @param value
* (Number) The value to set.
* @param date
* (Date) Day on which to record the last value.
*
* @return Promise<>
*/
setDailyLastNumeric: function (field, value, date=new Date()) {
return this.storage.setDailyLastNumericFromFieldID(this.fieldID(field),
value, date);
},
/**
* Record the most recent value for a daily last text field.
*
* This is like `setDailyLastNumeric` except for a daily last text field.
*/
setDailyLastText: function (field, value, date=new Date()) {
return this.storage.setDailyLastTextFromFieldID(this.fieldID(field),
value, date);
},
//---------------------------------------------------------------------------
// End of data recording APIs.
//---------------------------------------------------------------------------
/**
* Obtain all values stored for this measurement.
*
@ -564,6 +662,11 @@ Provider.prototype = Object.freeze({
return this.storage.enqueueOperation(func);
},
/**
* Obtain persisted provider state.
*
* State is backend by storage.
*/
getState: function (key) {
let name = this.name;
let storage = this.storage;