summaryrefslogtreecommitdiffstats
path: root/toolkit/components/glean/docs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/glean/docs')
-rw-r--r--toolkit/components/glean/docs/dev/builtin_pings.md13
-rw-r--r--toolkit/components/glean/docs/dev/code_organization.md56
-rw-r--r--toolkit/components/glean/docs/dev/images/fog-modules.svg6
-rw-r--r--toolkit/components/glean/docs/dev/index.md15
-rw-r--r--toolkit/components/glean/docs/dev/ipc.md184
-rw-r--r--toolkit/components/glean/docs/dev/jog.md91
-rw-r--r--toolkit/components/glean/docs/dev/local_glean.md100
-rw-r--r--toolkit/components/glean/docs/dev/new_metric_types.md285
-rw-r--r--toolkit/components/glean/docs/dev/preferences.md105
-rw-r--r--toolkit/components/glean/docs/dev/storage.md14
-rw-r--r--toolkit/components/glean/docs/dev/style_guide.md49
-rw-r--r--toolkit/components/glean/docs/dev/testing.md240
-rw-r--r--toolkit/components/glean/docs/dev/updating_parser.md52
-rw-r--r--toolkit/components/glean/docs/dev/updating_sdk.md48
-rw-r--r--toolkit/components/glean/docs/index.md30
-rw-r--r--toolkit/components/glean/docs/user/geckoview_streaming_migration.md262
-rw-r--r--toolkit/components/glean/docs/user/getting_started.md97
-rw-r--r--toolkit/components/glean/docs/user/gifft.md241
-rw-r--r--toolkit/components/glean/docs/user/index.md17
-rw-r--r--toolkit/components/glean/docs/user/instrumentation_tests.md278
-rw-r--r--toolkit/components/glean/docs/user/migration.md909
-rw-r--r--toolkit/components/glean/docs/user/new_definitions_file.md116
22 files changed, 3208 insertions, 0 deletions
diff --git a/toolkit/components/glean/docs/dev/builtin_pings.md b/toolkit/components/glean/docs/dev/builtin_pings.md
new file mode 100644
index 0000000000..cbfff8c559
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/builtin_pings.md
@@ -0,0 +1,13 @@
+# Built-in Pings
+
+FOG embeds the Glean SDK so
+[its documentation on pings is authoritative](https://mozilla.github.io/glean/book/user/pings/index.html).
+The only detail FOG adds is to clarify
+[the "baseline" ping's schedule](https://mozilla.github.io/glean/book/user/pings/baseline.html#scheduling).
+Specifically, in Firefox Desktop, the application is considered
+* "active" when started,
+ or when a user interacts with Firefox after a 20min period of inactivity,
+* "inactive" after the user stops interacting with Firefox after 2min of activity.
+
+For more details about why, see the bug tree around
+[bug 1635242](https://bugzilla.mozilla.org/show_bug.cgi?id=1635242).
diff --git a/toolkit/components/glean/docs/dev/code_organization.md b/toolkit/components/glean/docs/dev/code_organization.md
new file mode 100644
index 0000000000..e943dbfd50
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/code_organization.md
@@ -0,0 +1,56 @@
+# FOG code organization
+
+![Modules of Project FOG](images/fog-modules.svg)
+
+The diagram shows the different modules of Project FOG.
+
+## FOG control
+
+This module is the glue between Firefox and Glean.
+
+* The code lives in `toolkit/components/glean/src`.
+* It is written in Rust.
+* The crate is named `fog_control`.
+* It is not published to crates.io.
+* It is not consumed by other Rust crates inside mozilla-central.
+
+This module is responsible for
+
+* collecting and assembling the [client information](https://mozilla.github.io/glean/book/user/pings/index.html#the-client_info-section)
+* configuring the Glean SDK via the Rust Language Binding
+* watching the Firefox Telemetry data upload preference (`datareporting.healthreport.uploadEnabled`)
+* scheduling builtin pings
+* controling ping upload workers
+* passing IPC buffers
+
+It calls into `glean` (the Glean SDK Rust Language Binding) to:
+
+* configure and initialize Glean
+* toggle `upload_enabled`
+* get upload tasks
+
+It calls into `fog` to:
+
+* pass IPC buffers
+* record to its own metrics
+
+## FOG API
+
+This module provides the user-facing API for Glean inside mozilla-central.
+
+* The code lives in `toolkit/components/glean/api`.
+* It is written in Rust.
+* The crate is named `fog`.
+* It is not published to crates.io.
+* It can be consumed by other Rust crates inside mozilla-central for their Glean usage.
+
+This module is responsible for
+
+* exposing a specific metric API in Rust
+* wrapping metric implementations for handling IPC
+* exposing FFI functionality to implement other language APIs on top.
+ See also [Adding a New Metric Type](new_metric_types.md).
+
+It calls into `glean` (the Glean SDK Rust Language Binding) for:
+
+* metric types (including pings)
diff --git a/toolkit/components/glean/docs/dev/images/fog-modules.svg b/toolkit/components/glean/docs/dev/images/fog-modules.svg
new file mode 100644
index 0000000000..c0271e9f6e
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/images/fog-modules.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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/. -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(255, 255, 255);" version="1.1" width="601px" height="511px" viewBox="-0.5 -0.5 601 511" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-03-27T09:36:49.445Z&quot; agent=&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:76.0) Gecko/20100101 Firefox/76.0&quot; etag=&quot;_te5ruI7nKv5hlBG0WM7&quot; version=&quot;12.9.3&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;02tFoor6QwIjLFNeosYU&quot; name=&quot;Page-1&quot;&gt;7Vtbd6M2EP41fkyPQYDxo+NcmnbbpOvu2eZRBtmmxYgVsmP311eAZC6SDU4AX5ok5wSNLsA334xGI9ED4+XmkcBw8Rt2kd/T++6mB+56uq4NLYP9iyVbLtHsYSqZE8/lskww8f5FXNjn0pXnoqjQkGLsUy8sCh0cBMihBRkkBL8Vm82wX7xrCOdIEkwc6MvS755LF6nUNvuZ/GfkzRfizlqf1yyhaMwF0QK6+C0nAvc9MCYY0/RquRkjP0ZP4JL2e9hTu3swggJap0P/YUNGP0L/5QvY+N8XzwT/Y93wUdbQX/EX5g9LtwIBgleBi+JB+j1w+7bwKJqE0Ilr35jSmWxBlz4raexy5vn+GPuYJH3BbDbTHYfJI8puh3I1rjW1TIvVyK8hngkRijY5EX+tR4SXiJItayJqBcScZBYvvmUKs4RaFnlliX6Qk2S+GzrDkV1wKI+AVW8XVtdEtmuoYLX1KbBaglWza+IKQFu4AgWuls9ue+t66/iGvjcPkgrrxyo2rFuSPNauyK7m/H/SbYYZPHm9iIZxxU2UuKMRa6BZ4UYe5Q6tkY9DRGLPAh0vmItx2dulQxdvx8TJgwppiRNMMbSo+KKCAxygEhu4SLx4+rrgNtayx7zYiMuXnuvGd1ESrUjFBphj2pUGqRkq4rTFG6OCN0frv2umPQUUkQD60SfBEoINq12TBrpkmGZKYCOXRRK8iAld4DlmGrzPpCVcsjZfMA65fv5GlG55WARXFBe1hzYe/Svu/pPJS6+5mrsNHzkpbEUhYO+b6xQXX/N1WbektK1SW4RXxEEHsLF43AbJHPGuT9/MP23zj8lo8818CbSvweDGFsFIjNtBEhDkQ+qtixGaSqNJ1xEhcJtrEGIvoFFu5JdYkHELDErcGpbiqlJ73TjYnl2kT5Bxa/cq76ebJTk0Ns5t8tdk+GY7SB2+TW3TMBsyZqAVARwqbFlX2LLVli0PJXB/gWs4cYgX0qNdah6sffAK5+qg2M2f3Lsa9ukUonQMxqdv3Tvv6BflXM3DzvI8nKvAtJ4DOMrBXrh1quGyPs1zLzYiZ5UzT3VDcBb2qfdL9jaosM/D7VuyT1XWKrecSxc/X1cRrbks+lAuBiJ7poyRLMdG01lDuRjj3IxeTsU8PD8ywejlieEMlzFqwTQK08Vpqdy/AhXs1pInC4tqZBk7cMTvd44CrErnqDcevHwsu9vfw/0xZtMG9q+A3IZxagejy07+0UcwYKLJ3a8XCLE+PDuIZQeSTpoXh+1uG6IKy/Z2fOT58HIzMlYpqqs707WHrimh+/Qyzr3mg4zyAi+nq6iTJVk5g7UDIgeYrcDLbg2vQY3QIHBH8QY5Kzk+jCLPKS2w3rlYes/C7AMxhNl0DJHTmanQmZAdtw6TFk5GycTMspdP35z3OrACM8zSQFppoBQZaaCmFmO63STVCkTLeFdBNS1HtIx2TVNNLHmqqWZ+Uq0VqsmJ+df7icS2j29ynkkeHpTw3k0zOd6ottFbm4ZFHJBTwO/PV4x/KWLXFRF7t/i3fVzLjH+VwXry00tPK+Tk6U87iW1V2KmrNvV3ScjmAa+TYmk7juogihKoVk5toG6a+n8ytR29/1V2KVYH+XJwmg2azjdbQN0ADRg1WdxNPhHIuQNKYBDNkoOFKHCwmxwtvLi0jAlKbB/IHr3bRAKwT2IKF7FXKayi2nzqrm86Mh/5dGeaHIrY1NxsfNRJ9q1sNaqwv1urMcCn1VQZQ7XVDM/LavalVAly1tdgNapD591aTY3V2lnvzzbP2D3Bdyl9bgy6Cb6NisMt+56r3WBdTnLlgkEXXU0waJx8z86QV/epC/ThNkb7WvNaVo2TLIOWFHHobKriPFftr2Y01VczsS7T80jqb2YubpLbqUBorr2MMCtm36amzi37xBfc/wc=&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><rect x="0" y="60" width="600" height="120" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/><rect x="0" y="180" width="600" height="330" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><rect x="460" y="60" width="140" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-end; width: 138px; height: 1px; padding-top: 75px; margin-left: 460px;"><div style="box-sizing: border-box; font-size: 0; text-align: right; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div align="right"><font style="font-size: 16px">Developer facing</font></div></div></div></div></foreignObject><text x="598" y="79" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="end">Developer facing</text></switch></g><rect x="470" y="180" width="130" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-end; width: 128px; height: 1px; padding-top: 195px; margin-left: 470px;"><div style="box-sizing: border-box; font-size: 0; text-align: right; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div style="font-size: 16px" align="right"><font style="font-size: 16px">Internals</font></div></div></div></div></foreignObject><text x="598" y="199" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="end">Internals</text></switch></g><path d="M 250 150 L 250 190 L 120 190 L 120 203.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 120 208.88 L 116.5 201.88 L 120 203.63 L 123.5 201.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="190" y="90" width="120" height="60" fill="#f8cecc" stroke="#b85450" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 120px; margin-left: 191px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">C++</div></div></div></foreignObject><text x="250" y="124" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">C++</text></switch></g><rect x="360" y="90" width="120" height="60" fill="#f8cecc" stroke="#b85450" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 120px; margin-left: 361px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">JavaScript</div></div></div></foreignObject><text x="420" y="124" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">JavaScript</text></switch></g><path d="M 420 150 L 420 190 L 120 190 L 120 203.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 120 208.88 L 116.5 201.88 L 120 203.63 L 123.5 201.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="360" y="90" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 120px; margin-left: 361px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">JavaScript</div></div></div></foreignObject><text x="420" y="124" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">JavaScript</text></switch></g><path d="M 80 150 L 80 170 L 80 223.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 80 228.88 L 76.5 221.88 L 80 223.63 L 83.5 221.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="20" y="90" width="120" height="60" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 120px; margin-left: 21px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div>Rust</div></div></div></div></foreignObject><text x="80" y="124" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Rust</text></switch></g><rect x="20" y="230" width="120" height="60" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 260px; margin-left: 21px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">FOG API   </div></div></div></foreignObject><text x="80" y="264" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">FOG API   </text></switch></g><path d="M 140 470 L 160 470 L 150 470 L 163.63 470" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 168.88 470 L 161.88 473.5 L 163.63 470 L 161.88 466.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="20" y="440" width="120" height="60" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 470px; margin-left: 21px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">FOG Control</div></div></div></foreignObject><text x="80" y="474" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">FOG Control</text></switch></g><rect x="170" y="440" width="120" height="60" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 470px; margin-left: 171px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Glean SDK</div></div></div></foreignObject><text x="230" y="474" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Glean SDK</text></switch></g><rect x="480" y="0" width="120" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 10px; margin-left: 481px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Rust</div></div></div></foreignObject><text x="540" y="14" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Rust</text></switch></g><rect x="480" y="30" width="120" height="20" fill="#f8cecc" stroke="#b85450" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 40px; margin-left: 481px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">C++</div></div></div></foreignObject><text x="540" y="44" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">C++</text></switch></g><path d="M 230 220 L 270 260 L 230 300 L 190 260 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 260px; margin-left: 191px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">IPC parent?</div></div></div></foreignObject><text x="230" y="264" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">IPC parent?</text></switch></g><path d="M 230 300 L 230 433.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 230 438.88 L 226.5 431.88 L 230 433.63 L 233.5 431.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 140 260 L 183.63 260" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 188.88 260 L 181.88 263.5 L 183.63 260 L 181.88 256.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="230" y="310" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 320px; margin-left: 231px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">YES</div></div></div></foreignObject><text x="250" y="324" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">YES</text></switch></g><rect x="270" y="240" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 250px; margin-left: 271px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">NO</div></div></div></foreignObject><text x="290" y="254" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">NO</text></switch></g><rect x="360" y="230" width="230" height="200" fill="#f5f5f5" stroke="#666666" pointer-events="all"/><path d="M 270 260 L 460 260 Q 470 260 470 261.82 L 470 263.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 470 268.88 L 466.5 261.88 L 470 263.63 L 473.5 261.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 470 290 L 470 310 L 470 290 L 470 303.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 470 308.88 L 466.5 301.88 L 470 303.63 L 473.5 301.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="410" y="270" width="120" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 280px; margin-left: 411px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">transfer encoding</div></div></div></foreignObject><text x="470" y="284" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">transfer encoding</text></switch></g><path d="M 470 330 L 470 353.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 470 358.88 L 466.5 351.88 L 470 353.63 L 473.5 351.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="410" y="310" width="120" height="20" fill="#f8cecc" stroke="#b85450" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 320px; margin-left: 411px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">IPC send</div></div></div></foreignObject><text x="470" y="324" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">IPC send</text></switch></g><path d="M 470 380 L 470 393.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 470 398.88 L 466.5 391.88 L 470 393.63 L 473.5 391.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="410" y="360" width="120" height="20" fill="#f8cecc" stroke="#b85450" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 370px; margin-left: 411px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">IPC recv</div></div></div></foreignObject><text x="470" y="374" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">IPC recv</text></switch></g><path d="M 470 420 L 470 470 L 296.37 470" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 291.12 470 L 298.12 466.5 L 296.37 470 L 298.12 473.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="410" y="400" width="120" height="20" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 410px; margin-left: 411px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">transfer decoding</div></div></div></foreignObject><text x="470" y="414" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">transfer decoding</text></switch></g><rect x="520" y="230" width="70" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 240px; margin-left: 521px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">IPC layer</div></div></div></foreignObject><text x="555" y="244" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">IPC layer</text></switch></g><rect x="100" y="210" width="40" height="20" fill="#f8cecc" stroke="#b85450" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 220px; margin-left: 101px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><font style="font-size: 11px">C API</font></div></div></div></foreignObject><text x="120" y="224" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">C API</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg> \ No newline at end of file
diff --git a/toolkit/components/glean/docs/dev/index.md b/toolkit/components/glean/docs/dev/index.md
new file mode 100644
index 0000000000..334f040ed9
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/index.md
@@ -0,0 +1,15 @@
+# Developing Firefox on Glean
+
+This section of docs is designed to be helpful to people developing FOG.
+If you're not touching code, tests, or documentation in `toolkit/components/glean`,
+and you're not interested in implementation details, you probably want
+[the user docs instead](../user/index).
+
+```{toctree}
+:titlesonly:
+:maxdepth: 1
+:glob:
+
+*
+Glean SDK Source <https://github.com/mozilla/glean/>
+```
diff --git a/toolkit/components/glean/docs/dev/ipc.md b/toolkit/components/glean/docs/dev/ipc.md
new file mode 100644
index 0000000000..1d6bf5366d
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/ipc.md
@@ -0,0 +1,184 @@
+# Inter-process Communication (IPC)
+
+Firefox Desktop is a multi-process desktop application.
+Code requiring instrumentation may be on any of its processes,
+so FOG provide facilities to do just that.
+
+## Design
+
+The IPC Design of FOG was worked out in
+[bug 1618253](https://bugzilla.mozilla.org/show_bug.cgi?id=1618253).
+
+It centred around a few specific concepts:
+
+### Forbidding Non-Commutative Operations
+
+Because we cannot nicely impose a canonical ordering of metric operations across all processes,
+FOG forbids non-[commutative](https://en.wikipedia.org/wiki/Commutative_property)
+metric operations in some circumstances.
+
+For example,
+`Add()`-ing to a Counter metric works from multiple processes because the order doesn't matter.
+However, given a String metric being `Set()` from multiple processes simultaneously,
+which value should it take?
+
+This ambiguity is not a good foundation to build trust on,
+so we forbid setting a String metric from multiple processes.
+
+#### List of Forbidden Operations
+
+* Boolean's `set` (this is the metric type's only operation)
+* Labeled Boolean's `set` (this is the metric type's only operation)
+* String's `set` (this is the metric type's only operation)
+* Labeled String's `set` (this is the metric type's only operation)
+* String List's `set`
+ * `add` is permitted (order and uniqueness are not guaranteed)
+* Timespan's `start`, `stop`, and `cancel` (these are the metric type's only operations)
+* UUID's `set` and `generateAndSet` (these are the metric type's only operations)
+* Datetime's `set` (this is the metric type's only operation)
+* Quantity's `set` (this is the metric type's only operation)
+
+This list may grow over time as new metric types are added.
+If there's an operation/metric type on this list that you need to use in a non-parent process,
+please reach out
+[on the #glean channel](https://chat.mozilla.org/#/room/#glean:mozilla.org)
+and we'll help you out.
+
+### Process Agnosticism
+
+For metric types that can be used cross-process,
+FOG provides no facility for identifying which process the instrumentation is on.
+
+What this means is that if you accumulate to a
+[Timing Distribution](https://mozilla.github.io/glean/book/user/metrics/timing_distribution.html)
+in multiple processes,
+all the samples from all the processes will be combined in the same metric.
+
+If you wish to distinguish samples from different process types,
+you will need multiple metrics and inline code to select the proper one for the given process.
+For example:
+
+```C++
+if (XRE_GetProcessType() == GeckoProcessType_Default) {
+ mozilla::glean::performance::cache_size.Accumulate(numBytes / 1024);
+} else {
+ mozilla::glean::performance::non_main_process_cache_size.Accumulate(numBytes / 1024);
+}
+```
+
+### Scheduling
+
+FOG makes no guarantee about when non-main-process metric values are sent across IPC.
+FOG will try its best to schedule opportunistically in idle moments,
+and during orderly shutdowns.
+
+There are a few cases where we provide more firm guarantees:
+
+#### Tests
+
+There are test-only APIs in Rust, C++, and Javascript.
+These do not await a flush of child process metric values.
+You can use the test-only method `testFlushAllChildren` on the `FOG`
+XPCOM component to await child data's arrival:
+```js
+await Services.fog.testFlushAllChildren();
+```
+See [the test documentation](testing.md) for more details on testing FOG.
+For writing tests about instrumentation, see
+[the instrumentation test documentation](../user/instrumentation_tests).
+
+#### Pings
+
+We do not guarantee that non-main-process data has made it into a specific ping.
+
+[Built-in pings](https://mozilla.github.io/glean/book/user/pings/index.html)
+are submitted by the Rust Glean SDK at times FOG doesn't directly control,
+so there may be data not present in the parent process when a built-in ping is submitted.
+We don't anticipate this causing a problem since child-process data that
+"misses" a given ping will be included in the next one.
+
+At this time,
+[Custom Pings](https://mozilla.github.io/glean/book/user/pings/custom.html)
+must be sent in the parent process and have no mechanism
+to schedule their submission for after child-process data arrives in the parent process.
+[bug 1732118](https://bugzilla.mozilla.org/show_bug.cgi?id=1732118)
+tracks the addition of such a mechanism or guarantee.
+
+#### Shutdown
+
+We will make a best effort during an orderly shutdown to flush all pending data in child processes.
+This means a disorderly shutdown (usually a crash)
+may result in child process data being lost.
+
+#### Size
+
+We don't measure or keep an up-to-date calculation of the size of the IPC Payload.
+We do, however, keep a count of the number of times the IPC Payload has been accessed.
+This is used as a (very) conservative estimate of the size of the IPC Payload so we do not exceed the
+[IPC message size limit](https://searchfox.org/mozilla-central/search?q=kMaximumMessageSize).
+
+See [bug 1745660](https://bugzilla.mozilla.org/show_bug.cgi?id=1745660).
+
+### Mechanics
+
+The rough design is that the parent process can request an immediate flush of pending data,
+and each child process can decide to flush its pending data whenever it wishes.
+The former is via `FlushFOGData() returns (ByteBuf)` and the latter via `FOGData(ByteBuf)`.
+
+Pending Data is a buffer of bytes generated by `bincode` in Rust in the Child,
+handed off to C++, passed over IPC,
+then given back to `bincode` in Rust on the Parent.
+
+Rust is then responsible for turning the pending data into
+[metrics API][glean-metrics] calls on the metrics in the parent process.
+
+#### Supported Process Types
+
+FOG supports messaging between the following types of child process and the parent process:
+* content children (via `PContent`
+ (for now. See [bug 1641989](https://bugzilla.mozilla.org/show_bug.cgi?id=1641989))
+* gmp children (via `PGMP`)
+* gpu children (via `PGPU`)
+* rdd children (via `PRDD`)
+* socket children (via `PSocketProcess`)
+* utility children (via `PUtilityProcess`)
+
+See
+[the process model docs](/dom/ipc/process_model.rst)
+for more information about what that means.
+
+### Adding Support for a new Process Type
+
+Adding support for a new process type is a matter of extending the two messages
+mentioned above in "Mechanics" to another process type's protocol (ipdl file).
+
+1. Add two messages to the appropriate sections in `P<ProcessType>.ipdl`
+ * (( **Note:** `PGPU` _should_ be the only ipdl where `parent`
+ means the non-parent/-main/-UI process,
+ but double-check that you get this correct.))
+ * Add `async FOGData(ByteBuf&& aBuf);` to the parent/main/UI process side of things
+ (most often `parent:`).
+ * Add `async FlushFOGData() returns (ByteBuf buf);` to the non-parent/-main/-UI side
+ (most often `child:`).
+2. Implement the protocol endpoints in `P<ProcessType>{Child|Parent}.{h|cpp}`
+ * The message added to the `parent: ` section goes in
+ `P<ProcessType>Parent.{h|cpp}` and vice versa.
+3. Add to `FOGIPC.cpp`'s `FlushAllChildData` code that
+ 1. Enumerates all processes of the newly-supported type (there may only be one),
+ 2. Calls `SendFlushFOGData on each, and
+ 3. Adds the resulting promise to the array.
+4. Add to `FOGIPC.cpp`'s `SendFOGData` the correct `GeckoProcessType_*`
+ enum value, and appropriate code for getting the parent process singleton and calling
+ `SendFOGData` on it.
+5. Add to the fog crate's `register_process_shutdown` function
+ handling for at-shutdown flushing of IPC data.
+ If this isn't added, we will log (but not panic)
+ on the first use of Glean APIs on an unsupported process type.
+ * "Handling" might be an empty block with a comment explaining where to find it
+ (like how `PROCESS_TYPE_DEFAULT` is handled)
+ * Or it might be custom code
+ (like `PROCESS_TYPE_CONTENT`'s)
+6. Add to the documented [list of supported process types](#supported-process-types)
+ the process type you added support for.
+
+[glean-metrics]: https://mozilla.github.io/glean/book/reference/metrics/index.html
diff --git a/toolkit/components/glean/docs/dev/jog.md b/toolkit/components/glean/docs/dev/jog.md
new file mode 100644
index 0000000000..c8d0549cd4
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/jog.md
@@ -0,0 +1,91 @@
+# Runtime Metric Definition Subsystem: JOG
+
+```{admonition} I'm Sorry
+Why is it caled JOG? Because it's concerned with... run... time.
+```
+
+The normal mechanism for registering metrics,
+for reasons as varied from ease-of-impl to performance,
+happens at compile time.
+However, this doesn't support use cases like
+* [Artifact Builds][artifact-build]
+ (Where only the JavaScript of Firefox Desktop is repackaged at build time,
+ so there is no compile environment)
+* Dynamic Telemetry
+ (A theorized system for instrumenting Firefox Desktop without shipping code)
+* Web Extensions
+ (Or at least the kind that can't or won't use
+ [the Glean JS SDK][glean-js])
+
+Thus we need a subsystem that supports the runtime registration of metrics.
+We call it JOG and it was implemented in [bug 1698184][impl-bug].
+
+## JavaScript Only
+
+Metrics in C++ and Rust are identified by identifiers which we can't swap out at runtime.
+Thus, in order for changes to metrics to be visible to instrumented systems in C++ or Rust, you must compile.
+
+JavaScript, on the other hand, we supply instances to on-demand.
+It not only supports the specific use cases driving this project,
+it's the only environment that can benefit from runtime metric definition in Firefox Desktop.
+
+## Design
+
+The original design was done as part of
+[bug 1662863][design-bug].
+Things have mostly just been refined from there.
+
+## Architecture
+
+We silo as much of the subsystem as we can into the
+`jog` module located in `toolkit/components/glean/bindings/jog/`.
+This includes the metrics construction factory and storage for metrics instances and their names and ids.
+
+Unfortunately, so that the metrics instances can be accessed by FFI,
+the Rust metrics instances created by the `jog` crate are stored within the `fog` crate.
+
+Speaking of FFI, the `jog` crate is using cbindgen to be accessible to C++.
+
+If necessary or pleasant, it is probably possible to do away with the C++ storage,
+moving the category set and metrics id map to Rust and moving information over FFI as needed.
+
+Test methods are run from `nsIFOG` (so we can use them in JS in xpcshell)
+to static `JOG::` functions.
+
+### Build Integration
+
+If JOG detects we're an artifact build (by checking `MOZ_ARTIFACT_BUILDS`),
+it generates `jogfile.json` and ensures it is placed in `GreD`
+(next to the `firefox` binary).
+
+`jogfile.json` includes only the metric and ping information necessary to register them at runtime.
+(It doesn't know about tags or descriptions, just the shapes and names of things)
+
+This file is read the first time JS tries to get a metric category from the
+`Glean` global or a ping from the `GleanPings` global.
+
+Yes, this is on the main thread. Yes, this is synchronous. Yes, this is file I/O.
+
+Since this is a developer build, we think this is worth it to support Artifact Builds.
+
+If we're wrong about this and there are additional conditions we should place JOG under,
+please [contact us][glean-channel].
+
+#### If things get weird, delete `objdir/dist/bin/jogfile.json`
+
+Sometimes, metrics or pings you've added may not appear when you run Firefox.
+For these and other odd cases, the solution is the same:
+delete `jogfile.json` from the `dist/bin` directory of your objdir, then try again.
+
+This shouldn't happen if you keep your artifact and non-artifact objdirs segregated
+(as is good practice).
+
+If, despite doing things properly you still see this or something else odd, then that's a bug.
+Please [file it in Toolkit :: Telemetry][file-bug]
+
+[artifact-build]: https://firefox-source-docs.mozilla.org/contributing/build/artifact_builds.html
+[glean-js]: https://mozilla.github.io/glean/book/user/adding-glean-to-your-project/javascript.html
+[impl-bug]: https://bugzilla.mozilla.org/show_bug.cgi?id=1698184
+[design-bug]: https://bugzilla.mozilla.org/show_bug.cgi?id=1662863
+[glean-channel]: https://chat.mozilla.org/#/room/#glean:mozilla.org
+[file-bug]: https://bugzilla.mozilla.org/enter_bug.cgi?assigned_to=nobody%40mozilla.org&bug_ignored=0&bug_severity=--&bug_status=NEW&bug_type=defect&cf_a11y_review_project_flag=---&cf_fx_iteration=---&cf_fx_points=---&cf_performance_impact=---&cf_status_firefox106=---&cf_status_firefox107=---&cf_status_firefox108=---&cf_status_firefox_esr102=---&cf_status_thunderbird_esr102=---&cf_status_thunderbird_esr91=---&cf_tracking_firefox106=---&cf_tracking_firefox107=---&cf_tracking_firefox108=---&cf_tracking_firefox_esr102=---&cf_tracking_firefox_relnote=---&cf_tracking_thunderbird_esr102=---&cf_tracking_thunderbird_esr91=---&cf_webcompat_priority=---&component=Telemetry&contenttypemethod=list&contenttypeselection=text%2Fplain&defined_groups=1&filed_via=standard_form&flag_type-203=X&flag_type-37=X&flag_type-41=X&flag_type-607=X&flag_type-721=X&flag_type-737=X&flag_type-787=X&flag_type-799=X&flag_type-800=X&flag_type-803=X&flag_type-846=X&flag_type-855=X&flag_type-864=X&flag_type-930=X&flag_type-936=X&flag_type-937=X&flag_type-952=X&form_name=enter_bug&maketemplate=Remember%20values%20as%20bookmarkable%20template&op_sys=Unspecified&priority=--&product=Toolkit&rep_platform=Unspecified&short_desc=Problem%20with%20JOG%3A%20&target_milestone=---&version=unspecified
diff --git a/toolkit/components/glean/docs/dev/local_glean.md b/toolkit/components/glean/docs/dev/local_glean.md
new file mode 100644
index 0000000000..046cf6e79c
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/local_glean.md
@@ -0,0 +1,100 @@
+# Developing with a local Glean build
+
+FOG uses a release version of Glean, as published on [crates.io][cratesio-glean].
+
+For local development and try runs you can replace this Glean implementation with a local or remote version.
+
+1. To tell `mach` where to find your Glean, patch the [top-level `Cargo.toml`][cargo-toml]. E.g. like this:
+
+ ```toml
+ [patches.crates-io]
+ glean = { git = "https://github.com/myfork/glean", branch = "my-feature-branch" }
+ glean-core = { git = "https://github.com/myfork/glean", branch = "my-feature-branch" }
+ ```
+
+ Both crates are required to ensure they are in sync.
+
+ You can specify the exact code to use by `branch`, `tag` or `rev` (Git commit).
+ See the [cargo documentation for details][cargo-doc].
+
+ You can also use a path dependency:
+
+ ```toml
+ [patches.crates-io]
+ glean = { path = "../glean/glean-core/rlb" }
+ glean-core = { path = "../glean/glean-core" }
+ ```
+
+2. If the crate version in the patched repository is not
+ [semver]-compatible with the version required by the
+ `fog` and `fog_control` crates,
+ you need to change the version in the following files to match the ones in your
+ `glean` repo:
+
+ ```
+ toolkit/components/glean/Cargo.toml
+ toolkit/components/glean/api/Cargo.toml
+ ```
+
+ This tells FOG's crates that it needs your local Glean's version.
+
+3. Update the Cargo lockfile:
+
+ ```
+ cargo update -p glean
+ ```
+
+4. Mozilla's supply-chain management policy requires that third-party software
+ (which includes the Glean SDK because it is distributed as though it is third-party)
+ be audited and certified as safe.
+ Your local Glean SDK probably hasn't been vetted. If you try to vendor right now,
+ `./mach vendor rust` will complain something like:
+
+ ```
+ Vet error: There are some issues with your policy.audit-as-crates-io entries
+ ```
+
+ This is because your local Glean SDK is neither of a version nor is from a source that has been vetted.
+ To allow your local Glean crates to be treated as crates.io-sourced crates for vetting,
+ add the following sections to the top of `supply-chain/config.toml`:
+
+ ```toml
+ [policy.glean]
+ audit-as-crates-io = true
+
+ [policy.glean-core]
+ audit-as-crates-io = true
+ ```
+
+ If your local Glean is of a non-vetted version, you can update `glean` and
+ `glean-core`'s entries in `supply-chain/audits.toml` to the version you're using.
+ If you don't, `./mach vendor rust` will complain and not complete.
+
+ **Note:** Do not attempt to check these changes in.
+ These changes bypass supply chain defenses.
+ `@supply-chain-reviewers` may become cross as they `r-` your patch.
+
+5. Vendor the changed crates:
+
+ ```
+ ./mach vendor rust
+ ```
+
+ **Note:** If you're using a path dependency, `mach vendor rust` doesn't actually change files.
+ Instead it pulls the files directly from the location on disk you specify.
+
+6. Finally, build Firefox:
+
+ ```
+ ./mach build
+ ```
+
+A remote reference works for try runs as well,
+but a path dependency will not.
+
+Please ensure to not land a non-release version of Glean.
+
+[cratesio-glean]: https://crates.io/crates/glean
+[cargo-toml]: https://searchfox.org/mozilla-central/rev/f07a609a76136ef779c65185165ff5ac513cc172/Cargo.toml#76
+[cargo-doc]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-git-repositories
+[semver]: https://semver.org/
diff --git a/toolkit/components/glean/docs/dev/new_metric_types.md b/toolkit/components/glean/docs/dev/new_metric_types.md
new file mode 100644
index 0000000000..a3bcdc8b54
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/new_metric_types.md
@@ -0,0 +1,285 @@
+# Adding a New Metric Type
+
+This document covers how to add a new metric type to FOG.
+You should only have to do this if a new metric type is added to the
+[Glean SDK](https://mozilla.github.io/glean/book/user/metrics/index.html)
+and it is needed in Firefox Desktop.
+
+## IPC
+
+For detailed information about the IPC design,
+including a list of forbidden operations,
+please consult
+[the FOG IPC documentation](ipc.md).
+
+When adding a new metric type, the main IPC considerations are:
+* Which operations are forbidden because they are not commutative?
+ * Most `set`-style operations cannot be reconciled sensibly across multiple processes.
+* If there are non-forbidden operations,
+what partial representation will this metric have in non-main processes?
+Put another way, what shape of storage will this take up in the
+[IPC Payload](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/api/src/ipc.rs)?
+ * For example, Counters can aggregate all partial counts together to a single
+ "partial sum". So
+ [its representation](https://searchfox.org/mozilla-central/rev/803b368879fa332e8e2c1840bf1ec164f7ed2c32/toolkit/components/glean/api/src/ipc.rs#45)
+ in the IPC Payload is just a single number per Counter.
+ * In contrast, Timing Distributions' bucket arrangements are known only to the core,
+ so it can't combine sample counts in child processes.
+ Instead we record durations in the highest resolution (nanos),
+ and send a stream of high-precision samples across IPC.
+
+To implement IPC support in a metric type,
+we split FOG's Rust implementation of the metric into three pieces:
+1. An umbrella `enum` with the name `MetricTypeMetric`.
+ * It has a `Child` and a `Parent` variant.
+ * It is IPC-aware and is responsible for
+ * If on a non-parent-process,
+ either storing partial representations in the IPC Payload,
+ or logging errors if forbidden non-test APIs are called.
+ (Or panicking if test APIs are called.)
+ * If on the parent process, dispatching API calls on its inner Rust Language Binding metric.
+2. The parent-process implementation is supplied by
+ [the RLB](https://crates.io/crates/glean/).
+ * For testing, it stores the `MetricId` that identifies this particular metric in a cross-process fashion.
+ * For testing, it exposes a `child_metric()` function to create its `Child` equivalent.
+ * For testing and if it supports operations in a non-parent-process,
+ it exposes a `metric_id()` function to access the stored `MetricId`.
+3. The `MetricTypeIpc` is the non-parent-process implementation.
+ * If it does support operations in non-parent processes it stores the
+ `MetricId` that identifies this particular metric in a cross-process fashion.
+
+## Mirrors
+
+FOG can mirror Glean metrics to Telemetry probes via the
+[Glean Interface For Firefox Telemetry](../user/gifft.md).
+
+Can this metric type be mirrored?
+Should it be mirrored?
+
+If so, add an appropriate Telemetry probe for it to mirror to,
+documenting the compatibility in
+[the GIFFT docs](../user/gifft.md).
+
+### GIFFT Tests
+
+If you add a GIFFT mirror, don't forget to test that the mirror works.
+You should be able to do this by adding a task to
+[`toolkit/components/glean/tests/xpcshell/test_GIFFT.js`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/xpcshell/test_GIFFT.js).
+
+### GIFFT C++ State: Typical Locking and Shutdown
+
+Some metric types (`labeled_*`, `timespan`, `timing_distribution`)
+require holding state in C++ to make GIFFT work.
+Pings also hold state to support `testBeforeNextSubmit()`.
+If your new metric type requires state in C++,
+the current state-of-the-art is a `StaticDataMutex`-locked `UniquePtr` to a `nsTHashTable`.
+Access to the inner map is guarded by the lock and is controlled and lazily-instantiated through a single access function.
+[See Ping's `GetCallbackMapLock()`](https://searchfox.org/mozilla-central/source/toolkit/components/glean/bindings/private/Ping.cpp)
+for example.
+
+It is important to clear this state to avoid leaks.
+(See [bug 1752417](https://bugzilla.mozilla.org/show_bug.cgi?id=1752417).)
+However, instrumentation may call metrics APIs at any time.
+
+Therefore, GIFFT explicitly stops supporting these state-requiring operations after the
+[`AppShutdownTelemetry` shutdown phase](https://searchfox.org/mozilla-central/source/xpcom/base/ShutdownPhase.h).
+This is because during the next phase (`XPCOMWillShutdown`) we clear the state.
+
+## Rust
+
+FOG uses the Rust Language Binding APIs (the `glean` crate) with a layer of IPC on top.
+
+The IPC additions and glean-core trait implementations are in the
+[`private` module of the `fog` crate](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/api/src/metrics).
+
+Each metric type gets its own file, mimicking the structure in
+[`glean_core`](https://github.com/mozilla/glean/tree/main/glean-core/src/metrics)
+and [`glean`](https://github.com/mozilla/glean/tree/main/glean-core/rlb/src/private).
+Unless, of course, that metric is a labeled metric type.
+Then the sub metric type gets its own file,
+and you need to add "Labeledness" to it by implementing
+`Sealed` for your new type following the pattern in
+[`api/src/private/labeled.rs`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/api/src/private/labeled.rs).
+
+Every method on the metric type is public for now,
+including test methods,
+and is at least all the methods exposed via the
+[metric traits](https://github.com/mozilla/glean/tree/main/glean-core/src/traits).
+
+To support IPC and the MLA FFI (see below)
+we identify metric instances by MetricId and store them in maps in
+[the `__glean_metric_maps` mod of `metrics.rs`](https://hg.mozilla.org/mozilla-central/toolkit/components/glean/api/src/metrics.rs).
+This work is done by the `rust.py` and `rust(_pings).jinja2` extensions to `glean_parser` found
+[in the `build_scripts/glean_parser_ext/` folder](https://searchfox.org/mozilla-central/source/toolkit/components/glean/build_scripts/glean_parser_ext).
+
+You shouldn't have to edit these files for new metric types,
+as the original modifications to `glean_parser` for this type should already be generating correct code.
+
+### Rust Tests
+
+You should be able to smoke test the basic functionality in Rust unit tests.
+You can do this within the metric type implementation file directly.
+
+## C++ and JS
+
+The C++ and JS APIs are implemented [atop the Rust API](code_organization.md).
+We treat them both together since, though they're different languages,
+they're both implemented in C++ and share much of their implementation.
+
+The overall design is to build the C++ API atop the Multi-Language Architecture's
+(MLA's) FFI, then build the JS API atop the C++ API.
+This allows features like the
+[Glean Interface For Firefox Telemetry (GIFFT)](../user/gifft.md)
+that target only C++ and JS to be more simply implemented in the C++ layer.
+Exceptions to this (where the JS uses the FFI directly) are discouraged.
+
+Each metric type has six pieces you'll need to cover:
+
+### 1. MLA FFI
+
+- Using our convenient macros,
+ define the metric type's Multi-Language Architecture FFI layer above the Rust API in
+ [`api/src/ffi/`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/api/src/ffi/).
+
+### 2. C++ Impl
+
+- Implement a type called `XMetric` (e.g. `CounterMetric`) in `mozilla::glean::impl` in
+ [`bindings/private/`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/bindings/private/).
+ - Its methods should be named the same as the ones in the Rust API, transformed to `CamelCase`.
+ - They should all be public.
+ - Multiplex the FFI's `test_have` and `test_get` functions into a single
+ `TestGetValue` function that returns a
+ `mozilla::Maybe` wrapping the C++ type that best fits the metric type.
+- Include the new metric type in
+ [`bindings/MetricTypes.h`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/bindings/MetricTypes.h).
+- Include the new files in
+ [`moz.build`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/moz.build).
+ The header file should be added to `EXPORTS.mozilla.glean.bindings` and the
+ `.cpp` file should be added to `UNIFIED_SOURCES`.
+
+### 3. IDL
+
+- Duplicate the public API (including its docs) to
+ {searchfox}`dom/webidl/GleanMetrics.webidl`
+ with the name `GleanX` (e.g. `GleanCounter`).
+ - Inherit from `GleanMetric`.
+ - The naming style for methods here is `lowerCamelCase`.
+ - If the metric method is a reserved word, prepend it with a `_`.
+ - Web IDL bindings use
+ [their own mapping for types](/dom/webIdlBindings/index.md).
+ If you choose ones that most closely resemble the C++ types,
+ you'll make your life easier.
+- Add a new mapping in `dom/bindings/Bindings.conf`:
+ ```idl
+ 'GleanX': {
+ 'nativeType': 'mozilla::glean::GleanX',
+ 'headerFile': 'mozilla/glean/bindings/X.h',
+ },
+ ```
+ - If you don't, you will get a build error complaining `fatal error: 'mozilla/dom/GleanX.h' file not found`.
+
+### 4. JS Impl
+
+- Implement the `GleanX` (e.g. `GleanCounter`) type
+ in the same header and `.cpp` as `XMetric` in
+ {searchfox}`toolkit/components/glean/bindings/private/`
+ - It should own an instance of and delegate method implementations to `XMetric`.
+ - In the definition of `GleanX`, member identifiers are back to
+ `CamelCase`.
+ - Test-only methods can throw `DataError` on failure.
+ - Review the [Web IDL Bindings documentation](/dom/webIdlBindings/index.html)
+ for help with optional, nullable, and non-primitive types.
+
+### 6. Tests
+
+Two languages means two test suites.
+
+- Add a never-expiring test-only metric of your type to
+ [`test_metrics.yaml`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/test_metrics.yaml).
+ - Feel free to be clever with the name,
+ but be sure to make clear that it is test-only.
+- **C++ Tests (GTest)** - Add a small test case to
+ [`gtest/TestFog.cpp`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/gtest/TestFog.cpp).
+ - For more details, peruse the [testing docs](testing.md).
+- **JS Tests (xpcshell)** - Add a small test case to
+ [`xpcshell/test_Glean.js`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/xpcshell/test_Glean.js)
+ and
+ [`xpcshell/test_JOG.js`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/xpcshell/test_JOG.js).
+ If your metric type has supported IPC operations, also add cases to the `IPC` variants of these test files.
+ - For more details, peruse the [testing docs](testing.md).
+
+### 7. API Documentation
+
+Metric API Documentation is centralized in
+[the Glean SDK Book](https://mozilla.github.io/glean/book/user/metrics/index.html).
+
+You will need to craft a Pull Request against
+[the SDK](https://github.com/mozilla/glean/)
+adding a C++ and JS example to the specific metric type's API docs.
+
+Add a notice at the top of both examples that these APIs are only available in Firefox Desktop:
+````md
+<div data-lang="C++" class="tab">
+
+> **Note**: C++ APIs are only available in Firefox Desktop.
+
+```cpp
+#include "mozilla/glean/GleanMetrics.h"
+
+mozilla::glean::category_name::metric_name.Api(args);
+```
+
+There are test APIs available too:
+
+```cpp
+#include "mozilla/glean/GleanMetrics.h"
+
+ASSERT_EQ(value, mozilla::glean::category_name::metric_name.TestGetValue().ref());
+```
+</div>
+
+// and again for <div data-lang="JS">
+````
+
+If you're lucky, the Rust API will have already been added.
+Otherwise you'll need to write an example for that one too.
+
+### 8. Labeled metrics (if necessary)
+
+If your new metric type is Labeled, you have more work to do.
+I'm assuming you've already implemented the non-labeled sub metric type following the steps above.
+Now you must add "Labeledness" to it.
+
+There are four pieces to this:
+
+#### FFI
+
+- To add the writeable storage Rust will use to store the dynamically-generated sub metric instances,
+ add your sub metric type's map as a list item in the `submetric_maps` `mod` of
+ [`rust.jinja2`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2).
+- Following the pattern of the others, add a `fog_{your labeled metric name here}_get()` FFI API to
+ `api/src/ffi/mod.rs`.
+ This is what C++ and JS will use to allocate and retrieve sub metric instances by id.
+
+#### C++
+
+- Following the pattern of the others, add a template specialiation for `Labeled<YourSubMetric>::Get` to
+ [`bindings/private/Labeled.cpp`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/bindings/private/Labeled.cpp).
+ This will ensure C++ consumers can fetch or create sub metric instances.
+
+#### JS
+
+- Already handled for you since the JS types all inherit from `GleanMetric`
+ and the JS template knows to add your new type to `NewSubMetricFromIds(...)`
+ (see `GleanLabeled::NamedGetter` if you're curious).
+
+#### Tests
+
+- The labeled variant will need tests the same as Step #6.
+ A tip: be sure to test two labels with different values.
+
+## Python Tests
+
+We have a suite of tests for ensuring code generation generates appropriate code.
+You should add a metric to [that suite](testing.md) for your new metric type.
+You will need to regenerate the expected files.
diff --git a/toolkit/components/glean/docs/dev/preferences.md b/toolkit/components/glean/docs/dev/preferences.md
new file mode 100644
index 0000000000..732e25c5c9
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/preferences.md
@@ -0,0 +1,105 @@
+# Preferences and Defines
+
+## User Preferences
+
+`datareporting.healthreport.uploadEnabled`
+
+This determines whether the Glean SDK is enabled.
+It can be controlled by users via `about:preferences#privacy`.
+If this is set to false from true, we send a
+["deletion-request" ping](https://mozilla.github.io/glean/book/user/pings/deletion_request.html)
+and no data collections will be persisted or reported from that point.
+
+## Test-only Preferences
+
+`telemetry.fog.test.localhost_port`
+
+If set to a value `port` which is greater than 0, pings will be sent to
+`http://localhost:port` instead of `https://incoming.telemetry.mozilla.org`.
+If set to a value `port` which is less than 0, FOG will:
+1) Tell Glean that upload is enabled, even if it isn't.
+2) Take all pings scheduled for upload and drop them on the floor,
+ telling the Glean SDK that it was sent successfully.
+
+This is how you emulate "recording enabled but upload disabled"
+like developer builds have in Firefox Telemetry.
+When switching from `port < 0` to `port >= 0`,
+Glean will be told (if just temporarily) that upload is disabled.
+This clears the stores of recorded-but-not-reported data.
+Defaults to 0.
+
+`telemetry.fog.test.activity_limit`
+`telemetry.fog.test.inactivity_limit`
+
+This pair of prefs control the length of time of activity before inactivity
+(or vice versa)
+needed before FOG informs the SDK's Client Activity API that the client was (in)active.
+Present to allow testing without figuring out how to mock Rust's clock.
+Their values are integer seconds.
+Defaults to 120 (activity), 1200 (inactivity).
+
+## Internal Preferences
+
+`telemetry.fog.artifact_build`
+
+Read-only. This pref is `true` only if `MOZ_ARTIFACT_BUILDS` was set during configure.
+If true, [JOG](./jog) is enabled so that artifact builds will exhibit changes to their Glean metrics.
+
+## Defines
+
+`MOZ_AUTOMATION`
+
+If set, and `GLEAN_SOURCE_TAGS` isn't set, FOG will set a
+[Glean source tag](https://mozilla.github.io/glean/book/reference/debug/sourceTags.html)
+of `automation`.
+
+If `GLEAN_SOURCE_TAGS` is set, the `automation` source tag will not be added automatically.
+
+If not set, any tags set by `GLEAN_SOURCE_TAGS` will be present.
+
+`MOZ_GLEAN_ANDROID`
+
+If set, the Glean SDK is assumed to be managed by something other than FOG, meaning:
+* [GIFFT][gifft] is disabled.
+* FOG doesn't initialize Glean
+* FOG doesn't relay (in)activity or experiment annotations to Glean
+
+`MOZILLA_OFFICIAL`
+
+If unset, we set a `glean_disable_upload` Rust feature in
+`gkrust` and `gkrust-shared` which is forwarded to `fog_control` as `disable_upload`.
+This feature defaults FOG to an "upload disabled"
+mode where collection on the client proceeds as normal but no ping is sent.
+This mode can be overridden at runtime in two ways:
+* If the ping has a
+ [Debug Tag](https://mozilla.github.io/glean/book/user/debugging/index.html)
+ then it is sent so that it may be inspected in the
+ [Glean Debug Ping Viewer](https://debug-ping-preview.firebaseapp.com/).
+* If the preference `telemetry.fog.test.localhost_port` is set to a value greater than 0,
+ then pings are sent to a server operating locally at that port
+ (even if the ping has a Debug Tag), to enable testing.
+
+`MOZILLA_OFFICIAL` tends to be set on most builds released to users,
+including builds distributed by Linux distributions.
+It tends to not be set on local developer builds.
+See [bug 1680025](https://bugzilla.mozilla.org/show_bug.cgi?id=1680025) for details.
+
+`MOZ_ARTIFACT_BUILDS`
+
+If `MOZ_ARTIFACT_BUILDS` is set in the build config,
+[JOG](./jog) will generate a file for the runtime-registration of metrics and pings.
+This is to support [Artifact Builds](/contributing/build/artifact_builds).
+
+See also `telemetry.fog.artifact_build`.
+
+`OS_TARGET`
+
+If not set to `'Android'` we set a `glean_million_queue` Rust feature
+([see gkrust-features.mozbuild][gkrust-features])
+which, when passed to the Glean SDK,
+opts us into a preinit queue that doesn't discard tasks until there are 10^6 of them.
+
+See [bug 1797494](https://bugzilla.mozilla.org/show_bug.cgi?id=1797494) for details.
+
+[gkrust-features]: https://searchfox.org/mozilla-central/source/toolkit/library/rust/gkrust-features.mozbuild
+[gifft]: ../user/gifft
diff --git a/toolkit/components/glean/docs/dev/storage.md b/toolkit/components/glean/docs/dev/storage.md
new file mode 100644
index 0000000000..00464c6173
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/storage.md
@@ -0,0 +1,14 @@
+# Storage
+
+Both FOG and the Glean SDK require some storage in the
+[Firefox Profile Directory](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Multiple_profiles).
+
+## FOG
+
+At present FOG's storage is limited to its [preferences](preferences.md).
+
+## Glean SDK
+
+The Glean SDK stores things in the
+[Glean Data Directory](https://mozilla.github.io/glean/book/dev/core/internal/directory-structure.html)
+which can be found at `<profile_dir>/datareporting/glean`.
diff --git a/toolkit/components/glean/docs/dev/style_guide.md b/toolkit/components/glean/docs/dev/style_guide.md
new file mode 100644
index 0000000000..d96766da9f
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/style_guide.md
@@ -0,0 +1,49 @@
+# FOG Documentation Style Guide
+
+FOG's Documentation is written in Markdown.
+You can find its source at `toolkit/components/glean/docs`.
+
+## Line breaks
+
+We will use [semantic linefeeds]:
+* Break anywhere before 80-100 characters
+* Break after any punctuation when it makes sense
+* Break before or after any markdown when it makes sense
+
+**Tip:** To keep lines narrow, use markdown's [reference link]
+feature whenever it makes sense (or all the time. Up to you.).
+
+## Linking to other documentation
+
+Linking to other external documentation is [easy][reference link].
+Linking to other pieces of documentation in the source docs requires a
+link to the source file in the sphinx tree.
+
+Links can be relative e.g. to link to the [preferences] docs:
+
+```md
+[preferences](preferences.md)
+```
+
+Or they can be absolute e.g. to link to the [Telemetry] docs:
+```md
+[Telemetry](/toolkit/components/telemetry/index.rst)
+```
+
+Sphinx will automagically transform that to an
+appropriately-base-url'd url with a `.html` suffix.
+
+
+## Use of "Main" versus "Parent" for processes
+
+When talking about Glean, it is helpeful to distinguish when we are on the main
+process specifically. This is because there is only one main process, and there is only one Glean.
+However, "parent" is a relative term, and it may refer to a process
+that is the parent of another process, but is not itself the main process.
+Prefer use of "main" when it is accurate.
+
+[semantic linefeeds]: https://rhodesmill.org/brandon/2012/one-sentence-per-line/
+[reference link]: https://spec.commonmark.org/0.29/#reference-link
+[Telemetry]: /toolkit/components/telemetry/index.rst
+[#firefox-source-docs:mozilla.org]: https://chat.mozilla.org/#/room/#firefox-source-docs:mozilla.org
+[bug 1621950]: https://bugzilla.mozilla.org/show_bug.cgi?id=1621950
diff --git a/toolkit/components/glean/docs/dev/testing.md b/toolkit/components/glean/docs/dev/testing.md
new file mode 100644
index 0000000000..fe6c3e0fe7
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/testing.md
@@ -0,0 +1,240 @@
+# Testing
+
+```{admonition} This documentation is about testing FOG itself
+This document contains information about how FOG tests itself,
+how to add new tests, how and what to log, and stuff like that.
+If you're interested in learning how to test instrumentation you added,
+you'll want to read
+[the instrumetnation testing docs](../user/instrumentation_tests) instead.
+```
+
+Given the multiple API languages, processes, and dependencies,
+testing FOG is a matter of choosing the right tool for the situation.
+
+## One Big Command
+
+To run all the things, here's the tl;dr:
+
+`./mach build && ./mach lint -W -w -o --fix
+&& ./mach rusttests && ./mach gtest "FOG*"
+&& python3 ./mach python-test toolkit/components/glean/tests/pytest
+&& ./mach test toolkit/components/glean/tests/xpcshell
+&& ./mach telemetry-tests-client toolkit/components/telemetry/tests/marionette/tests/client/test_fog* --gecko-log "-"
+&& ./mach test toolkit/components/glean/tests/browser
+`
+
+## Logging
+
+An often-overlooked first line of testing is "what do the logs say?".
+To turn on logging for FOG, use any of the following:
+* Run Firefox with `RUST_LOG="fog_control,fog,glean_core"`.
+ * On some platforms this will use terminal colours to indicate log level.
+* Run Firefox with `MOZ_LOG="timestamp,sync,glean::*:5,fog::*:5,fog_control::*:5,glean_core::*:5"`.
+* Set the following prefs:
+ * `logging.config.timestamp` to `true`
+ * `logging.config.sync` to `true`
+ * `logging.fog_control::*` to `5`
+ * `logging.fog::*` to `5`
+ * `logging.glean::*` to `5`
+ * `logging.glean_core::*` to `5`
+ * `logging.config.clear_on_startup` to `false` (or all these prefs will be cleared on startup)
+
+For more information on logging in Gecko, see the
+[Gecko Logging docs](/xpcom/logging).
+
+User-destined logs (of the "You did something wrong" variety) might output to the
+[Browser Console](/devtools-user/browser_console/index)
+if they originate in JS land. Open via
+<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>J</kbd>, or
+<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>J</kbd>.
+
+```{admonition} Note
+At present, Rust logging in non-main processes just doesn't work.
+```
+
+### What to log, and to where?
+
+FOG covers a lot a ground (languages, layers):
+where you are determines what logging you have available.
+
+Here are some common situtations for logging:
+
+#### JS to C++
+
+If your logging is aimed at the user using the JS API
+(e.g. because the type provided isn't convertable to the necessary C++ type)
+then use the Browser Console via
+[FOG's Common's `LogToBrowserConsole`](https://searchfox.org/mozilla-central/rev/d107bc8aeadcc816ba85cb21c1a6a1aac1d4ef9f/toolkit/components/glean/bindings/private/Common.cpp#19).
+
+#### C++
+
+If you are in C++ and didn't come from JS, use `MOZ_LOG` with module `fog`.
+
+#### Rust
+
+Use the logging macros from `log`, e.g. `log::info!` or `log::error!`.
+Remember that, no matter the log level, `log::debug!` and `log::trace!`
+[will not appear in non-debug builds](/testing-rust-code/index.md#gecko-logging)
+
+If you are logging due to a situation caused by and fixable by a developer using the API,
+use `log::error!(...)`. Otherwise, use a quieter level.
+
+## `about:glean`
+
+`about:glean` is a page in a running Firefox that allows you to
+[debug the Glean SDK](https://mozilla.github.io/glean/book/user/debugging/index.html)
+in Firefox Desktop.
+It does this through the displayed user interface (just follow the instructions).
+
+## Linting
+
+To keep in accordance with Mozilla's various and several Coding Styles,
+we rely on `mach lint`.
+
+To lint the code in the "usual" way, automatically fixing where possible, run:
+`./mach lint -W -w -o --fix`
+
+This should keep you from checking in code that will automatically be backed out.
+
+## Rust
+
+Not all of our Rust code can be tested in a single fashion, unfortunately.
+
+### Using `rusttests` (Treeherder symbol `Br` (a build task))
+
+If the crate you're testing has no Gecko symbols you can write standard
+[Rust tests](https://doc.rust-lang.org/book/ch11-01-writing-tests.html).
+
+This supports both unit tests
+(inline in the file under test) and integration tests
+(in the `tests/` folder in the crate root).
+Metric type tests are currently written as unit tests inline in the file,
+as they require access to the metric ID, which should only be exposed in tests.
+
+To run FOG's `rusttests` suite use `mach rusttests`
+
+If the crate uses only a few Gecko symbols, they may use the
+`with_gecko` feature to conditionally use them.
+This allows the crate to test its non-Gecko-adjacent code using Rust tests.
+(You will need to cover the Gecko-adjacent code via another means.)
+
+**Note:** Some FOG rusttests panic on purpose. They print stack traces to stdout.
+If the rusttests fail and you see a stack trace,
+double-check it isn't from a purposefully-panicking test.
+
+**Note:** If a test fails, it is very likely they'll poison the test lock.
+This will cause all subsequent tests that attempt to take the test lock
+(which is all of them)
+to also fail due to `PoisonError`s. They can be safely ignored.
+
+### Using `gtest` (Treeherder symbol `GTest` (a build task))
+
+Because Gecko symbols aren't built for the
+`rusttests` build,
+any test that is written for code that uses Gecko symbols should be written as a
+[`gtest`](https://github.com/google/googletest)
+in `toolkit/components/glean/tests/gtest/`.
+You can write the actual test code in Rust.
+It needs to be accompanied by a C++ GTest that calls a C FFI-exported Rust function.
+See [Testing & Debugging Rust Code](/testing-rust-code/index.md) for more.
+See [`toolkit/components/glean/tests/gtest/TestFog.cpp`](https://searchfox.org/mozilla-central/source/toolkit/components/glean/tests/gtest/TestFog.cpp)
+and [`toolkit/components/glean/tests/gtest/test.rs`](https://searchfox.org/mozilla-central/source/toolkit/components/glean/tests/gtest/test.rs)
+for an example.
+
+By necessity these can only be integration tests against the compiled crate.
+
+**Note:** When adding a new test file, don't forget to add it to
+`toolkit/components/glean/tests/gtest/moz.build` and use the
+`FOG` prefix in your test names
+(e.g. `TEST(FOG, YourTestName) { ... }`).
+
+To run FOG's Rust `gtest` suite use `mach gtest FOG.*`
+
+## Python (Treeherder symbol `py3(fp)` aka `source-test-python-fog`)
+
+The [Glean Parser](https://github.com/mozilla/glean_parser/)
+has been augmented to generate FOG-specific APIs for Glean metrics.
+This augmentation is tested by running:
+
+`mach test toolkit/components/glean/tests/pytest`
+
+These tests require Python 3+.
+If your default Python is Python 2, you may need to instead run:
+
+`python3 mach python-test toolkit/components/glean/tests/pytest`
+
+These tests check the code generator output against known good file contents.
+If you change the code generator the files will need an update.
+Run the test suite with the `UPDATE_EXPECT` environment variable set to do that automatically:
+
+`UPDATE_EXPECT=1 mach test toolkit/components/glean/tests/pytest`
+
+## C++ (Treeherder symbol `GTest` (a build task))
+
+To test the C++ parts of FOG's implementation
+(like metric types)
+you should use `gtest`.
+FOG's `gtest` tests are in
+[`gtest/`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/gtest/).
+
+You can either add a test case to an existing file or add a new file.
+If you add a new file, remember to add it to the
+[`moz.build`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/gtest/moz.build))
+or the test runner won't be able to find it.
+
+All tests should start with `FOG` so that all tests are run with
+`./mach gtest FOG*`.
+
+## JS (Treeherder symbol `X(Xn)` for some number `n`)
+
+To test the JS parts of FOG's implementation
+(like metric types)
+you should use `xpcshell`.
+FOG's `xpcshell` tests are in
+[`xpcshell/`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/xpcshell).
+
+You can either add a test case to an existing file or add a new file.
+If you add a new file, remember to add it to the
+[`xpcshell.ini`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/xpcshell/xpcshell.ini)
+or the test runner will not be able to find it.
+
+To run FOG's JS tests, run:
+`./mach test toolkit/components/glean/tests/xpcshell`
+
+## Non-content-process multiprocess (Browser Chrome Mochitests with Treeherder symbol `M(bcN)` for some number `N`)
+
+To test e.g. the GPU process support you need a full Firefox browser:
+xpcshell doesn't have the flexibility.
+To test that and have access to privileged JS (i.e. `Glean` and `FOG` APIs),
+we use browser-chrome-flavoured mochitests you can find in
+[`browser/`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/browser).
+
+If you need to add a new test file, remember to add it to the
+[`browser.ini`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/glean/tests/browser/browser.ini)
+manifest, or the test runner will not be able to find it.
+
+To run FOG's browser chrome tests, run:
+`./mach test toolkit/components/glean/tests/browser`
+
+## Integration (Marionette, borrowing `telemetry-tests-client` Treeherder symbol `tt(c)`)
+
+To test pings (See [bug 1681742](https://bugzilla.mozilla.org/show_bug.cgi?id=1681742))
+or anything that requires one or more full browsers running,
+we use the `telemetry-tests-client` suite in
+[`toolkit/components/telemetry/tests/marionette/tests/client/`](https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/telemetry/tests/marionette/tests/client/).
+
+For more information on this suite, look to
+[Firefox Telemetry's Test Documentation](https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/internals/tests.html#integration-tests-telemetry-tests-client-and-telemetry-integration-tests)
+and
+[Marionette's Documentation](/testing/marionette/Testing.md).
+
+To run these integration tests, run:
+`./mach telemetry-tests-client toolkit/components/telemetry/tests/marionette/tests/client/`
+
+To capture the Firefox under test's logs, use the `--gecko-log` parameter.
+For example, to echo to stdout:
+`./mach telemetry-tests-client toolkit/components/telemetry/tests/marionette/tests/client/test_fog* --gecko-log "-"`
+
+**Note:** Running the `tt(c)` suite in this way ignored skip directives in the manifest.
+This means that you might run tests that are not expected to succeed on your platform.
+Check `toolkit/components/telemetry/tests/marionette/tests/client/manifest.ini` for details.
diff --git a/toolkit/components/glean/docs/dev/updating_parser.md b/toolkit/components/glean/docs/dev/updating_parser.md
new file mode 100644
index 0000000000..4609f76a80
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/updating_parser.md
@@ -0,0 +1,52 @@
+# Updating glean_parser
+
+Project FOG uses the `glean_parser` to generate code from metric definitions.
+It depends on [glean-parser] from pypi.org
+
+[glean-parser]: https://pypi.org/project/glean-parser/
+
+To update the in-tree glean-parser change the version in `third_party/python/requirements.in`,
+then run
+
+```
+./mach vendor python
+```
+
+```{note}
+**Important**: the glean_parser and all of its dependencies must support Python 3.5, as discussed here.
+This is the minimum version supported by mach and installed on the CI images for running tests.
+This is enforced by the version ranges declared in the Python installation manifest.
+```
+
+## Version mismatch of the Python dependencies
+
+The logic for handling version mismatches is very similar to the one for the Rust crates.
+See [Updating the Glean SDK](updating_sdk.md) for details.
+However, updating Python packages also requires to think about Python 3.5 (and Python 2, still) compatibility.
+
+## Keeping versions in sync
+
+The Glean SDK and `glean_parser` are currently released as separate projects.
+However each Glean SDK release requires a specific `glean_parser` version.
+When updating one or the other ensure versions stay compatible.
+You can find the currently used `glean_parser` version in the Glean SDK source tree, e.g. in [sdk_generator.sh].
+
+[sdk_generator.sh]: https://github.com/mozilla/glean/blob/main/glean-core/ios/sdk_generator.sh#L28
+
+## Using a local `glean_parser` development version
+
+To test out a new `glean_parser` in mozilla-central follow these steps:
+
+1. Remove `glean_parser` from the user-wide virtual environment.
+ This can be found in a path like `~/.mozbuild/srcdirs/gecko-f5e3b9c6ded5/_virtualenvs/mach/lib/python3.10/site-packages/glean_parser`
+ Note that the `gecko-f5e3b9c6ded5` part will be different depending on your local checkout.
+ Remove all directories and files mentioning `glean_parser`
+2. Remove `glean_parser` from the build virtual enviromment.
+ This can be found in `$MOZ_OBJDIR/_virtualenvs/common/lib/python3.6/site-packages/glean_parser`.
+ Note that `$MOZ_OBJDIR` depends on your local mozconfig configuration.
+ Remove all directories and files mentioning `glean_parser`
+3. Copy the local `glean_parser` checkout into `third_party/python/glean_parser`.
+ E.g. `cp ~/code/glean_parser $GECKO/third_party/python/glean_parser`.
+
+You should now be able to build `mozilla-central` and it will use the modified `glean_parser`.
+You can make further edits in `$GECKO/third_party/python/glean_parser`.
diff --git a/toolkit/components/glean/docs/dev/updating_sdk.md b/toolkit/components/glean/docs/dev/updating_sdk.md
new file mode 100644
index 0000000000..fafec08d39
--- /dev/null
+++ b/toolkit/components/glean/docs/dev/updating_sdk.md
@@ -0,0 +1,48 @@
+# Updating the Glean SDK
+
+Project FOG uses the Glean SDK published as the [`glean`][glean-crate]
+and [`glean-core`][glean-core] crates on crates.io.
+
+[glean-crate]: https://crates.io/crates/glean
+[glean-core]: https://crates.io/crates/glean-core
+
+These two crates' versions are included in several places in mozilla-central.
+To update them all, you should use the command
+`mach update-glean <version, like "54.1.0">`.
+
+This is a semi-manual process.
+Please pay attention to the output of `mach update-glean` for instructions,
+and follow them closely.
+
+## Version mismatches of Rust dependencies
+
+Other crates that are already vendored might require a different version of the same dependencies that the Glean SDK requires.
+The general strategy for Rust dependencies is to keep one single version of the dependency in-tree
+(see [comment #8 in bug 1591555](https://bugzilla.mozilla.org/show_bug.cgi?id=1591555#c8)).
+This might be hard to do in reality since some dependencies might require tweaks in order to work.
+The following strategy can be followed to decide on version mismatches:
+
+* If the versions only **differ by the patch version**, Cargo will keep the vendored version,
+ unless some other dependency pinned specific patch versions;
+ assuming it doesn’t break the Glean SDK;
+ if it does, follow the next steps;
+* If the version of the **vendored dependency is newer** (greater major or minor version) than the version required by the Glean SDK,
+ [file a bug in the Glean SDK component][glean-bug] to get Glean to require the same version;
+ * You will have to abandon updating the Glean SDK to this version.
+ You will have to wait for Glean SDK to update its dependency and for a new Glean SDK release.
+ Then you will have to update to that new Glean SDK version.
+* If the version of the **vendored dependency is older** (lower major or minor version), consider updating the vendored version to the newer one;
+ seek review from the person who vendored that dependency in the first place;
+ if that is not possible or breaks mozilla-central build, then consider keeping both versions vendored in-tree; please note that this option will probably only be approved for small crates,
+ and will require updating the `TOLERATED_DUPES` list in `mach vendor`
+ (instructions are provided as you go).
+
+## Keeping versions in sync
+
+The Glean SDK and `glean_parser` are currently released as separate projects.
+However each Glean SDK release requires a specific `glean_parser` version.
+When updating one or the other ensure versions stay compatible.
+You can find the currently used `glean_parser` version in the Glean SDK source tree, e.g. in [sdk_generator.sh].
+
+[sdk_generator.sh]: https://github.com/mozilla/glean/blob/main/glean-core/ios/sdk_generator.sh#L28
+[glean-bug]: https://bugzilla.mozilla.org/enter_bug.cgi?product=Data+Platform+and+Tools&component=Glean%3A+SDK&priority=P3&status_whiteboard=%5Btelemetry%3Aglean-rs%3Am%3F%5D
diff --git a/toolkit/components/glean/docs/index.md b/toolkit/components/glean/docs/index.md
new file mode 100644
index 0000000000..c9d8f42eb4
--- /dev/null
+++ b/toolkit/components/glean/docs/index.md
@@ -0,0 +1,30 @@
+# Firefox on Glean (FOG)
+
+Firefox on Glean (FOG) is the name of the layer that integrates the
+[Glean SDK][glean-sdk] into
+[Firefox Desktop](https://www.firefox.com/).
+
+The [Glean SDK][glean-sdk]
+is a data collection library built by Mozilla for use in its products.
+Like [Telemetry][telemetry], it can be used to
+(in accordance with our [Privacy Policy][privacy-policy])
+send anonymous usage statistics to Mozilla in order to make better decisions.
+
+If you have any questions,
+please reach out to the team on
+[#glean:mozilla.org][glean-matrix].
+
+```{toctree}
+:titlesonly:
+:maxdepth: 2
+:glob:
+
+user/index
+dev/index
+```
+
+[telemetry]: ../telemetry/index
+[glean-sdk]: https://github.com/mozilla/glean/
+[book-of-glean]: https://mozilla.github.io/glean/book/index.html
+[privacy-policy]: https://www.mozilla.org/privacy/
+[glean-matrix]: https://chat.mozilla.org/#/room/#glean:mozilla.org
diff --git a/toolkit/components/glean/docs/user/geckoview_streaming_migration.md b/toolkit/components/glean/docs/user/geckoview_streaming_migration.md
new file mode 100644
index 0000000000..6dede3e90e
--- /dev/null
+++ b/toolkit/components/glean/docs/user/geckoview_streaming_migration.md
@@ -0,0 +1,262 @@
+# Migrating Telemetry Collected via Geckoview Streaming to Glean
+
+With Geckoview Streaming (GVST) having been deprecated,
+this is a guide to migrating collections to [Glean][book-of-glean]
+via [Firefox on Glean](../index.md).
+
+```{contents}
+```
+
+## Before we Begin
+
+You should familiarize yourself with the guide on
+[Adding New Metrics to Firefox Desktop](./new_definitions_file.md).
+
+You should also read through the guide for
+[Migrating Metrics from Legacy Telemetry to Glean](./migration.md).
+
+This guide assumes some basic familiarity with the above.
+The [Glean book][book-of-glean] has a full API reference, as well.
+
+## Process
+
+There are 3 main steps:
+
+1. Move the metric definition and make necessary updates
+2. Update the call to use the Glean API
+3. Update the tests to use the Glean test API
+
+### Move and Update the Metric Definition
+
+Existing metrics that make use of the GVST will already have a fully specified YAML
+entry that we will use as a starting point.
+This is convenient, but we want to make some minor updates rather than take it fully as is.
+At a minimum we need to move the definition out of the
+[Geckoview metrics.yaml][gv-metrics-yaml] file and change from GVST to [GIFFT](./gifft.md).
+It can go into whichever metrics.yaml file you feel is most appropriate.
+If an appropriate one does not exist, create a new one [following this guide][new-yaml].
+Completely remove the metric definition from the Geckoview `metrics.yaml`.
+
+For all metric types other than `labeled counters` the first step is to change the key
+of the `gecko_datapoint` entry to `telemetry_mirror`.
+Next, update the value as per the rules outlined in the [GIFFT guide][telemetry-mirror-doc].
+This change is required to keep data flowing in the Legacy Telemetry version of the metric.
+Doing so will ensure that downstream analyses do not break unintentionally.
+It is not necessary to modify the [Histograms.json][histograms-json] or
+[Scalars.yaml][scalars-yaml] file.
+
+To migrate `labeled counters` instead fully remove the `gecko_datapoint` entry.
+Note that our overall treatment of this type is slightly different.
+
+Next add the bug that covers the migration to the `bugs` field.
+Update the `description` field as well to indicate the metric used to be collected
+via the Geckoview Streaming API. Other fields should be updated as makes sense.
+For example, you may need to update the field for `notification_emails`.
+Since you are not changing the collection a new data review is not necessary.
+However, if you notice a metric is set to expire soon and it should continue to be collected,
+complete a [data review renewal][dr-renewal] form.
+
+Do not change the name or metric type.
+**If you need to change or one both you are creating a new collection.**
+
+### Update Calls to Use the Glean API
+
+The next step is to update the metric calls to the Glean API.
+Fortunately, for the vast matjority of metricsthis is a 1:1 swapout,
+or for `labeled counters` (which are `categorical histograms` in legacy) we add a second call.
+We identify the Glean API, remove the old call, and put in its place the Glean call.
+You can find a full API reference in the [Glean][book-of-glean], but we'll look at how to record
+values for the types that have existing GVST metrics.
+
+One way to mentally organize the metrics is to break them into two groups, those that are set,
+and those that are accumulated to. As when you use the Legacy Telemetry API for GVST,
+they are invoked slightly differently.
+
+To record in C++, you need to include `#include "mozilla/glean/GleanMetrics.h"`.
+In Javascript, it is extremely unlikely that you will not already have access to `Glean`.
+If you do not, please reach out to a Data Collection Tools team member on
+[the #glean:mozilla.org Matrix channel](https://chat.mozilla.org/#/room/#glean:mozilla.org).
+
+Let's try a few examples.
+
+#### Migrating a Set Value (string) in C++
+
+Let's look at the case of the Graphics Adaptor Vendor ID.
+This is a String,
+and it's recorded via GVST in C++
+
+GVST YAML entry (slightly truncated):
+
+```YAML
+geckoview:
+ version:
+ description: >
+ The version of the Gecko engine, example: 74.0a1
+ type: string
+ gecko_datapoint: gecko.version
+```
+
+And this is recorded:
+
+```CPP
+Telemetry::ScalarSet(Telemetry::ScalarID::GECKO_VERSION,
+ NS_ConvertASCIItoUTF16(gAppData->version));
+```
+
+To migrate this, let's update our YAML entry, again moving it out of the GVST
+metrics.yaml into the most appropriate one:
+
+```YAML
+geckoview:
+ version:
+ description: >
+ The version of the Gecko engine, example: 74.0a1
+ (Migrated from geckoview.gfx.adapter.primary)
+ type: string
+ telemetry_mirror: GECKO_VERSION
+```
+
+Notice how we've checked all of our boxes:
+
+* Made sure our category makes sense
+* Changed gecko_datapoint to telemetry_mirror
+* Updated the description to note that it was migrated from another collection
+* Kept the type identical.
+
+Now we can update our call:
+
+```CPP
+mozilla::glean::geckoview::version.Set(nsDependentCString(gAppData->version));
+```
+
+#### Migrating a Labeled Counter in C++
+
+Let's look at probably the most complicated scenario, one where we need to accumulate
+to a labeled collection. Because the glean `labeled counter` and legacy `categorical histogram`
+type do not support GIFFT, we will add a second call.
+Let's take a look at an elided version of how this would be done with GVST:
+
+```CPP
+switch (aResult.as<NonDecoderResult>()) {
+ case NonDecoderResult::SizeOverflow:
+ AccumulateCategorical(LABELS_AVIF_DECODE_RESULT::size_overflow);
+ return;
+ case NonDecoderResult::OutOfMemory:
+ AccumulateCategorical(LABELS_AVIF_DECODE_RESULT::out_of_memory);
+ return;
+ case NonDecoderResult::PipeInitError:
+ AccumulateCategorical(LABELS_AVIF_DECODE_RESULT::pipe_init_error);
+ return;
+}
+```
+
+And we update it by adding a call with the FOG API:
+
+```CPP
+switch (aResult.as<NonDecoderResult>()) {
+ case NonDecoderResult::SizeOverflow:
+ AccumulateCategorical(LABELS_AVIF_DECODE_RESULT::size_overflow);
+ mozilla::glean::avif::decode_result.EnumGet(avif::DecodeResult::eSizeOverflow).Add();
+ return;
+ case NonDecoderResult::OutOfMemory:
+ AccumulateCategorical(LABELS_AVIF_DECODE_RESULT::out_of_memory);
+ mozilla::glean::avif::decode_result.EnumGet(avif::DecodeResult::eOutOfMemory).Add();
+ return;
+ case NonDecoderResult::PipeInitError:
+ AccumulateCategorical(LABELS_AVIF_DECODE_RESULT::pipe_init_error);
+ mozilla::glean::avif::decode_result.EnumGet(avif::DecodeResult::ePipeInitError).Add();
+ return;
+}
+```
+
+#### Migrating an Accumulated Value (Histogram) in Javascript
+
+Javascript follows the same pattern. Consider the case when want to record the number
+of uniqiue site origins. Here's the original JS implementation:
+
+```Javascript
+let originCount = this.computeSiteOriginCount(aWindows, aIsGeckoView);
+let histogram = Services.telemetry.getHistogramById(
+ "FX_NUMBER_OF_UNIQUE_SITE_ORIGINS_ALL_TABS",
+);
+
+if (!this._lastRecordSiteOrigin) {
+ this._lastRecordSiteOrigin = currentTime;
+} else if (currentTime >= this._lastRecordSiteOrigin + this.min_interval) {
+ this._lastRecordSiteOrigin = currentTime;
+
+ histogram.add(originCount);
+}
+```
+
+And here is the direct Glean version
+
+```Javascript
+let originCount = this.computeSiteOriginCount(aWindows, aIsGeckoView);
+
+if (!this._lastRecordSiteOrigin) {
+ this._lastRecordSiteOrigin = currentTime;
+} else if (currentTime >= this._lastRecordSiteOrigin + this.min_interval) {
+ this._lastRecordSiteOrigin = currentTime;
+
+ Glean.tabs.uniqueSiteOriginsAllTabs.accumulateSamples([originCount]);
+}
+
+```
+
+Note that we don't have to call into Services to get the histogram object.
+
+### Update the tests to use the Glean test API
+
+The last piece is updating tests. If tests don't exist
+(which is often the case since testing metrics collected via GVST can be challenging),
+we recommend that you write them as the
+[the Glean test API is quite straightforward](./instrumentation_tests.md).
+
+The main test method is `testGetValue()`. Returning to our earlier example of
+Number of Unique Site Origins, in a Javascript test we can invoke:
+
+```Javascript
+let result = Glean.tabs.uniqueSiteOriginsAllTabs.testGetValue();
+
+// This collection is a histogram, we can check the sum for this test
+Assert.equal(result.sum, 144);
+```
+
+If your collection is in a child process, it can be helpful to invoke
+`await Services.fog.testFlushAllChildren();`
+
+If you wish to write a C++ test, `testGetValue()` is also our main method:
+
+```CPP
+#include "mozilla/glean/GleanMetrics.h"
+
+ASSERT_EQ(1,
+ mozilla::glean::avif::image_decode_result
+ .EnumGet(avif::DecodeResult::eSizeOverflow)
+ .TestGetValue()
+ .unwrap()
+ .ref());
+
+ASSERT_EQ(3,
+ mozilla::glean::avif::image_decode_result
+ .EnumGet(avif::DecodeResult::eOutOfMemory)
+ .TestGetValue()
+ .unwrap()
+ .ref());
+
+ASSERT_EQ(0,
+ mozilla::glean::avif::image_decode_result
+ .EnumGet(avif::DecodeResult::ePipeInitError)
+ .TestGetValue()
+ .unwrap()
+ .ref());
+```
+
+[book-of-glean]: https://mozilla.github.io/glean/book/index.html
+[gv-metrics-yaml]: https://searchfox.org/mozilla-central/source/toolkit/components/telemetry/geckoview/streaming/metrics.yaml
+[histograms-json]: https://searchfox.org/mozilla-central/source/toolkit/components/telemetry/Histograms.json
+[scalars-yaml]: https://searchfox.org/mozilla-central/source/toolkit/components/telemetry/Scalars.yaml
+[new-yaml]: ./new_definitions_file.md#where-do-i-define-new-metrics-and-pings
+[dr-renewal]: https://github.com/mozilla/data-review/blob/main/renewal_request.md
+[telemetry-mirror-doc]: https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/gifft.html#the-telemetry-mirror-property-in-metrics-yaml
diff --git a/toolkit/components/glean/docs/user/getting_started.md b/toolkit/components/glean/docs/user/getting_started.md
new file mode 100644
index 0000000000..50b955bced
--- /dev/null
+++ b/toolkit/components/glean/docs/user/getting_started.md
@@ -0,0 +1,97 @@
+# Getting Started with Firefox on Glean (FOG)
+
+This documentation is designed to be helpful to those who are
+* New to data collection in Firefox Desktop,
+* Experienced with data collection in Firefox Desktop, but not the Glean kind
+* Those who are just interested in a refresher.
+
+## What is FOG?
+
+Firefox on Glean (FOG) is the library that brings
+[the Glean SDK](https://mozilla.github.io/glean/book/index.html),
+Mozilla's modern data collection system,
+to Firefox Desktop.
+
+FOG's code is in `toolkit/components/glean` and is considered part of the
+`Toolkit :: Telemetry` [module][modules].
+Bugs against FOG can be [filed][file-fog-bugs]
+in Bugzilla in the `Toolkit` product and the `Telemetry` component.
+(No bugs about adding new instrumentation, please.
+You can file those in the components that you want instrumented.)
+You can find folks who can help answer your questions about FOG in
+* [#glean:mozilla.org](https://chat.mozilla.org/#/room/#glean:mozilla.org)
+* [#telemetry:mozilla.org](https://chat.mozilla.org/#/room/#telemetry:mozilla.org)
+* Slack#data-help
+
+On top of the usual things Glean embedders supply
+(user engagement monitoring, network upload configuration, data upload preference watching, ...)
+FOG supplies Firefox-Desktop-specific things:
+* Privileged JS API
+* C++ API
+* IPC
+* Test Preferences
+* Support for `xpcshell`, browser-chrome mochitests, GTests, and rusttests
+* `about:glean`
+* ...and more.
+
+## What do I need to know about Glean?
+
+You use the APIs supplied by the Glean SDK to instrument Mozilla projects.
+
+The unit of instrumentation is the **metric**.
+Recording the number of times a user opens a new tab? That's a metric.
+Timing how long each JS garbage collector pass takes? Also a metric.
+
+Glean has documentation about
+[how to add a new metric][add-a-metric]
+that you should follow to learn how to add a metric to instrument Firefox Desktop.
+There are some [peculiarities specific to Firefox Desktop](new_definitions_file)
+that you'll wish to review as well.
+Don't forget to get [Data Collection Review][data-review]
+for any new or expanded data collections in mozilla projects.
+
+By adding a metric you've told the Glean SDK what shape of instrumentation you want.
+And by using the metric's APIs to instrument your code,
+you've put your interesting data into that metric.
+But how does the data leave Firefox Desktop and make it to Mozilla's Data Pipeline?
+
+Batches of related metrics are collected into **pings**
+which are submitted according to their specific schedules.
+If you don't say otherwise, any non-`event`-metric will be sent in the
+[built-in Glean "metrics" ping][metrics-ping] about once a day.
+(`event` metrics are sent in [the "events" ping][events-ping]
+more frequently than that).
+
+With data being sent to Mozilla's Data Pipeline, how do you analyse it?
+
+That's an impossible question to answer completely without knowing a _lot_ about what questions you want to answer.
+However, in general, if you want to see what data is being collected by your instrumentation,
+[go to its page in Glean Dictionary][glean-dictionary]
+and you'll find links and information there about how to proceed.
+
+## Where do I learn more?
+
+Here in the [FOG User Documentation](./index) you will find FOG-specific details like
+[how to write instrumentation tests](instrumentation_tests), or
+[how to use Glean APIs to mirror data to Telemetry](gifft).
+
+Most of what you should have to concern yourself with, as an instrumentor,
+is documented in [the Book of Glean](https://mozilla.github.io/glean/book/index.html).
+Such as its [illuminating glossary][glean-glossary],
+the [list of all metric types][metrics-types],
+or the index of our long-running blog series [This Week in Glean][twig-index].
+
+And for anything else you need help with, please find us in
+[#glean:mozilla.org](https://chat.mozilla.org/#/room/#glean:mozilla.org).
+We'll be happy to help you learn more about FOG and Glean.
+
+[add-a-metric]: https://mozilla.github.io/glean/book/user/metrics/adding-new-metrics.html
+[metrics-ping]: https://mozilla.github.io/glean/book/user/pings/metrics.html
+[events-ping]: https://mozilla.github.io/glean/book/user/pings/events.html
+[modules]: https://wiki.mozilla.org/Modules/All
+[data-review]: https://wiki.mozilla.org/Data_Collection
+[glean-dictionary]: https://dictionary.telemetry.mozilla.org/
+[glean-glossary]: https://mozilla.github.io/glean/book/appendix/glossary.html
+[twig-index]: https://mozilla.github.io/glean/book/appendix/twig.html
+[metrics-types]: https://mozilla.github.io/glean/book/reference/metrics/index.html
+[file-fog-bugs]: https://bugzilla.mozilla.org/enter_bug.cgi?product=Toolkit&component=Telemetry
diff --git a/toolkit/components/glean/docs/user/gifft.md b/toolkit/components/glean/docs/user/gifft.md
new file mode 100644
index 0000000000..4c173884ce
--- /dev/null
+++ b/toolkit/components/glean/docs/user/gifft.md
@@ -0,0 +1,241 @@
+# Glean Interface For Firefox Telemetry (GIFFT)
+
+To make Migration from Firefox Telemetry to Glean easier,
+the C++ and JS Glean API can be configured
+(on a metric-by-metric basis)
+to mirror data collection to both the Glean metric and a Telemetry probe.
+
+GIFFT should ideally be used only when the data you require for analysis still mostly lives in Telemetry,
+and should be removed promptly when no longer needed.
+Instrumentors are encouraged to have the Telemetry mirror probe expire within six versions.
+(As always you can renew an expiring probe if you're still using it,
+but this will help us get closer to the time when we eventually turn Telemetry off.)
+
+**Note:** GIFFT only works for data provided via C++ or JS.
+Rust Glean metrics APIs will not mirror to Telemetry as Telemetry does not have a Rust API.
+
+**Note:** Using the Glean API replaces the Telemetry API.
+Do not use any mix of the two APIs for the same probe.
+
+## How to Mirror a Glean Metric to a Firefox Telemetry Probe
+
+For the mirror to work, you need three things:
+* A compatible Glean metric (defined in a `metrics.yaml`)
+* A compatible Telemetry probe
+ (defined in `Histograms.json`, `Scalars.yaml`, or `Events.yaml`)
+* A `telemetry_mirror` property on the Glean metric definition identifying the Telemetry probe
+
+### Compatibility
+
+This compatibility table explains which Telemetry probe types can be mirrors for which Glean metric types:
+
+| Glean Metric Type | Telementry Probe Type |
+| ----------------- | --------------------- |
+| [boolean](https://mozilla.github.io/glean/book/reference/metrics/boolean.html) | [Scalar of kind: boolean](/toolkit/components/telemetry/collection/scalars.html) |
+| [labeled_boolean](https://mozilla.github.io/glean/book/reference/metrics/labeled_booleans.html) | [Keyed scalar of kind: boolean](/toolkit/components/telemetry/collection/scalars.html) |
+| [counter](https://mozilla.github.io/glean/book/reference/metrics/counter.html) | [Scalar of kind: uint](/toolkit/components/telemetry/collection/scalars.html) |
+| [labeled_counter](https://mozilla.github.io/glean/book/reference/metrics/labeled_counters.html) | [Keyed Scalar of kind: uint](/toolkit/components/telemetry/collection/scalars.html) |
+| [string](https://mozilla.github.io/glean/book/reference/metrics/string.html) | [Scalar of kind: string](/toolkit/components/telemetry/collection/scalars.html) |
+| [labeled_string](https://mozilla.github.io/glean/book/reference/metrics/labeled_strings.html) | *No Supported Telemetry Type* |
+| [string_list](https://mozilla.github.io/glean/book/reference/metrics/string_list.html) | [Keyed Scalar of kind: boolean](/toolkit/components/telemetry/collection/scalars.html). The keys are the strings. The values are all `true`. Calling `Set` on the labeled_string is not mirrored (since there's no way to remove keys from a keyed scalar of kind boolean). Doing so will log a warning. |
+| [timespan](https://mozilla.github.io/glean/book/reference/metrics/timespan.html) | [Scalar of kind: uint](/toolkit/components/telemetry/collection/scalars.html). The value is in units of milliseconds. |
+| [timing_distribution](https://mozilla.github.io/glean/book/reference/metrics/timing_distribution.html) | [Histogram of kind "linear" or "exponential"](/toolkit/components/telemetry/collection/histograms.html#exponential). Samples will be in units of milliseconts. |
+| [memory_distribution](https://mozilla.github.io/glean/book/reference/metrics/memory_distribution.html) | [Histogram of kind "linear" or "exponential"](/toolkit/components/telemetry/collection/histograms.html#exponential). Samples will be in `memory_unit` units. |
+| [custom_distribution](https://mozilla.github.io/glean/book/reference/metrics/custom_distribution.html) | [Histogram of kind "linear" or "exponential"](/toolkit/components/telemetry/collection/histograms.html#exponential). Samples will be used as is. Ensure the bucket count and range match. |
+| [uuid](https://mozilla.github.io/glean/book/reference/metrics/uuid.html) | [Scalar of kind: string](/toolkit/components/telemetry/collection/scalars.html). Value will be in canonical 8-4-4-4-12 format. Value is not guaranteed to be valid, and invalid values may be present in the mirrored scalar while the uuid metric remains empty. Calling `GenerateAndSet` on the uuid is not mirrored, and will log a warning. |
+| [url](https://mozilla.github.io/glean/book/reference/metrics/url.html) | [Scalar of kind: string](/toolkit/components/telemetry/collection/scalars.html). The stringified Url will be cropped to the maximum length allowed by the legacy type. |
+| [datetime](https://mozilla.github.io/glean/book/reference/metrics/datetime.html) | [Scalar of kind: string](/toolkit/components/telemetry/collection/scalars.html). Value will be in ISO8601 format. |
+| [events](https://mozilla.github.io/glean/book/reference/metrics/event.html) | [Events](/toolkit/components/telemetry/collection/events.html). The `value` field will be left empty. |
+| [quantity](https://mozilla.github.io/glean/book/reference/metrics/quantity.html) | [Scalar of kind: uint](/toolkit/components/telemetry/collection/scalars.html) |
+| [rate](https://mozilla.github.io/glean/book/reference/metrics/rate.html) | [Keyed Scalar of kind: uint](/toolkit/components/telemetry/collection/scalars.html). The keys are "numerator" and "denominator". Does not work for `rate` metrics with external denominators. |
+| [text](https://mozilla.github.io/glean/book/reference/metrics/text.html) | *No Supported Telemetry Type* |
+
+### The `telemetry_mirror` property in `metrics.yaml`
+
+You must use the C++ enum identifier of the Histogram, Scalar, or Event being mirrored to:
+* For Histograms, the Telemetry C++ enum identifier is the histogram's name
+ * e.g. The C++ enum identifier for `WR_RENDERER_TIME` is
+ `WR_RENDERER_TIME` (see {searchfox}`gfx/metrics.yaml`)
+* For Scalars, the Telemetry C++ enum identifier is the Scalar category and name in
+ `SCREAMING_SNAKE_CASE` with any `.` replaced with `_`
+ * e.g. The enum identifier for `extensions.startupCache.load_time` is
+ `EXTENSIONS_STARTUPCACHE_LOAD_TIME` (see {searchfox}`toolkit/components/extensions/metrics.yaml`)
+* For Events, the Telemetry C++ enum identifier is the Event category, method, and object
+ rendered in `Snakey_CamelCase`.
+ * e.g. The enum identifier for `page_load.toplevel#content` is
+ `Page_load_Toplevel_Content` (see {searchfox}`dom/metrics.yaml`)
+
+If you use the wrong enum identifier, this will manifest as a build error.
+
+If you are having trouble finding the correct conjugation for the mirror Telemetry probe,
+you can find the specific value in the list of all Telemetry C++ enum identifiers in
+`<objdir>/toolkit/components/telemetry/Telemetry{Histogram|Scalar|Event}Enums.h`.
+(Choose the file appropriate to the type of the Telemetry mirror.)
+
+## Artifact Build Support
+
+Sadly, GIFFT does not support Artifact builds.
+You must build Firefox when you add the mirrored metric so the C++ enum value is present,
+even if you only use the metric from Javascript.
+
+## Analysis Gotchas
+
+Firefox Telemetry and the Glean SDK are very different.
+Though GIFFT bridges the differences as best it can,
+there are many things it cannot account for.
+
+These are a few of the ways that differences between Firefox Telemetry and the Glean SDK might manifest as anomalies during analysis.
+
+### Processes, Products, and Channels
+
+Like Firefox on Glean itself,
+GIFFT doesn't know what process, product, or channel it is recording in.
+Telemetry does, and imposes restrictions on which probes can be recorded to and when.
+
+Ensure that the following fields in any Telemetry mirror's definition aren't too restrictive for your use:
+* `record_in_processes`
+* `products`
+* `release_channel_collection`/`releaseChannelCollection`
+
+A mismatch won't result in an error.
+If you, for example,
+record to a Glean metric in a release channel that the Telemetry mirror probe doesn't permit,
+then the Glean metric will have a value and the Telemetry mirror probe won't.
+
+Also recall that Telemetry probes split their values across processes.
+[Glean metrics do not](../dev/ipc.md).
+This may manifest as curious anomalies when comparing the Glean metric to its Telemetry mirror probe.
+Ensure your analyses are aggregating Telemetry values from all processes,
+or define and use process-specific Glean metrics and Telemetry mirror probes to keep things separate.
+
+### Pings
+
+Glean and Telemetry both send their built-in pings on their own schedules.
+This means the values present in these pings may not agree since they reflect state at different time.
+
+For example, if you are measuring "Number of Monitors" with a
+[`quantity`](https://mozilla.github.io/glean/book/reference/metrics/quantity.html)
+sent by default in the Glean "metrics" ping mirrored to a
+[Scalar of kind: uint](/toolkit/components/telemetry/collection/scalars.rst)
+sent by default in the Telemetry "main" ping,
+then if the user plugs in a second monitor between midnight
+(when Telemetry "main" pings with reason "daily" are sent) and 4AM
+(when Glean "metrics" pings with reason "today" are sent),
+the value in the `quantity` will be `2`
+while the value in the Scalar of kind: uint will be `1`.
+
+If the metric or mirrored probe are sent in Custom pings,
+the schedules could line up exactly or be entirely unrelated.
+
+### Labels
+
+Labeled metrics supported by GIFFT
+(`labeled_boolean` and `labeled_counter`)
+adhere to the Glean SDK's
+[label format](https://mozilla.github.io/glean/book/reference/metrics/index.html#label-format).
+
+Keyed Scalars, on the other hand, do not have a concept of an "Invalid key".
+Firefox Telemetry will accept just about any sequence of bytes as a key.
+
+This means that a label deemed invalid by the Glean SDK may appear in the mirrored probe's data.
+For example, using 72 "1" characters as a label that doesn't conform to the format
+(it is longer than 71 printable ASCII characters).
+See that the `labeled_boolean` metric
+[correctly ascribes it to `__other__`](https://mozilla.github.io/glean/book/reference/metrics/index.html#labeled-metrics)
+whereas the mirrored Keyed Scalar with kind boolean stores and retrieves it without change:
+```js
+Glean.testOnly.mirrorsForLabeledBools["1".repeat(72)].set(true);
+Assert.equal(true, Glean.testOnly.mirrorsForLabeledBools.__other__.testGetValue());
+// The above actually throws NS_ERROR_LOSS_OF_SIGNIFICANT_DATA because it also records
+// an invalid_label error. But you get the idea.
+let snapshot = Services.telemetry.getSnapshotForKeyedScalars().parent;
+Assert.equal(true, snapshot["telemetry.test.mirror_for_labeled_bool"]["1".repeat(72)]);
+```
+
+### Telemetry Events
+
+A Glean event can be mirrored to a Telemetry Event.
+Telemetry Events must be enabled before they can be recorded to via the API
+`Telemetry.setEventRecordingEnabled(category, enable);`.
+If the Telemetry Event isn't enabled,
+recording to the Glean event will still work,
+and the event will be Summarized in Telemetry as all disabled events are.
+
+See
+[the Telemetry Event docs](/toolkit/components/telemetry/collection/events.rst)
+for details on how disabled Telemetry Events behave.
+
+### Numeric Values
+
+The arguments and storage formats for Glean's numeric types
+(`counter`, `labeled_counter`, `quantity`, `rate`, and `timespan`)
+are different from Telemetry's numeric type
+(Scalar of kind `uint`).
+
+This results in a few notable differences.
+
+#### Saturation and Overflow
+
+`counter`, `labeled_counter`, and `rate` metrics are stored as 32-bit signed values.
+`quantity` metrics are stored as 64-bit signed values.
+`timing_distribution` samples can be 64-bit signed values.
+All of these Glean numeric metric types saturate at their maximum representable value,
+or according to the Limits section of the Glean metric type documentation.
+
+Scalars of kind `uint` are stored as 32-bit unsigned values.
+They will overflow if they exceed the value $2^{32} - 1$.
+
+If a Glean numeric type saturates, it will record an error of type `invalid_overflow`.
+In your analyses please check for these errors.
+
+#### Quantity Value Over-size
+
+Values greater than $2^{32} - 1$ passed to a `quantity` metric's
+`set()` method will be clamped to $2^{32} - 1$ before being passed to the metric's Telemetry mirror.
+
+#### Negative Values
+
+Values less than 0 passed to any numeric metric type's API will not be passed on to the Telemetry mirror.
+This avoids small negative numbers being cast into a stunningly large numbers,
+and keeps the Telemetry mirror's value closer to that of the Glean metric.
+
+#### Long Time Spans
+
+If the number of milliseconds between calls to a
+`timespan` metric's `start()` and `stop()` methods exceeds $2^{32} - 1$,
+the value passed to the metric's Telemetry mirror will be clamped to $2^{32} - 1$.
+
+The same happens for samples in `timing_distribution` metrics:
+values passed to the Telemetry mirror histogram will saturate at $2^{32} - 1$
+until they get past $2^{64}$ when they'll overflow.
+
+#### `timing_distribution` mirrors: Samples and Sums might be Different
+
+A specific value in a `timing_distribution` metric will not always agree with
+the corresponding value in its mirrored-to histogram.
+Though the calls to the clock are very close together in the code in Telemetry and Glean,
+Telemetry's are not on the exact same instruction as Glean's _and_
+Telemetry uses a different clock source (`TimeStamp::Now()`) than Glean (`time::precise_time_ns()`).
+
+Also, if these slight drifts happen to cross the boundary of a bucket in either system,
+samples might end up looking more different than you'd expect.
+
+This shouldn't affect analysis, but it can affect testing, so please
+[bear this difference in mind](./instrumentation_tests.md#general-things-to-bear-in-mind)
+in testing.
+
+### App Shutdown
+
+Telemetry only works up to
+[`ShutdownPhase::AppShutdownTelemetry` aka `profile-before-change-telemetry`][app-shutdown].
+Telemetry data recorded after that phase just aren't persisted.
+
+FOG _presently_ shuts down Glean in a later phase,
+and so is able to collect data deeper into shutdown.
+(The particular phase is not presently something anyone's asked us to guarantee,
+so that's why I'm not being precise.)
+
+What this means is that, for data recorded later in shutdown,
+Glean will report more complete information than Telemetry will.
+
+[app-shutdown]: https://searchfox.org/mozilla-central/source/xpcom/base/AppShutdown.cpp#57
diff --git a/toolkit/components/glean/docs/user/index.md b/toolkit/components/glean/docs/user/index.md
new file mode 100644
index 0000000000..f5ddf6d9e3
--- /dev/null
+++ b/toolkit/components/glean/docs/user/index.md
@@ -0,0 +1,17 @@
+# Using Firefox on Glean
+
+This section of docs is designed to be helpful to people instrumenting Firefox Desktop.
+You may wish to begin with the [Getting Started](getting_started.md) docs.
+Or, if you're already acquainted with Glean concepts and what FOG is,
+you might want to know [how to migrate a piece of Firefox Telemetry to Glean](migration.md).
+
+```{toctree}
+:titlesonly:
+:maxdepth: 1
+:glob:
+
+getting_started
+new_definitions_file
+*
+Glean SDK Documentation <https://mozilla.github.io/glean/book/index.html>
+```
diff --git a/toolkit/components/glean/docs/user/instrumentation_tests.md b/toolkit/components/glean/docs/user/instrumentation_tests.md
new file mode 100644
index 0000000000..e457e9fd39
--- /dev/null
+++ b/toolkit/components/glean/docs/user/instrumentation_tests.md
@@ -0,0 +1,278 @@
+# Writing Instrumentation Tests
+
+```{admonition} Old Glean Proverb
+If it's important enough to be instrumented, it's important enough to be tested.
+```
+
+All metrics and pings in the Glean SDK have [well-documented APIs for testing][glean-metrics-apis].
+You'll want to familiarize yourself with `TestGetValue()`
+(here's [an example JS (xpcshell) test of some metrics][metrics-xpcshell-test])
+for metrics and
+[`TestBeforeNextSubmit()`][test-before-next-submit]
+(here's [an example C++ (gtest) test of a custom ping][ping-gtest])
+for pings.
+
+All test APIs are available in all three of FOG's supported languages:
+Rust, C++, and JavaScript.
+
+But how do you get into a position where you can even call these test APIs?
+How do they fit in with Firefox Desktop's testing frameworks?
+
+## Manual Testing and Debugging
+
+The Glean SDK has [debugging capabilities][glean-debug]
+for manually verifying that instrumentation makes it to Mozilla's Data Pipeline.
+Firefox Desktop supports these via environment variables _and_
+via the interface on `about:glean`.
+
+This is all well and good for getting a good sense check that things are going well _now_,
+but in order to check that everything stays good through the future,
+you're going to want to write some automated tests.
+
+## General Things To Bear In Mind
+
+* You may see values from previous tests persist across tests because the profile directory was shared between test cases.
+ * You can reset Glean before your test by calling
+ `Services.fog.testResetFOG()` (in JS).
+ * If your instrumentation isn't on the parent process,
+ you should call `await Services.fog.testFlushAllChildren()` before `testResetFOG`.
+ That will ensure all pending data makes it to the parent process to be cleared.
+ * You shouldn't have to do this in C++ or Rust since there you should use the
+ `FOGFixture` test fixture.
+* If your metric is based on timing (`timespan`, `timing_distribution`),
+ do not expect to be able to assert the correct timing value.
+ Glean does a lot of timing for you deep in the SDK, so unless you mock the system's monotonic clock,
+ do not expect the values to be predictable.
+ * Instead, check that a value is `> 0` or that the number of samples is expected.
+ * You might be able to assert that the value is at least as much as a known, timed value,
+ but beware of rounding.
+ * If your metric is a `timing_distribution` mirroring to a Telemetry probe via [GIFFT](./gifft.md),
+ there may be [small observed differences between systems](./gifft.md#timing-distribution-mirrors-samples-and-sums-might-be-different)
+ that can cause equality assertions to fail.
+* Errors in instrumentation APIs do not panic, throw, or crash.
+ But Glean remembers that the errors happened.
+ * Test APIs, on the other hand, are permitted
+ (some may say "encouraged")
+ to panic, throw, or crash on bad behaviour.
+ * If you call a test API and it panics, throws, or crashes,
+ that means your instrumentation did something wrong.
+ Check your test logs for details about what went awry.
+
+### Tests and Artifact Builds
+
+Artifact build support is provided by [the JOG subsystem](../dev/jog).
+It is able to register the latest versions of all metrics and pings at runtime.
+However, the compiled code is still running against the
+version of those metrics and pings that was current at the time the artifacts were compiled.
+
+This isn't a problem unless:
+* You are changing a metric or ping that is used in instrumentation in the compiled code, or
+* You are using `testBeforeNextSubmit` in JavaScript for a ping submitted in the compiled code.
+
+When in doubt, simply test your new test in artifact mode
+(by e.g. passing `--enable-artifact-builds` to `mach try`)
+before submitting it.
+If it doesn't pass in artifact mode because of one of these two cases,
+you may need to skip your test whenever FOG's artifact build support is enabled:
+* xpcshell:
+```js
+add_task(
+ { skip_if: () => Services.prefs.getBoolPref("telemetry.fog.artifact_build", false) },
+ function () {
+ // ... your test ...
+ }
+);
+```
+* mochitest:
+```js
+add_task(function () {
+ if (Services.prefs.getBoolPref("telemetry.fog.artifact_build", false)) {
+ Assert.ok(true, "Test skipped in artifact mode.");
+ return;
+ }
+ // ... your test ...
+});
+```
+
+## The Usual Test Format
+
+Instrumentation tests tend to follow the same three-part format:
+1) Assert no value in the metric
+2) Express behaviour
+3) Assert correct value in the metric
+
+Your choice of test suite will depend on how the instrumented behaviour can be expressed.
+
+
+## `xpcshell` Tests
+
+If the instrumented behaviour is on the main or content process and can be called from privileged JS,
+`xpcshell` is an excellent choice.
+
+`xpcshell` is so minimal an environment, however, that
+(pending [bug 1756055](https://bugzilla.mozilla.org/show_bug.cgi?id=1756055))
+you'll need to manually tell it you need two things:
+1) A profile directory
+2) An initialized FOG
+
+```js
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_setup(function test_setup() {
+ // FOG needs a profile directory to put its data in.
+ do_get_profile();
+
+ // FOG needs to be initialized in order for data to flow.
+ Services.fog.initializeFOG();
+});
+```
+
+From there, just follow The Usual Test Format:
+
+```js
+add_task(function test_instrumentation() {
+ // 1) Assert no value
+ Assert.equal(undefined, Glean.myMetricCategory.myMetricName.testGetValue());
+
+ // 2) Express behaviour
+ // ...<left as an exercise to the reader>...
+
+ // 3) Assert correct value
+ Assert.equal(kValue, Glean.myMetricCategory.myMetricName.testGetValue());
+});
+```
+
+If your new instrumentation includes a new custom ping,
+there are two small additions to The Usual Test Format:
+
+* 1.1) Call `testBeforeNextSubmit` _before_ your ping is submitted.
+ The callback you register in `testBeforeNextSubmit`
+ is called synchronously with the call to the ping's `submit()`.
+* 3.1) Check that the ping actually was submitted.
+ If all your Asserts are inside `testBeforeNextSubmit`'s closure,
+ another way this test could pass is by not running any of them.
+
+```js
+add_task(function test_custom_ping() {
+ // 1) Assert no value
+ Assert.equal(undefined, Glean.myMetricCategory.myMetricName.testGetValue());
+
+ // 1.1) Set up Step 3.
+ let submitted = false;
+ GleanPings.myPing.testBeforeNextSubmit(reason => {
+ submitted = true;
+ // 3) Assert correct value
+ Assert.equal(kExpectedReason, reason, "Reason of submitted ping must match.");
+ Assert.equal(kExpectedMetricValue, Glean.myMetricCategory.myMetricName.testGetValue());
+ });
+
+ // 2) Express behaviour that sends a ping with expected reason and contents
+ // ...<left as an exercise to the reader>...
+
+ // 3.1) Check that the ping actually was submitted.
+ Assert.ok(submitted, "Ping was submitted, callback was called.");
+});
+```
+
+(( We acknowledge that this isn't the most ergonomic form.
+Please follow
+[bug 1756637](https://bugzilla.mozilla.org/show_bug.cgi?id=1756637)
+for updates on a better design and implementation for ping tests. ))
+
+## mochitest
+
+`browser-chrome`-flavoured mochitests can be tested very similarly to `xpcshell`,
+though you do not need to request a profile or initialize FOG.
+`plain`-flavoured mochitests aren't yet supported (follow
+[bug 1799977](https://bugzilla.mozilla.org/show_bug.cgi?id=1799977)
+for updates and a workaround).
+
+If you're testing in `mochitest`, your instrumentation (or your test)
+might not be running in the parent process.
+This means you get to learn the IPC test APIs.
+
+### IPC
+
+All test APIs must be called on the main process
+(they'll assert otherwise).
+But your instrumentation might be on any process, so how do you test it?
+
+In this case there's a slight addition to the Usual Test Format:
+1) Assert no value in the metric
+2) Express behaviour
+3) _Flush all pending FOG IPC operations with `await Services.fog.testFlushAllChildren()`_
+4) Assert correct value in the metric.
+
+**NOTE:** We learned in
+[bug 1843178](https://bugzilla.mozilla.org/show_bug.cgi?id=1843178)
+that the list of all content processes that `Services.fog.testFlushAllChildren()`
+uses is very quickly updated after the end of a call to `BrowserUtils.withNewTab(...)`.
+If you are using `withNewTab`, you should consider calling `testFlushAllChildren()`
+_within_ the callback.
+
+## GTests/Google Tests
+
+Please make use of the `FOGFixture` fixture when writing your tests, like:
+
+```cpp
+TEST_F(FOGFixture, MyTestCase) {
+ // 1) Assert no value
+ ASSERT_EQ(mozilla::Nothing(),
+ my_metric_category::my_metric_name.TestGetValue());
+
+ // 2) Express behaviour
+ // ...<left as an exercise to the reader>...
+
+ // 3) Assert correct value
+ ASSERT_EQ(kValue,
+ my_metric_category::my_metric_name.TestGetValue().unwrap().ref());
+}
+```
+
+The fixture will take care of ensuring storage is reset between tests.
+
+## Rust `rusttests`
+
+The general-purpose
+[Testing & Debugging Rust Code in Firefox](/testing-rust-code/index)
+is a good thing to review first.
+
+Unfortunately, FOG requires gecko
+(to tell it where the profile dir is, and other things),
+which means we need to use the
+[GTest + FFI approach](/testing-rust-code/index.md#gtests)
+where GTest is the runner and Rust is just the language the test is written in.
+
+This means your test will look like a GTest like this:
+
+```cpp
+extern "C" void Rust_MyRustTest();
+TEST_F(FOGFixture, MyRustTest) { Rust_MyRustTest(); }
+```
+
+Plus a Rust test like this:
+
+```rust
+#[no_mangle]
+pub extern "C" fn Rust_MyRustTest() {
+ // 1) Assert no value
+ assert_eq!(None,
+ fog::metrics::my_metric_category::my_metric_name.test_get_value(None));
+
+ // 2) Express behaviour
+ // ...<left as an exercise to the reader>...
+
+ // 3) Assert correct value
+ assert_eq!(Some(value),
+ fog::metrics::my_metric_category::my_metric_name.test_get_value(None));
+}
+```
+
+[glean-metrics-apis]: https://mozilla.github.io/glean/book/reference/metrics/index.html
+[metrics-xpcshell-test]: https://searchfox.org/mozilla-central/rev/66e59131c1c76fe486424dc37f0a8a399ca874d4/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js#28
+[ping-gtest]: https://searchfox.org/mozilla-central/rev/66e59131c1c76fe486424dc37f0a8a399ca874d4/toolkit/components/glean/tests/gtest/TestFog.cpp#232
+[test-before-next-submit]: https://mozilla.github.io/glean/book/reference/pings/index.html#testbeforenextsubmit
+[glean-debug]: https://mozilla.github.io/glean/book/reference/debug/index.html
diff --git a/toolkit/components/glean/docs/user/migration.md b/toolkit/components/glean/docs/user/migration.md
new file mode 100644
index 0000000000..f3c86a182b
--- /dev/null
+++ b/toolkit/components/glean/docs/user/migration.md
@@ -0,0 +1,909 @@
+# Migrating Firefox Telemetry to Glean
+
+This guide aims to help you migrate individual data collections from
+[Firefox Telemetry](/toolkit/components/telemetry/index.rst)
+to
+[Glean][book-of-glean] via [Firefox on Glean](../index.md).
+
+This is intended to be a reference to help you fill out your
+[migration worksheet][migration-worksheet],
+or for mentally translating Telemetry concepts to Glean ones.
+
+```{contents}
+```
+
+## General Things To Bear In Mind
+
+You should familiarize yourself with
+[the guide on adding new metrics to Firefox Desktop](new_definitions_file.md).
+Its advice stacks with the advice included in this guide as
+(once you've figured out what kind) you will indeed be adding new metrics.
+
+There are some other broad topics specific to migrating Firefox Telemetry stuff to Glean stuff:
+
+### Process-Agnosticism: No more `record_in_processes` field
+
+Glean (and thus FOG) [doesn't know anything about processes][ipc-dev-doc]
+except what it has to in order to ensure all the data makes it to the parent process.
+Firefox Telemetry cared very much about which process was collecting which specific data,
+keeping them separate.
+
+If you collect data in multiple processes and wish to keep data from each process type separate,
+you will need to provide this separation yourself.
+
+Please see [this dev doc][ipc-dev-doc] for an example of how to do that.
+
+### Channel-Agnosticism: No more `release_channel_collection: opt-out`
+
+FOG doesn't make a differentiation between pre-release Firefox and release Firefox,
+except inasmuch as is necessary to put the correct channel in `client_info.app_channel`.
+
+This means all data is collected in all build configurations.
+
+If you wish or are required to only collect your data in pre-release Firefox,
+please avail yourself of the `EARLY_BETA_OR_EARLIER` `#define` or `AppConstant`.
+
+### File-level Product Inclusion/Exclusion: No more `products` field
+
+Glean determines which metrics are recorded in which products via
+[a dependency tree][repositories-yaml].
+This means FOG doesn't distinguish between products at the per-product level.
+
+If some of your metrics are recorded in different sets of products
+(e.g. some of your metrics are collected in both Firefox Desktop _and_ Firefox for Android,
+but others are Firefox Desktop-specific)
+you must separate them into separate [definitions files](new_definitions_file.md).
+
+### Many Definitions Files
+
+Each component is expected to own and care for its own
+[metrics definitions files](new_definitions_file.md).
+There is no centralized `Histograms.json` or `Scalars.yaml` or `Events.yaml`.
+
+Instead the component being instrumented will have its own `metrics.yaml`
+(and `pings.yaml` for any [Custom Pings][custom-pings])
+in which you will define the data.
+
+See [this guide](new_definitions_file.md) for details.
+
+### Testing
+
+Firefox Telemetry had very uneven support for testing instrumentation code.
+FOG has much better support. Anywhere you can instrument is someplace you can test.
+
+It's as simple as calling `testGetValue`.
+
+All migrated collections are expected to be tested.
+If you can't test them, then you'd better have an exceptionally good reason why not.
+
+For more details, please peruse the
+[instrumentation testing docs](instrumentation_tests).
+
+## Which Glean Metric Type Should I Use?
+
+Glean uses higher-level metric types than Firefox Telemetry does.
+This complicates migration as something that is "just a number"
+in Firefox Telemetry might map to any number of Glean metric types.
+
+Please choose the most specific metric type that solves your problem.
+This'll make analysis easier as
+1. Others will know more about how to analyse the metric from more specific types.
+2. Tooling will be able to present only relevant operations for more specific types.
+
+Example:
+> In Firefox Telemetry I record the number of monitors attached to the computer that Firefox Desktop is running on.
+> I could record this number as a [`string`][string-metric], a [`counter`][counter-metric],
+> or a [`quantity`][quantity-metric].
+> The `string` is an obvious trap. It doesn't even have the correct data type (string vs number).
+> But is it a `counter` or `quantity`?
+> If you pay attention to this guide you'll learn that `counter`s are used to accumulate sums of information,
+> whereas `quantity` metrics are used to record specific values.
+> The "sum" of monitors over time doesn't make sense, so `counter` is out.
+> `quantity` is the correct choice.
+
+## Histograms
+
+[Histograms][telemetry-histograms]
+are the oldest Firefox Telemetry data type, and as such they've accumulated
+([ha!][histogram-accumulate]) the most ways of being used.
+
+### Scalar Values in Histograms: kind `flag` and `count`
+
+If you have a Histogram that records exactly one value,
+please scroll down and look at the migration guide for the relevant Scalar:
+* For Histograms of kind `flag` see "Scalars of kind `bool`"
+* For Histograms of kind `count` see "Scalars of kind `uint`"
+
+### Continuous Distributions: kind `linear` and `exponential`
+
+If the Histogram you wish to migrate is formed of multiple buckets that together form a single continuous range
+(like you have buckets 1-5, 6-10, 11-19, and 20-50 - they form the range 1-50),
+then you will want a "distribution" metric type in Glean.
+Which kind of "distribution" metric type depends on what the samples are.
+
+#### Timing samples - Use Glean's `timing_distribution`
+
+The most common type of continuous distribution in Firefox Telemetry is a histogram of timing samples like
+[`GC_MS`][gc-ms].
+
+In Glean this sort of data is recorded using a
+[`timing-distribution`][timing-distribution-metric] metric type.
+
+You will no longer need to worry about the range of values or number or distribution of buckets
+(represented by the `low`, `high`, `n_buckets`, or `kind` in your Histogram's definition).
+Glean uses a [clever automatic bucketing algorithm][timing-distribution-metric] instead.
+
+So for a Histogram that records timing samples like this:
+
+```
+ "GC_MS": {
+ "record_in_processes": ["main", "content"],
+ "products": ["firefox", "geckoview_streaming"],
+ "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org", "jcoppeard@mozilla.com"],
+ "expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
+ "kind": "exponential",
+ "high": 10000,
+ "n_buckets": 50,
+ "bug_numbers": [1636419],
+ "description": "Time spent running JS GC (ms)"
+ },
+```
+
+You will migrate to a `timing_distibution` metric type like this:
+
+```yaml
+js:
+ gc:
+ type: timing_distribution
+ time_unit: millisecond
+ description: |
+ Time spent running the Javascript Garbage Collector.
+ Migrated from Firefox Telemetry's `GC_MS`.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1636419
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1636419#c8
+ data_sensitivity:
+ - technical
+ notification_emails:
+ - dev-telemetry-gc-alerts@mozilla.org
+ - jcoppeard@mozilla.com
+ expires: never
+```
+
+**GIFFT:** This type of collection is mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+
+#### Memory Samples - Use Glean's `memory_distribution`
+
+Another common content of `linear` or `exponential`
+Histograms in Firefox Telemetry is memory samples.
+For example, [`MEMORY_TOTAL`][memory-total]'s samples are in kilobytes.
+
+In Glean this sort of data is recorded using a
+[`memory-distribution`][memory-distribution-metric] metric type.
+
+You will no longer need to worry about the range of values or number or distribution of buckets
+(represented by the `low`, `high`, `n_buckets`, or `kind` in your Histogram's definition).
+Glean uses a [clever automatic bucketing algorithm][memory-distribution-metric] instead.
+
+So for a Histogram that records memory samples like this:
+
+```
+ "MEMORY_TOTAL": {
+ "record_in_processes": ["main"],
+ "products": ["firefox", "thunderbird"],
+ "alert_emails": ["memshrink-telemetry-alerts@mozilla.com", "amccreight@mozilla.com"],
+ "bug_numbers": [1198209, 1511918],
+ "expires_in_version": "never",
+ "kind": "exponential",
+ "low": 32768,
+ "high": 16777216,
+ "n_buckets": 100,
+ "description": "Total Memory Across All Processes (KB)",
+ "releaseChannelCollection": "opt-out"
+ },
+```
+
+You will migrate to a `memory_distribution` metric type like this:
+
+```yaml
+memory:
+ total:
+ type: memory_distribution
+ memory_unit: kilobyte
+ description: |
+ The total memory allocated across all processes.
+ Migrated from Telemetry's `MEMORY_TOTAL`.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1198209
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1511918
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1511918#c9
+ data_sensitivity:
+ - technical
+ notification_emails:
+ - memshrink-telemetry-alerts@mozilla.com
+ - amccreight@mozilla.com
+ expires: never
+```
+
+**GIFFT:** This type of collection is mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+
+#### Percentage Samples - Comment on bug 1657467
+
+A very common Histogram in Firefox Desktop is a distribution of percentage samples.
+[For example, `GC_SLICE_DURING_IDLE`][gc-idle].
+
+Glean doesn't currently have a good metric type for this.
+But we [intend to add one][new-metric-percent].
+If you are migrating a collection of this type,
+please add a comment to the bug detailing which probe you are migrating,
+and when you need it migrated by.
+We'll prioritize adding this metric type accordingly.
+
+#### Other - Use Glean's `custom_distribution`
+
+Continuous Distribution Histograms have been around long enough to have gotten weird.
+If you're migrating one of those histograms with units like
+["square root of pixels times milliseconds"][checkerboard-severity],
+we have a "catch all" metric type for you: [Custom Distribution][custom-distribution-metric].
+
+Sadly, you'll have to care about the bucketing algorithm and bucket ranges for this one.
+So for a Histogram with artisinal samples like:
+
+```
+ "CHECKERBOARD_SEVERITY": {
+ "record_in_processes": ["main", "content", "gpu"],
+ "products": ["firefox", "fennec", "geckoview_streaming"],
+ "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "botond@mozilla.com"],
+ "bug_numbers": [1238040, 1539309, 1584109],
+ "releaseChannelCollection": "opt-out",
+ "expires_in_version": "never",
+ "kind": "exponential",
+ "high": 1073741824,
+ "n_buckets": 50,
+ "description": "Opaque measure of the severity of a checkerboard event"
+ },
+```
+
+You will migrate it to a `custom_distribution` like:
+
+```yaml
+gfx.checkerboard:
+ severity:
+ type: custom_distribution
+ range_max: 1073741824
+ bucket_count: 50
+ histogram_type: exponential
+ unit: Opaque unit
+ description: >
+ An opaque measurement of the severity of a checkerboard event.
+ This doesn't have units, it's just useful for comparing two checkerboard
+ events to see which one is worse, for some implementation-specific
+ definition of "worse". The larger the value, the worse the
+ checkerboarding.
+ Migrated from Telemetry's `CHECKERBOARD_SEVERITY`.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1238040
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1539309
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+ notification_emails:
+ - gfx-telemetry-alerts@mozilla.com
+ - botond@mozilla.com
+ data_sensitivity:
+ - technical
+ expires: never
+```
+
+**TODO [Bug 1677447](https://bugzilla.mozilla.org/show_bug.cgi?id=1677447):**
+Custom Distributions aren't yet implemented in FOG. We're working on it.
+When they're done we'll see if they'll support GIFFT like the other distributions.
+
+#### Keyed Histograms with Continuous Sample Distributions - Ask on #glean:mozilla.org for assistance
+
+Glean doesn't currently have a good metric type for keyed continuous distributions
+like video play time keyed by codec.
+Please [reach out to us][glean-matrix] to explain your use-case.
+We will help you either work within what Glean currently affords or
+[design a new metric type for you][new-metric-type].
+
+### Discrete Distributions: kind `categorical`, `enumerated`, or `boolean` - Use Glean's `labeled_counter`
+
+If the samples don't fall in a continuous range and instead fall into a known number of buckets,
+Glean provides the [Labeled Counter][labeled-counter-metric] for these cases.
+
+Simply enumerate the discrete categories as `labels` in the `labeled_counter`.
+
+For example, for a Histogram of kind `categorical` like:
+
+```
+ "AVIF_DECODE_RESULT": {
+ "record_in_processes": ["main", "content"],
+ "products": ["firefox", "geckoview_streaming"],
+ "alert_emails": ["cchang@mozilla.com", "jbauman@mozilla.com"],
+ "expires_in_version": "never",
+ "releaseChannelCollection": "opt-out",
+ "kind": "categorical",
+ "labels": [
+ "success",
+ "parse_error",
+ "no_primary_item",
+ "decode_error",
+ "size_overflow",
+ "out_of_memory",
+ "pipe_init_error",
+ "write_buffer_error",
+ "alpha_y_sz_mismatch",
+ "alpha_y_bpc_mismatch"
+ ],
+ "description": "Decode result of AVIF image",
+ "bug_numbers": [1670827]
+ },
+```
+
+You would migrate to a `labeled_counter` like:
+
+```yaml
+avif:
+ decode_result:
+ type: labeled_counter,
+ description: |
+ Each AVIF image's decode result.
+ Migrated from Telemetry's `AVIF_DECODE_RESULT`.
+ labels:
+ - success
+ - parse_error
+ - no_primary_item
+ - decode_error
+ - size_overflow
+ - out_of_memory
+ - pipe_init_error
+ - write_buffer_error
+ - alpha_y_sz_mismatch
+ - alpha_y_bpc_mismatch
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1670827
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1670827#c9
+ data_sensitivity:
+ - technical
+ notification_emails:
+ - cchang@mozilla.com
+ - jbauman@mozilla.com
+ expires: never
+```
+
+**N.B:** Glean Labels have a strict regex.
+You may have to transform some categories to
+`snake_case` so that they're safe for the data pipeline.
+
+**GIFFT:** This type of collection is mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+**N.B.:** This will mirror back as a Keyed Scalar of kind `uint`,
+not as any kind of Histogram,
+so your original un-migrated histogram cannot be used as the mirror.
+
+#### Keyed Histograms with Discrete Sample Distributions: `"keyed": true` and kind `categorical`, `enumerated`, or `boolean` - Comment on bug 1657470
+
+Glean doesn't currently have a good metric type for this.
+But we [intend to add one][new-metric-keyed-categorical].
+If you are migrating a collection of this type,
+please add a comment to the bug detailing which probe you are migrating,
+and when you need it migrated by.
+We'll prioritize adding this metric type accordingly.
+
+## Scalars
+
+[Scalars][telemetry-scalars] are low-level individual data collections with a variety of uses.
+
+### Scalars of `kind: uint` that you call `scalarAdd` on - Use Glean's `counter`
+
+The most common kind of Scalar is of `kind: uint`.
+The most common use of such a scalar is to repeatedly call `scalarAdd`
+on it as countable things happen.
+
+The Glean metric type for countable things is [the `counter` metric type][counter-metric].
+
+So for a Scalar like this:
+
+```yaml
+script.preloader:
+ mainthread_recompile:
+ bug_numbers:
+ - 1364235
+ description:
+ How many times we ended up recompiling a script from the script preloader
+ on the main thread.
+ expires: "100"
+ keyed: false
+ kind: uint
+ notification_emails:
+ - dothayer@mozilla.com
+ - plawless@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ - 'fennec'
+ record_in_processes:
+ - 'main'
+ - 'content'
+```
+
+You will migrate to a `counter` metric type like this:
+
+```yaml
+script.preloader:
+ mainthread_recompile:
+ type: counter
+ description: |
+ How many times we ended up recompiling a script from the script preloader
+ on the main thread.
+ Migrated from Telemetry's `script.preloader.mainthread_recompile`.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1364235
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1364235#c25
+ data_sensitivity:
+ - technical
+ notification_emails:
+ - dothayer@mozilla.com
+ - plawless@mozilla.com
+ expires: "100"
+```
+
+**GIFFT:** This type of collection is mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+
+#### Keyed Scalars of `kind: uint` that you call `scalarAdd` on - Use Glean's `labeled_counter`
+
+Another very common use of Scalars is to have a Keyed Scalar of
+`kind: uint`. This was often used to track UI usage.
+
+This is supported by the [Glean `labeled_counter` metric type][labeled-counter-metric].
+
+So for a Keyed Scalar of `kind: uint` like this:
+
+```yaml
+urlbar:
+ tips:
+ bug_numbers:
+ - 1608461
+ description: >
+ A keyed uint recording how many times particular tips are shown in the
+ Urlbar and how often their confirm and help buttons are pressed.
+ expires: never
+ kind: uint
+ keyed: true
+ notification_emails:
+ - email@example.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - main
+```
+
+You would migrate it to a `labeled_counter` like this:
+
+```yaml
+urlbar:
+ tips:
+ type: labeled_counter
+ description: >
+ A keyed uint recording how many times particular tips are shown in the
+ Urlbar and how often their confirm and help buttons are pressed.
+ Migrated from Telemetry's `urlbar.tips`.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1608461
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1608461#c42
+ data_sensitivity:
+ - interaction
+ expires: never
+ notification_emails:
+ - email@example.com
+```
+
+Now, if your Keyed Scalar has a list of known keys,
+you should provide it to the `labeled_counter` using the `labels` property like so:
+
+```yaml
+urlbar:
+ tips:
+ type: labeled_counter
+ labels:
+ - tabtosearch_onboard_shown
+ - tabtosearch_shown
+ - searchtip_onboard_shown
+ ...
+```
+
+**N.B:** Glean Labels have a strict regex.
+You may have to transform some categories to
+`snake_case` so that they're safe for the data pipeline.
+
+**GIFFT:** This type of collection is mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+
+### Scalars of `kind: uint` that you call `scalarSet` on - Use Glean's `quantity`
+
+Distinct from counts which are partial sums,
+Scalars of `kind: uint` that you _set_ could contain just about anything.
+The best metric type depends on the type of data you're setting
+(See "Other Scalar-ish types" for some possibilities).
+
+If it's a numerical value you are setting, chances are you will be best served by
+[Glean's `quantity` metric type][quantity-metric].
+
+For a such a quantitative Scalar like:
+
+```yaml
+gfx.display:
+ primary_height:
+ bug_numbers:
+ - 1594145
+ description: >
+ Height of the primary display, takes device rotation into account.
+ expires: never
+ kind: uint
+ notification_emails:
+ - gfx-telemetry-alerts@mozilla.com
+ - ktaeleman@mozilla.com
+ products:
+ - 'geckoview_streaming'
+ record_in_processes:
+ - 'main'
+ release_channel_collection: opt-out
+```
+
+You would migrate it to a `quantity` like:
+
+```yaml
+gfx.display:
+ primary_height:
+ type: quantity
+ unit: pixels
+ description: >
+ Height of the primary display, takes device rotation into account.
+ Migrated from Telemetry's `gfx.display.primary_height`.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1594145
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1687219
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1594145#c4
+ data_sensitivity:
+ - technical
+ notification_emails:
+ - gfx-telemetry-alerts@mozilla.com
+ expires: never
+```
+
+Note the required `unit` property.
+
+**GIFFT:** This type of collection is mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+
+**IPC Note:** Due to `set` not being a [commutative operation][ipc-docs], using `quantity`
+on non-parent processes is forbidden.
+This is a restriction that favours correctness over friendliness,
+which we may revisit if enough use cases require it.
+Please [contact us][glean-matrix] if you'd like us to do so.
+
+#### Keyed Scalars of `kind: uint` that you call `scalarSet` on - Ask on #glean:mozilla.org for assistance
+
+Glean doesn't currently have a good metric type for keyed quantities.
+Please [reach out to us][glean-matrix] to explain your use-case.
+We will help you either work within what Glean currently affords or
+[design a new metric type for you][new-metric-type].
+
+### Scalars of `kind: uint` that you call `scalarSetMaximum` or some combination of operations on - Ask on #glean:mozilla.org for assistance
+
+Glean doesn't currently have a good metric type for dealing with maximums,
+or for dealing with values you both count and set.
+Please [reach out to us][glean-matrix] to explain your use-case.
+We will help you either work within what Glean currently affords or
+[design a new metric type for you][new-metric-type].
+
+### Scalars of `kind: string` - Use Glean's `string`
+
+If your string value is a unique identifier, then consider
+[Glean's `uuid` metric type][uuid-metric] first.
+
+If the string scalar value doesn't fit that or any other more specific metric type,
+then [Glean's `string` metric type][string-metric] will do.
+
+For a Scalar of `kind: string` like:
+
+```yaml
+widget:
+ gtk_version:
+ bug_numbers:
+ - 1670145
+ description: >
+ The version of Gtk 3 in use.
+ kind: string
+ expires: never
+ notification_emails:
+ - layout-telemetry-alerts@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ record_in_processes:
+ - 'main'
+```
+
+You will migrate it to a `string` metric like:
+
+```yaml
+widget:
+ gtk_version:
+ type: string
+ description: >
+ The version of Gtk 3 in use.
+ Migrated from Telemetry's `widget.gtk_version`.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1670145
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1670145#c7
+ data_sensitivity:
+ - technical
+ notification_emails:
+ - layout-telemetry-alerts@mozilla.com
+ expires: never
+```
+
+**GIFFT:** This type of collection is mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+
+**IPC Note:** Due to `set` not being a [commutative operation][ipc-docs], using `string`
+on non-parent processes is forbidden.
+This is a restriction that favours correctness over friendliness,
+which we may revisit if enough use cases require it.
+Please [contact us][glean-matrix] if you'd like us to do so.
+
+### Scalars of `kind: boolean` - Use Glean's `boolean`
+
+If you need to store a simple true/false,
+[Glean's `boolean` metric type][boolean-metric] is likely best.
+
+If you have more that just `true` and `false` to store,
+you may prefer a `labeled_counter`.
+
+For a Scalar of `kind: boolean` like:
+
+```yaml
+widget:
+ dark_mode:
+ bug_numbers:
+ - 1601846
+ description: >
+ Whether the OS theme is dark.
+ expires: never
+ kind: boolean
+ notification_emails:
+ - layout-telemetry-alerts@mozilla.com
+ - cmccormack@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ - 'fennec'
+ record_in_processes:
+ - 'main'
+```
+
+You would migrate to a `boolean` metric type like:
+
+```yaml
+widget:
+ dark_mode:
+ type: boolean
+ description: >
+ Whether the OS theme is dark.
+ Migrated from Telemetry's `widget.dark_mode`.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1601846
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1601846#c5
+ data_sensitivity:
+ - technical
+ notification_emails:
+ - layout-telemetry-alerts@mozilla.com
+ - cmccormack@mozilla.com
+ expires: never
+```
+
+**GIFFT:** This type of collection is mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+
+**IPC Note:** Due to `set` not being a [commutative operation][ipc-docs], using `boolean`
+on non-parent processes is forbidden.
+This is a restriction that favours correctness over friendliness,
+which we may revisit if enough use cases require it.
+Please [contact us][glean-matrix] if you'd like us to do so.
+
+#### Keyed Scalars of `kind: boolean` - Use Glean's `labeled_boolean`
+
+If you have multiple related true/false values, you may have put them in a
+Keyed Scalar of `kind: boolean`.
+
+The best match for this is
+[Glean's `labeled_boolean` metric type][labeled-boolean-metric].
+
+For a Keyed Scalar of `kind: boolean` like:
+
+```yaml
+devtools.tool:
+ registered:
+ bug_numbers:
+ - 1447302
+ - 1503568
+ - 1587985
+ description: >
+ Recorded on enable tool checkbox check/uncheck in Developer Tools options
+ panel. Boolean stating if the tool was enabled or disabled by the user.
+ Keyed by tool id. Current default tools with their id's are defined in
+ https://searchfox.org/mozilla-central/source/devtools/client/definitions.js
+ expires: never
+ kind: boolean
+ keyed: true
+ notification_emails:
+ - dev-developer-tools@lists.mozilla.org
+ - accessibility@mozilla.com
+ release_channel_collection: opt-out
+ products:
+ - 'firefox'
+ - 'fennec'
+ record_in_processes:
+ - 'main'
+```
+
+You would migrate to a `labeled_boolean` like:
+
+```yaml
+devtools.tool:
+ registered:
+ type: labeled_boolean
+ description: >
+ Recorded on enable tool checkbox check/uncheck in Developer Tools options
+ panel. Boolean stating if the tool was enabled or disabled by the user.
+ Migrated from Telemetry's `devtools.tool`.
+ labels:
+ - options
+ - inspector
+ - webconsole
+ - jsdebugger
+ - styleeditor
+ - performance
+ - memory
+ - netmonitor
+ - storage
+ - dom
+ - accessibility
+ - application
+ - dark
+ - light
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1447302
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1503568
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1587985
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1447302#c17
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1503568#c3
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1587985#c5
+ data_sensitivity:
+ - interaction
+ notification_emails:
+ - dev-developer-tools@lists.mozilla.org
+ - accessibility@mozilla.com
+ expires: never
+```
+
+**N.B:** Glean Labels have a strict regex.
+You may have to transform some categories to
+`snake_case` so that they're safe for the data pipeline.
+
+**GIFFT:** This type of collection is mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+
+**IPC Note:** Due to `set` not being a [commutative operation][ipc-docs], using `labeled_boolean`
+on non-parent processes is forbidden.
+This is a restriction that favours correctness over friendliness,
+which we may revisit if enough use cases require it.
+Please [contact us][glean-matrix] if you'd like us to do so.
+
+### Other Scalar-ish types: `rate`, `timespan`, `datetime`, `uuid`
+
+The Glean SDK provides some very handy higher-level metric types for specific data.
+If your data
+* Is two or more numbers that are related (like failure count vs total count),
+ then consider the [Glean `rate` metric type][rate-metric].
+* Is a single duration or span of time (like how long Firefox takes to start),
+ then consider the [Glean `timespan` metric type][timespan-metric].
+* Is a single point in time (like the most recent sync time),
+ then consider the [Glean `datetime` metric type][datetime-metric].
+* Is a unique identifier (like a session id),
+ then consider the [Glean `uuid` metric type][uuid-metric].
+
+**GIFFT:** These types of collection are mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+
+## Events - Use Glean's `event`
+
+[Telemetry Events][telemetry-events]
+are a lesser-used form of data collection in Firefox Desktop.
+Glean aimed to remove some of the stumbling blocks facing instrumentors when using events
+in the [Glean `event` metric type][event-metric]:
+
+* Don't worry about enabling event categories.
+ In Glean all `events` are always on.
+* No more event `name`.
+ Events in Glean follow the same `category.name.metric_name`
+ naming structure that other metrics do.
+* No more `method`/`object`/`value`.
+ Events in Glean are just their identifier and an `extras` key/value dictionary.
+
+Since the two Event types aren't that analogous you will need to decide if your event
+* Prefers to put its `method`/`object`/`value` in the `extras` dictionary
+* Prefers to fold its `method`/`object`/`value` into its identifier
+
+**GIFFT:** Events are mirrorable back to Firefox Telemetry via the
+[Glean Interface For Firefox Telemetry][gifft].
+See [the guide][gifft] for instructions.
+
+## Other: Environment, Crash Annotations, Use Counters, Etc - Ask on #glean:mozilla.org for assistance
+
+Telemetry has a lot of collection subsystems build adjacent to those already mentioned.
+We have solutions for the common ones,
+but they are entirely dependent on the specific use case.
+Please [reach out to us][glean-matrix] to explain it to us so we can help you either
+work within what Glean currently affords or
+[design a new metric type for you][new-metric-type].
+
+[book-of-glean]: https://mozilla.github.io/glean/book/index.html
+[gc-ms]: https://glam.telemetry.mozilla.org/firefox/probe/gc_ms/explore
+[histogram-accumulate]: https://searchfox.org/mozilla-central/rev/d59bdea4956040e16113b05296c56867f761735b/toolkit/components/telemetry/core/Telemetry.h#61
+[ipc-docs]: ../dev/ipc.md
+[gifft]: gifft.md
+[memory-total]: https://glam.telemetry.mozilla.org/firefox/probe/memory_total/explore
+[migration-worksheet]: https://docs.google.com/spreadsheets/d/1uEK7zSIJDcGGmof9NywP5AwaovVQCv_Bm3iNqibtESI/edit#gid=0
+[boolean-metric]: https://mozilla.github.io/glean/book/reference/metrics/boolean.html
+[labeled-boolean-metric]: https://mozilla.github.io/glean/book/reference/metrics/labeled_booleans.html
+[counter-metric]: https://mozilla.github.io/glean/book/reference/metrics/counter.html
+[labeled-counter-metric]: https://mozilla.github.io/glean/book/reference/metrics/labeled_counters.html
+[string-metric]: https://mozilla.github.io/glean/book/reference/metrics/string.html
+[labeled-string-metric]: https://mozilla.github.io/glean/book/reference/metrics/labeled_strings.html
+[timespan-metric]: https://mozilla.github.io/glean/book/reference/metrics/timespan.html
+[timing-distribution-metric]: https://mozilla.github.io/glean/book/reference/metrics/timing_distribution.html
+[memory-distribution-metric]: https://mozilla.github.io/glean/book/reference/metrics/memory_distribution.html
+[uuid-metric]: https://mozilla.github.io/glean/book/reference/metrics/uuid.html
+[datetime-metric]: https://mozilla.github.io/glean/book/reference/metrics/datetime.html
+[event-metric]: https://mozilla.github.io/glean/book/reference/metrics/event.html
+[custom-distribution-metric]: https://mozilla.github.io/glean/book/reference/metrics/custom_distribution.html
+[quantity-metric]: https://mozilla.github.io/glean/book/reference/metrics/quantity.html
+[rate-metric]: https://mozilla.github.io/glean/book/reference/metrics/rate.html
+[ipc-dev-doc]: ../dev/ipc.md
+[gc-idle]: https://glam.telemetry.mozilla.org/firefox/probe/gc_slice_during_idle/explore
+[new-metric-keyed-categorical]: https://bugzilla.mozilla.org/show_bug.cgi?id=1657470
+[new-metric-percent]: https://bugzilla.mozilla.org/show_bug.cgi?id=1657467
+[new-metric-type]: https://wiki.mozilla.org/Glean/Adding_or_changing_Glean_metric_types
+[glean-matrix]: https://chat.mozilla.org/#/room/#glean:mozilla.org
+[checkerboard-severity]: https://searchfox.org/mozilla-central/rev/d59bdea4956040e16113b05296c56867f761735b/gfx/layers/apz/src/CheckerboardEvent.cpp#44
+[telemetry-events]: /toolkit/components/telemetry/collection/events.rst
+[telemetry-scalars]: /toolkit/components/telemetry/collection/scalars.rst
+[telemetry-histograms]: /toolkit/components/telemetry/collection/histograms.rst
+[repositories-yaml]: https://github.com/mozilla/probe-scraper/blob/main/repositories.yaml
diff --git a/toolkit/components/glean/docs/user/new_definitions_file.md b/toolkit/components/glean/docs/user/new_definitions_file.md
new file mode 100644
index 0000000000..b9d1ada8f3
--- /dev/null
+++ b/toolkit/components/glean/docs/user/new_definitions_file.md
@@ -0,0 +1,116 @@
+# New Metrics and Pings
+
+To add a new metric or ping to Firefox Desktop you should follow the
+[Glean SDK documentation on the subject](https://mozilla.github.io/glean/book/user/adding-new-metrics.html),
+with some few twists we detail herein:
+
+## Testing
+
+Instrumentation, being code, should be tested.
+Firefox on Glean [supports a wide variety of Firefox Desktop test suites][instrumentation-tests]
+in addition to [Glean's own debugging mechanisms][glean-debug].
+
+## IPC
+
+Firefox Desktop is made of multiple processes.
+You can record data from any process in Firefox Desktop
+[subject to certain conditions](../dev/ipc.md).
+
+If you will be recording data to this metric in multiple processes,
+you should make yourself aware of those conditions.
+
+## Where do I Define new Metrics and Pings?
+
+Metrics and pings are defined in their definitions files
+(`metrics.yaml` or `pings.yaml`, respectively).
+But where can you find `metrics.yaml` or `pings.yaml`?
+
+If you're not the first person in your component to ask that question,
+the answer is likely "in the root of your component".
+Look for the definitions files near to where you are instrumenting your code.
+Or you can look in
+`toolkit/components/glean/metrics_index.py`
+to see the list of all currently-known definitions files.
+
+If you _are_ the first person in your component to ask that question,
+you get to choose where to start them!
+We recommend adding them in the root of your component, next to a `moz.build`.
+Be sure to link to this document at the top of the file!
+It contains many useful tidbits of information that anyone adding new metrics should know.
+Preferably, use this blank template to get started,
+substituting your component's `product :: component` tag from
+[the list](https://searchfox.org/mozilla-central/source/toolkit/components/glean/tags.yaml):
+
+```yaml
+# 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/.
+
+# Adding a new metric? We have docs for that!
+# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html
+
+---
+$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
+$tags:
+ - 'Your Product :: Your Component'
+
+```
+
+If you add a new definitions file, be sure to edit
+`toolkit/components/glean/metrics_index.py`,
+adding your definitions files to the Python lists therein.
+If you don't, no API will be generated for your metrics and your build will fail.
+You will have to decide which products your metrics will be used in.
+For code that's also used in other Gecko-based products (Firefox Desktop, Firefox for Android, Focus for Android), use `gecko_metrics`.
+For Desktop-only instrumentation use `firefox_desktop_metrics`.
+For other products use their respective lists.
+
+Changes to `metrics_index.py` are automatically reflected in the data pipeline once a day
+using the [fog-updater automation in probe-scraper](https://github.com/mozilla/probe-scraper/tree/main/fog-updater).
+Data will not show up in datasets and tools until this happens.
+If something is unclear or data is not showing up in time you will need to file a bug in
+`Data Platform and Tools :: General`.
+
+If you have any questions, be sure to ask on
+[the #glean channel](https://chat.mozilla.org/#/room/#glean:mozilla.org).
+
+**Note:** Do _not_ use `toolkit/components/glean/metrics.yaml`
+or `toolkit/components/glean/pings.yaml`.
+These are for metrics instrumenting the code under `toolkit/components/glean`
+and are not general-purpose locations for adding metrics and pings.
+
+## How does Expiry Work?
+
+In FOG,
+unlike in other Glean-SDK-using projects,
+metrics expire based on Firefox application version.
+This is to allow metrics to be valid over the entire life of an application version,
+whether that is the 4-6 weeks of usual releases or the 13 months of ESR releases.
+
+There are three values accepted in the `expires` field of `metrics.yaml`s for FOG:
+* `"X"` (where `X` is the major portion of a Firefox Desktop version) -
+ The metric will be expired when the `MOZ_APP_VERSION` reaches or exceeds `X`.
+ (For example, when the Firefox Version is `88.0a1`,
+ all metrics marked with `expires: "88"` or lower will be expired.)
+ This is the recommended form for all new metrics to ensure they stop recording when they stop being relevant.
+* `expired` - For marking a metric as manually expired.
+ Not usually used, but sometimes helpful for internal tests.
+* `never` - For marking a metric as part of a permanent data collection.
+ Metrics marked with `never` must have
+ [instrumentation tests](instrumentation_tests).
+
+For more information on what expiry means and the
+`metrics.yaml` format, see
+[the Glean SDK docs](https://mozilla.github.io/glean/book/user/metric-parameters.html)
+on this subject. Some quick facts:
+
+* Data collected to expired metrics is not recorded or sent.
+* Recording to expired metrics is not an error at runtime.
+* Expired metrics being in a `metrics.yaml` is a linting error in `glean_parser`.
+* Expired (and non-expired) metrics that are no longer useful should be promptly removed from your `metrics.yaml`.
+ This reduces the size and improves the performance of Firefox
+ (and speeds up the Firefox build process)
+ by decreasing the amount of code that needs to be generated.
+
+[instrumentation-tests]: ./instrumentation_tests
+[glean-debug]: https://mozilla.github.io/glean/book/reference/debug/index.html