Bug 1626969 - Include locale in Glean pings.r=chutten,supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D185153
This commit is contained in:
Travis Long 2023-08-07 11:02:56 +00:00
parent ba2e07af4d
commit aa6cb926b7
45 changed files with 616 additions and 285 deletions

8
Cargo.lock generated
View file

@ -2235,9 +2235,9 @@ dependencies = [
[[package]]
name = "glean"
version = "53.1.0"
version = "53.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0efbf048a79e634cd5ccd224f972018e3f217c72d4071bbe6ebee382037bffcb"
checksum = "df470f5f41c8fc6113bd48fa18e0f1a8193e1e2b1f3942f8255b064e60db42ff"
dependencies = [
"chrono",
"crossbeam-channel",
@ -2255,9 +2255,9 @@ dependencies = [
[[package]]
name = "glean-core"
version = "53.1.0"
version = "53.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826ac72df83806896eda459414972a0ca288d4d206811fd10a8a00a581b85498"
checksum = "6f72ce41516d772676db57c87fee1c99545bbda4b430793f24d0c81918cfbd8a"
dependencies = [
"android_logger",
"bincode",

View file

@ -36,7 +36,7 @@ allprojects {
topsrcdir = gradle.mozconfig.topsrcdir
topobjdir = gradle.mozconfig.topobjdir
gleanVersion = "53.1.0"
gleanVersion = "53.2.0"
if (gleanVersion != getRustVersionFor("glean")) {
throw new StopExecutionException("Mismatched Glean version, expected: ${gleanVersion}," +
" found ${getRustVersionFor("glean")}")

8
gfx/wr/Cargo.lock generated
View file

@ -987,9 +987,9 @@ dependencies = [
[[package]]
name = "glean"
version = "53.1.0"
version = "53.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0efbf048a79e634cd5ccd224f972018e3f217c72d4071bbe6ebee382037bffcb"
checksum = "df470f5f41c8fc6113bd48fa18e0f1a8193e1e2b1f3942f8255b064e60db42ff"
dependencies = [
"chrono",
"crossbeam-channel",
@ -1007,9 +1007,9 @@ dependencies = [
[[package]]
name = "glean-core"
version = "53.1.0"
version = "53.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826ac72df83806896eda459414972a0ca288d4d206811fd10a8a00a581b85498"
checksum = "6f72ce41516d772676db57c87fee1c99545bbda4b430793f24d0c81918cfbd8a"
dependencies = [
"android_logger",
"bincode",

View file

@ -52,7 +52,7 @@ svg_fmt = "0.4"
tracy-rs = "0.1.2"
derive_more = { version = "0.99", default-features = false, features = ["add_assign"] }
etagere = "0.2.6"
glean = "53.1.0"
glean = "53.2.0"
firefox-on-glean = { version = "0.1.0", optional = true }
swgl = { path = "../swgl", optional = true }
topological-sort = "0.1"

View file

@ -25,7 +25,7 @@ tracy-rs = "0.1.2"
log = "0.4"
lazy_static = "1"
fxhash = "0.2.1"
glean = { version = "53.1.0", optional = true }
glean = { version = "53.2.0", optional = true }
firefox-on-glean = { version = "0.1.0", optional = true }
serde = { optional = true, version = "1.0", features = ["serde_derive"] }

View file

@ -6,7 +6,8 @@
Classes = [
{
'js_name': 'locale',
'name': 'Locale',
'js_name': 'locale',
'cid': '{92735ff4-6384-4ad6-8508-757010e149ee}',
'contract_ids': ['@mozilla.org/intl/localeservice;1'],
'interfaces': ['mozILocaleService'],

View file

@ -89,7 +89,7 @@ vendored:third_party/python/wheel
vendored:third_party/python/zipp
# glean-sdk may not be installable if a wheel isn't available
# and it has to be built from source.
pypi-optional:glean-sdk==53.1.0:telemetry will not be collected
pypi-optional:glean-sdk==53.2.0:telemetry will not be collected
# Mach gracefully handles the case where `psutil` is unavailable.
# We aren't (yet) able to pin packages in automation, so we have to
# support down to the oldest locally-installed version (5.4.2).

View file

@ -191,15 +191,15 @@ user-login = "jrmuizel"
user-name = "Jeff Muizelaar"
[[publisher.glean]]
version = "53.1.0"
when = "2023-06-28"
version = "53.2.0"
when = "2023-08-02"
user-id = 48
user-login = "badboy"
user-name = "Jan-Erik Rediger"
[[publisher.glean-core]]
version = "53.1.0"
when = "2023-06-28"
version = "53.2.0"
when = "2023-08-02"
user-id = 48
user-login = "badboy"
user-name = "Jan-Erik Rediger"
@ -1233,3 +1233,15 @@ criteria = "safe-to-deploy"
version = "1.4.0"
notes = "I have read over the macros, and audited the unsafe code."
aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml"
[[audits.mozilla.audits.time]]
who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
delta = "0.1.45 -> 0.3.17"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
[[audits.mozilla.audits.time-macros]]
who = "Kershaw Chang <kershaw@mozilla.com>"
criteria = "safe-to-deploy"
version = "0.2.6"
aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"

View file

@ -1,13 +1,11 @@
Metadata-Version: 2.1
Name: glean-parser
Version: 7.2.1
Version: 8.1.0
Summary: Parser tools for Mozilla's Glean telemetry
Home-page: https://github.com/mozilla/glean_parser
Author: The Glean Team
Author-email: glean-team@mozilla.com
License: UNKNOWN
Keywords: glean_parser
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
@ -19,6 +17,8 @@ Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: AUTHORS.md
Requires-Dist: appdirs (>=1.4)
Requires-Dist: Click (>=7)
Requires-Dist: diskcache (>=4)
@ -86,6 +86,17 @@ $ glean_parser check < ping.json
## Unreleased
## 8.1.0
- Increased the maximum metric name length in version 2.0.0 schema ([#596](https://github.com/mozilla/glean_parser/pull/596))
## 8.0.0
- BREAKING CHANGE: Remove exposed `lint_yaml_files` function ([#580](https://github.com/mozilla/glean_parser/pull/580))
- Rust: Removed `__glean_metric_maps` from the Rust Jinja template. This functionality is better placed downstream ([Bug 1816526](https://bugzilla.mozilla.org/show_bug.cgi?id=1816526))
- New lint: check that all referenced pings are known ([#584](https://github.com/mozilla/glean_parser/pull/584))
- Add experimental server-side JavaScript outputter ([FXA-7922](https://mozilla-hub.atlassian.net/browse/FXA-7922))
## 7.2.1
- Unbreak last minor release ([#579](https://github.com/mozilla/glean_parser/pull/579))
@ -722,5 +733,3 @@ $ glean_parser check < ping.json
## 0.1.0 (2018-10-15)
- First release on PyPI.

View file

@ -3,38 +3,40 @@ glean_parser/__main__.py,sha256=7kIBMO-kL7boJxYrKp3CkRr4xX4_ct4BqCiCvtg2jjU,8631
glean_parser/coverage.py,sha256=2IwC4XMDtDamMkBFoYilmqJzW4gyypq65YVCur8SNas,4405
glean_parser/data_review.py,sha256=BweeeTkNNS6HrIDkztawhbDByrk_-Avxpg7YeST3VAs,2152
glean_parser/javascript.py,sha256=w4ZhNBHBKWYk0h3t7G0Ud2tR__hRqzn9dlEXNKLdQrA,11230
glean_parser/javascript_server.py,sha256=a-mpoSZ_ZnDWtRGKIVxH6NxSUST42yP9vJU85kQcRKE,6004
glean_parser/kotlin.py,sha256=5z8_74xlqvHDsedwZhGf1_qb7swPEgIZumkJIuj3ef8,12598
glean_parser/lint.py,sha256=A21ZKb9WSrgug6t8q1YHvXUxlB198xrkmZ26HCUDSlE,16303
glean_parser/lint.py,sha256=Ik8n_L4HERuHk0kU83N8fjdQ1avE3wKi0WgkAf-c5hk,18248
glean_parser/markdown.py,sha256=GkCr1CrV6mnRQseT6FO1-JJ7Eup8X3lxUfRMBTxXpe4,9066
glean_parser/metrics.py,sha256=CSad9CbUWKF771Z82LhBAFAL9uXum0ycRwIHtl_i91E,12384
glean_parser/metrics.py,sha256=2IuTEN8bNUEDwwS0kSXW-nEf040LvycTMY9Zg0Rr1m4,12431
glean_parser/parser.py,sha256=cUOnvSXKfEBg8YTpRcWiPcMwpFpK1TTqsVO_zjUtpR4,15309
glean_parser/pings.py,sha256=yh_DzRAI9k2_NiCIlpQiNg-ggVrttB4hk7gwtKlr72s,2815
glean_parser/rust.py,sha256=PJzTfYWzAumJYCP5IYPc6fhS_Qa30Q8NTK9plg3sDnk,6744
glean_parser/swift.py,sha256=T1BSGahd9wUd6VDeNC89SdN6M34jKXDlydMpSI0QLOs,8379
glean_parser/tags.py,sha256=bemKYvcbMO4JrghiNSe-A4BNNDtx_FlUPkgrPPJy84Y,1391
glean_parser/translate.py,sha256=S_a4PMXt3PyD7Wg35OM4xHEwPraqkcJzm_w95IEegPU,7962
glean_parser/translate.py,sha256=Z28qdCFXKKT6ILu4FQEO6PfJ6Ia8MCFYYP2ArgK4mF8,8148
glean_parser/translation_options.py,sha256=Lxzr6G7MP0tC_ZYlZXftS4j0SLiqO-5mGVTEc7ggXis,2037
glean_parser/util.py,sha256=Hei33QDq4a_lIHp5j98KovN6C7tmLrvVamEX2a1DcTo,16825
glean_parser/validate_ping.py,sha256=0TNvILH6dtzJDys3W8Kqorw6kk03me73OCUDtpoHcXU,2118
glean_parser/schemas/metrics.1-0-0.schema.yaml,sha256=cND3cvi6iBfPUVmtfIBQfGJV9AALpbvN7nu8E33_J-o,19566
glean_parser/schemas/metrics.2-0-0.schema.yaml,sha256=SOgqMzRs9QxyCBhjZwUhzlryeNLeaVAKMTwggG7XtQk,23843
glean_parser/schemas/metrics.2-0-0.schema.yaml,sha256=wAlXT7hKZDzF1P8yyIftkmIhrcRhJ1XEjZV0FUTIks8,25849
glean_parser/schemas/pings.1-0-0.schema.yaml,sha256=hwCnsKpEysmrmVp-QHGBArEkVY3vaU1rVsxlTwhAzws,4315
glean_parser/schemas/pings.2-0-0.schema.yaml,sha256=rD1s-rfz1xC9biHyLfBCnsoQxVYHwpe_S05awfe2xDA,4363
glean_parser/schemas/tags.1-0-0.schema.yaml,sha256=OGXIJlvvVW1vaqB_NVZnwKeZ-sLlfH57vjBSHbj6DNI,1231
glean_parser/templates/data_review.jinja2,sha256=jeYU29T1zLSyu9fKBBFu5BFPfIw8_hmOUXw8RXhRXK8,3287
glean_parser/templates/javascript.buildinfo.jinja2,sha256=4mXiZCQIk9if4lxlA05kpSIL4a95IdwGwqle2OqqNAs,474
glean_parser/templates/javascript.jinja2,sha256=cT_bG-jC6m4afECXmcsqHwiiHjRuVtJnfv90OD2Mwxw,2669
glean_parser/templates/javascript_server.jinja2,sha256=0kVF0yrVpNs1WnNlIAyuJVFS0QzasBD7J5Z0Blav_io,5246
glean_parser/templates/kotlin.buildinfo.jinja2,sha256=X0lk2SNu5OIIj2i6mUyF9CWFQIonLgfqkgT5fA-5G6c,920
glean_parser/templates/kotlin.geckoview.jinja2,sha256=MJOgtoDXmBjE9pwk-G6T89y36RZuMbDWM_-DBN_gFJo,5099
glean_parser/templates/kotlin.jinja2,sha256=3DqUMXJRkmTvSp_5IRyvGmw5iXYWdox7coMFe3YDxcc,5247
glean_parser/templates/markdown.jinja2,sha256=vAHHGGm28HRDPd3zO_wQMAUZIuxE9uQ7hl3NpXxcKV4,3425
glean_parser/templates/qmldir.jinja2,sha256=m6IGsp-tgTiOfQ7VN8XW6GqX0gJqJkt3B6Pkaul6FVo,156
glean_parser/templates/rust.jinja2,sha256=tznLKaZxi_Z9puGqDKD0uuWefZcVHiNdQHB4BP9zJfs,10797
glean_parser/templates/rust.jinja2,sha256=tMuBBsC_96UC4-AMBFHpu9u5aojoArkP_hwnREfWNQU,3600
glean_parser/templates/swift.jinja2,sha256=OsaEIlEdcOrUMvI_UzbxWv75lluTAWZGncH_pU-pbZQ,4809
glean_parser-7.2.1.dist-info/AUTHORS.md,sha256=yxgj8MioO4wUnrh0gmfb8l3DJJrf-l4HmmEDbQsbbNI,455
glean_parser-7.2.1.dist-info/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
glean_parser-7.2.1.dist-info/METADATA,sha256=6ZY8M4qK01Cz54nm4d9tOii3CBbW9lSaUSpHwUnm9JA,28275
glean_parser-7.2.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
glean_parser-7.2.1.dist-info/entry_points.txt,sha256=s-clJTIqp-PpJD-n3AnIQZFkTafIrzsTbAPX9vNY018,69
glean_parser-7.2.1.dist-info/top_level.txt,sha256=q7T3duD-9tYZFyDry6Wv2LcdMsK2jGnzdDFhxWcT2Z8,13
glean_parser-7.2.1.dist-info/RECORD,,
glean_parser-8.1.0.dist-info/AUTHORS.md,sha256=yxgj8MioO4wUnrh0gmfb8l3DJJrf-l4HmmEDbQsbbNI,455
glean_parser-8.1.0.dist-info/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
glean_parser-8.1.0.dist-info/METADATA,sha256=u3C1jRfAODOsD1lxYfKoIGlTTTX6ud14p3Ohhrb0Zo4,28967
glean_parser-8.1.0.dist-info/WHEEL,sha256=AtBG6SXL3KF_v0NxLf0ehyVOh0cold-JbJYXNGorC6Q,92
glean_parser-8.1.0.dist-info/entry_points.txt,sha256=mf9d3sv8BwSjjR58x9KDnpVkONCnv3fPQC2NjJl15Xg,68
glean_parser-8.1.0.dist-info/top_level.txt,sha256=q7T3duD-9tYZFyDry6Wv2LcdMsK2jGnzdDFhxWcT2Z8,13
glean_parser-8.1.0.dist-info/RECORD,,

View file

@ -1,5 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.40.0)
Generator: bdist_wheel (0.41.0)
Root-Is-Purelib: true
Tag: py3-none-any

View file

@ -1,3 +1,2 @@
[console_scripts]
glean_parser = glean_parser.__main__:main_wrapper

View file

@ -0,0 +1,162 @@
# -*- coding: 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/.
"""
Outputter to generate server Javascript code for collecting events.
This outputter is different from the rest of the outputters in that the code it
generates does not use the Glean SDK. It is meant to be used to collect events
using "events as pings" pattern in server-side environments. In these environments
SDK assumptions to measurement window and connectivity don't hold.
Generated code takes care of assembling pings with metrics, serializing to messages
conforming to Glean schema, and logging with mozlog. Then it's the role of the ingestion
pipeline to pick the messages up and process.
Warning: this outputter supports limited set of metrics,
see `SUPPORTED_METRIC_TYPES` below.
"""
from collections import defaultdict
from pathlib import Path
from typing import Any, Dict, Optional, List
from . import __version__
from . import metrics
from . import util
# Adding a metric here will require updating the `generate_js_metric_type` function
# and might require changes to the template.
SUPPORTED_METRIC_TYPES = ["string"]
def event_class_name(pingName: str) -> str:
return util.Camelize(pingName) + "ServerEvent"
def generate_metric_name(metric: metrics.Metric) -> str:
return f"{metric.category}.{metric.name}"
def generate_metric_argument_name(metric: metrics.Metric) -> str:
return f"{metric.category}_{metric.name}"
def generate_js_metric_type(metric: metrics.Metric) -> str:
return metric.type
def generate_metric_argument_description(metric: metrics.Metric) -> str:
return metric.description.replace("\n", " ").rstrip()
def generate_ping_factory_method(ping: str) -> str:
return f"create{util.Camelize(ping)}Event"
def output(
lang: str,
objs: metrics.ObjectTree,
output_dir: Path,
) -> None:
"""
Given a tree of objects, output Javascript or Typescript code to `output_dir`.
The output is a single file containing all the code for assembling pings with
metrics, serializing, and submitting.
:param lang: Either "javascript" or "typescript";
:param objects: A tree of objects (metrics and pings) as returned from
`parser.parse_objects`.
:param output_dir: Path to an output directory to write to.
"""
template = util.get_jinja2_template(
"javascript_server.jinja2",
filters=(
("event_class_name", event_class_name),
("metric_name", generate_metric_name),
("metric_argument_name", generate_metric_argument_name),
("js_metric_type", generate_js_metric_type),
("metric_argument_description", generate_metric_argument_description),
("factory_method", generate_ping_factory_method),
),
)
# In this environment we don't use a concept of measurement window for collecting
# metrics. Only "events as pings" are supported.
# For each ping we generate code which contains all the logic for assembling it
# with metrics, serializing, and submitting. Therefore we don't generate classes for
# each metric as in standard outputters.
PING_METRIC_ERROR_MSG = (
" Server-side environment is simplified and this"
+ " parser doesn't generate individual metric files. Make sure to pass all"
+ " your ping and metric definitions in a single invocation of the parser."
)
if "pings" not in objs:
print("❌ No ping definition found." + PING_METRIC_ERROR_MSG)
return
# Go through all metrics in objs and build a map of
# ping->list of metric categories->list of metrics
# for easier processing in the template.
ping_to_metrics: Dict[str, Dict[str, List[metrics.Metric]]] = defaultdict(dict)
for _category_key, category_val in objs.items():
for _metric_name, metric in category_val.items():
if isinstance(metric, metrics.Metric):
if metric.type not in SUPPORTED_METRIC_TYPES:
print(
"❌ Ignoring unsupported metric type: "
+ f"{metric.type}:{metric.name}."
+ " Reach out to Glean team to add support for this"
+ " metric type."
)
continue
for ping in metric.send_in_pings:
metrics_by_type = ping_to_metrics[ping]
metrics_list = metrics_by_type.setdefault(metric.type, [])
metrics_list.append(metric)
if not ping_to_metrics:
print("❌ No pings with metrics found." + PING_METRIC_ERROR_MSG)
return
extension = ".js" if lang == "javascript" else ".ts"
filepath = output_dir / ("server_events" + extension)
with filepath.open("w", encoding="utf-8") as fd:
fd.write(
template.render(
parser_version=__version__,
pings=ping_to_metrics,
lang=lang,
)
)
def output_javascript(
objs: metrics.ObjectTree, output_dir: Path, options: Optional[Dict[str, Any]] = None
) -> None:
"""
Given a tree of objects, output Javascript code to `output_dir`.
:param objects: A tree of objects (metrics and pings) as returned from
`parser.parse_objects`.
:param output_dir: Path to an output directory to write to.
"""
output("javascript", objs, output_dir)
def output_typescript(
objs: metrics.ObjectTree, output_dir: Path, options: Optional[Dict[str, Any]] = None
) -> None:
"""
Given a tree of objects, output Typescript code to `output_dir`.
:param objects: A tree of objects (metrics and pings) as returned from
`parser.parse_objects`.
:param output_dir: Path to an output directory to write to.
"""
output("typescript", objs, output_dir)

View file

@ -27,8 +27,12 @@ from . import tags
from . import util
# Yield only an error message
LintGenerator = Generator[str, None, None]
# Yield fully constructed GlinterNits
NitGenerator = Generator["GlinterNit", None, None]
class CheckType(enum.Enum):
warning = 0
@ -304,6 +308,40 @@ def check_redundant_ping(
yield ("The word 'custom' is redundant.")
def check_unknown_ping(
check_name: str,
check_type: CheckType,
all_pings: Dict[str, pings.Ping],
metrics: Dict[str, metrics.Metric],
parser_config: Dict[str, Any],
) -> NitGenerator:
"""
Check that all pings in `send_in_pings` for all metrics are either a builtin ping
or in the list of defined custom pings.
"""
available_pings = [p for p in all_pings]
for _, metric in metrics.items():
if check_name in metric.no_lint:
continue
send_in_pings = metric.send_in_pings
for target_ping in send_in_pings:
if target_ping in pings.RESERVED_PING_NAMES:
continue
if target_ping not in available_pings:
msg = f"Ping `{target_ping} `in `send_in_pings` is unknown."
name = ".".join([metric.category, metric.name])
nit = GlinterNit(
check_name,
name,
msg,
check_type,
)
yield nit
# The checks that operate on an entire category of metrics:
# {NAME: (function, is_error)}
CATEGORY_CHECKS: Dict[
@ -341,6 +379,20 @@ PING_CHECKS: Dict[
"REDUNDANT_PING": (check_redundant_ping, CheckType.error),
}
ALL_OBJECT_CHECKS: Dict[
str,
Tuple[
Callable[
# check name, check type, pings, metrics, config
[str, CheckType, dict, dict, dict],
NitGenerator,
],
CheckType,
],
] = {
"UNKNOWN_PING_REFERENCED": (check_unknown_ping, CheckType.error),
}
class GlinterNit:
def __init__(self, check_name: str, name: str, msg: str, check_type: CheckType):
@ -410,6 +462,29 @@ def _lint_pings(
return nits
def _lint_all_objects(
objects: Dict[str, Dict[str, Union[metrics.Metric, pings.Ping, tags.Tag]]],
parser_config: Dict[str, Any],
) -> List[GlinterNit]:
nits: List[GlinterNit] = []
pings = objects.get("pings")
if not pings:
return []
metrics = objects.get("all_metrics")
if not metrics:
return []
for check_name, (check_func, check_type) in ALL_OBJECT_CHECKS.items():
new_nits = list(
check_func(check_name, check_type, pings, metrics, parser_config)
)
nits.extend(new_nits)
return nits
def lint_metrics(
objs: metrics.ObjectTree,
parser_config: Optional[Dict[str, Any]] = None,
@ -427,6 +502,9 @@ def lint_metrics(
nits: List[GlinterNit] = []
valid_tag_names = [tag for tag in objs.get("tags", [])]
nits.extend(_lint_all_objects(objs, parser_config))
for category_name, category in sorted(list(objs.items())):
if category_name == "pings":
nits.extend(_lint_pings(category, parser_config, valid_tag_names))
@ -495,15 +573,6 @@ def lint_metrics(
return nits
def lint_yaml_files(
input_filepaths: Iterable[Path],
file=sys.stderr,
parser_config: Optional[Dict[str, Any]] = None,
) -> List:
"""Always empty."""
return []
def glinter(
input_filepaths: Iterable[Path],
parser_config: Optional[Dict[str, Any]] = None,

View file

@ -29,7 +29,8 @@ class Lifetime(enum.Enum):
class DataSensitivity(enum.Enum):
technical = 1
interaction = 2
web_activity = 3
stored_content = 3
web_activity = 3 # Old, deprecated name
highly_sensitive = 4

View file

@ -42,7 +42,7 @@ definitions:
short_id:
allOf:
- $ref: "#/definitions/snake_case"
- maxLength: 30
- maxLength: 70
labeled_metric_id:
type: string
@ -483,27 +483,60 @@ definitions:
tabs, addons, or windows a user has open; uses of specific Firefox
features; session length, scrolls and clicks; and the status of
discrete user preferences.
It also includes information about the user's in-product journeys
and product choices helpful to understand engagement (attitudes).
For example, selections of add-ons or tiles to determine
potential interest categories etc.
- **Category 3: Web activity data:** (`web_activity`) Information
about user web browsing that could be considered sensitive. Examples
include users specific web browsing history; general information
about their web browsing history (such as TLDs or categories of
webpages visited over time); and potentially certain types of
interaction data about specific webpages visited.
- **Category 3: Stored Content & Communications:**
(`stored_content`, formerly Web activity data, `web_activity`)
Information about what people store, sync, communicate or connect to
where the information is generally considered to be more sensitive
and personal in nature.
Examples include users' saved URLs or URL history,
specific web browsing history, general information
about their web browsing history
(such as TLDs or categories of webpages visited over time)
and potentially certain types of interaction data
about specific web pages or stories visited
(such as highlighted portions of a story).
It also includes information such as content saved by users to
an individual account like saved URLs, tags, notes, passwords
and files as well as communications that users have with one another
through a Mozilla service.
- **Category 4: Highly sensitive data
or clearly identifiable personal data:** (`highly_sensitive`)
- **Category 4: Highly sensitive data:** (`highly_sensitive`)
Information that directly identifies a person, or if combined with
other data could identify a person. Examples include e-mail,
usernames, identifiers such as google ad id, apple id, fxaccount,
city or country (unless small ones are explicitly filtered out), or
certain cookies. It may be embedded within specific website content,
such as memory contents, dumps, captures of screen data, or DOM
data.
other data could identify a person.
This data may be embedded within specific website content,
such as memory contents, dumps, captures of screen data,
or DOM data.
Examples include account registration data like name, password,
and email address associated with an account,
payment data in connection with subscriptions or donations,
contact information such as phone numbers or mailing addresses,
email addresses associated with surveys, promotions
and customer support contacts.
It also includes any data from different categories that,
when combined, can identify a person, device, household or account.
For example Category 1 log data combined with Category 3 saved URLs.
Additional examples are: voice audio commands
(including a voice audio file), speech-to-text or text-to-speech
(including transcripts), biometric data, demographic information,
and precise location data associated with a persistent identifier,
individual or small population cohorts.
This is location inferred or determined from mechanisms
other than IP such as wi-fi access points, Bluetooth beacons,
cell phone towers or provided directly to us,
such as in a survey or a profile.
type: array
items:
enum:
- technical
- interaction
- stored_content
- web_activity
- highly_sensitive
type: string
@ -687,11 +720,13 @@ additionalProperties:
data_sensitivity:
description: >
Text metrics require Category 3 (`web_activity`)
Text metrics require Category 3
(`stored_content` / `web_activity`)
or Category 4 (`highly_sensitive`).
type: array
items:
enum:
- stored_content
- web_activity
- highly_sensitive

View file

@ -0,0 +1,154 @@
{# The final Javascript/Typescript code is autogenerated, but this
Jinja2 template is not. Please file bugs! #}
/* 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/. */
// AUTOGENERATED BY glean_parser v{{ parser_version }}. DO NOT EDIT. DO NOT COMMIT.
// This requires `uuid` and `mozlog` libraries to be in the environment
{% if lang == "typescript" %}
// @types/uuid and mozlog types definitions are required in devDependencies
// for the latter see https://github.com/mozilla/fxa/blob/85bda71cda376c417b8c850ba82aa14252208c3c/types/mozlog/index.d.ts
{% endif %}
import { v4 as uuidv4 } from 'uuid';
import mozlog{% if lang == "typescript" %}, { Logger }{% endif %} from 'mozlog';
const GLEAN_EVENT_MOZLOG_TYPE = 'glean-server-event';
let _logger{% if lang == "typescript" %}: Logger{% endif %};
{% for ping, metrics_by_type in pings.items() %}
class {{ ping|event_class_name }} {
{% if lang == "typescript" %}
_applicationId: string;
_appDisplayVersion: string;
_channel: string;
{% endif %}
/**
* Create {{ ping|event_class_name }} instance.
*
* @param {string} applicationId - The application ID.
* @param {string} appDisplayVersion - The application display version.
* @param {string} channel - The channel.
*/
{% if lang == "typescript" %}
constructor(
applicationId: string,
appDisplayVersion: string,
channel: string,
logger_options: any
) {
{% else %}
constructor(applicationId, appDisplayVersion, channel, logger_options) {
{% endif %}
this._applicationId = applicationId;
this._appDisplayVersion = appDisplayVersion;
this._channel = channel;
if (!_logger) {
// append '-glean' to `logger_options.app` to avoid collision with other loggers and double logging
logger_options.app = logger_options.app + '-glean';
// set the format to `heka` so messages are properly ingested and decoded
logger_options.fmt = 'heka';
{% if lang == "typescript" %}
// mozlog types declaration requires a typePrefix to be passed when creating a logger
// we don't want a typePrefix, so we pass `undefined`
_logger = mozlog(logger_options)(undefined);
{% else %}
_logger = mozlog(logger_options)();
{% endif %}
}
}
/**
* Record and submit a server event object.
* Event is logged using internal mozlog logger.
*
{% for metric_type, metrics in metrics_by_type.items() %}
{% for metric in metrics %}
* @param { {{-metric|js_metric_type-}} } {{ metric|metric_argument_name }} - {{ metric|metric_argument_description }}.
{% endfor %}
{% endfor %}
*/
record({
{% for metric_type, metrics in metrics_by_type.items() %}
{% for metric in metrics %}
{{ metric|metric_argument_name }},
{% endfor %}
{% endfor %}
{% if lang == "typescript" %}
}: {
{% for metric_type, metrics in metrics_by_type.items() %}
{% for metric in metrics %}
{{ metric|metric_argument_name }}: {{ metric|js_metric_type }};
{% endfor %}
{% endfor %}
{% endif %}
}) {
let timestamp = new Date().toISOString();
let eventPayload = {
metrics: {
{% for metric_type, metrics in metrics_by_type.items() %}
{{ metric_type }}: {
{% for metric in metrics %}
'{{ metric|metric_name }}': {{ metric|metric_argument_name }},
{% endfor %}
},
{% endfor %}
},
ping_info: {
seq: 0, // this is required, however doesn't seem to be useful in server context
start_time: timestamp,
end_time: timestamp,
},
// `Unknown` fields below are required in the Glean schema, however they are not useful in server context
client_info: {
telemetry_sdk_build: 'glean_parser v{{ parser_version }}',
first_run_date: 'Unknown',
os: 'Unknown',
os_version: 'Unknown',
architecture: 'Unknown',
app_build: 'Unknown',
app_display_version: this._appDisplayVersion,
app_channel: this._channel,
},
};
let eventPayloadSerialized = JSON.stringify(eventPayload);
// This is the message structure that Decoder expects: https://github.com/mozilla/gcp-ingestion/pull/2400
let ping = {
document_namespace: this._applicationId,
document_type: '{{ ping }}',
document_version: '1',
document_id: uuidv4(),
payload: eventPayloadSerialized,
};
// this is similar to how FxA currently logs with mozlog: https://github.com/mozilla/fxa/blob/4c5c702a7fcbf6f8c6b1f175e9172cdd21471eac/packages/fxa-auth-server/lib/log.js#L289
_logger.info(GLEAN_EVENT_MOZLOG_TYPE, ping);
}
}
{% endfor %}
{% for ping in pings %}
export const {{ ping|factory_method }} = function ({
applicationId,
appDisplayVersion,
channel,
logger_options
{% if lang == "typescript" %}
}: {
applicationId: string;
appDisplayVersion: string;
channel: string;
logger_options: any;
{% endif %}
}) {
return new {{ ping|event_class_name }}(
applicationId,
appDisplayVersion,
channel,
logger_options
);
};
{% endfor %}

View file

@ -86,191 +86,4 @@ pub mod {{ category.name|snake_case }} {
{% endfor %}
{% if metric_by_type|length > 0 %}
#[allow(dead_code)]
pub(crate) mod __glean_metric_maps {
use std::collections::HashMap;
use super::{id_for_extra_key, extra_keys_len};
use crate::private::*;
{% for typ, metrics in metric_by_type.items() %}
pub static {{typ.0}}: ::glean::private::__export::Lazy<HashMap<MetricId, &Lazy<{{typ.1}}>>> = ::glean::private::__export::Lazy::new(|| {
let mut map = HashMap::with_capacity({{metrics|length}});
{% for metric in metrics %}
map.insert({{metric.0}}.into(), &super::{{metric.1}});
{% endfor %}
map
});
{% endfor %}
/// Wrapper to record an event based on its metric ID.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `extra` - An map of (extra key id, string) pairs.
/// The map will be decoded into the appropriate `ExtraKeys` type.
/// # Returns
///
/// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
/// or an `EventRecordingError::InvalidId` if no event by that ID exists
/// or an `EventRecordingError::InvalidExtraKey` if the `extra` map could not be deserialized.
pub(crate) fn record_event_by_id(metric_id: u32, extra: HashMap<i32, String>) -> Result<(), EventRecordingError> {
match metric_id {
{% for metric_id, event in events_by_id.items() %}
{{metric_id}} => {
assert!(
extra_keys_len(&super::{{event}}) != 0 || extra.is_empty(),
"No extra keys allowed, but some were passed"
);
super::{{event}}.record_raw(extra);
Ok(())
}
{% endfor %}
_ => Err(EventRecordingError::InvalidId),
}
}
/// Wrapper to record an event based on its metric ID, with a provided timestamp.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `timestamp` - The time at which this event was recorded.
/// * `extra` - An map of (extra key id, string) pairs.
/// The map will be decoded into the appropriate `ExtraKeys` type.
/// # Returns
///
/// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
/// or an `EventRecordingError::InvalidId` if no event by that ID exists
/// or an `EventRecordingError::InvalidExtraKey` if the event doesn't take extra pairs,
/// but some are passed in.
pub(crate) fn record_event_by_id_with_time(metric_id: MetricId, timestamp: u64, extra: HashMap<i32, String>) -> Result<(), EventRecordingError> {
match metric_id {
{% for metric_id, event in events_by_id.items() %}
MetricId({{metric_id}}) => {
if extra_keys_len(&super::{{event}}) == 0 && !extra.is_empty() {
return Err(EventRecordingError::InvalidExtraKey);
}
super::{{event}}.record_with_time(timestamp, extra);
Ok(())
}
{% endfor %}
_ => Err(EventRecordingError::InvalidId),
}
}
/// Wrapper to record an event based on its metric ID.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `extra` - An map of (string, string) pairs.
/// The map will be decoded into the appropriate `ExtraKeys` types.
/// # Returns
///
/// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
/// or an `EventRecordingError::InvalidId` if no event by that ID exists
/// or an `EventRecordingError::InvalidExtraKey` if the `extra` map could not be deserialized.
pub(crate) fn record_event_by_id_with_strings(metric_id: u32, extra: HashMap<String, String>) -> Result<(), EventRecordingError> {
match metric_id {
{% for metric_id, event in events_by_id.items() %}
{{metric_id}} => {
assert!(
extra_keys_len(&super::{{event}}) != 0 || extra.is_empty(),
"No extra keys allowed, but some were passed"
);
let extra = extra
.into_iter()
.map(|(k, v)| id_for_extra_key(&*k, &super::{{event}}).map(|k| (k, v)))
.collect::<Result<HashMap<_, _>, _>>()?;
super::{{event}}.record_raw(extra);
Ok(())
}
{% endfor %}
_ => Err(EventRecordingError::InvalidId),
}
}
/// Wrapper to get the currently stored events for event metric.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `ping_name` - (Optional) The ping name to look into.
/// Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
/// Returns the recorded events or `None` if nothing stored.
///
/// # Panics
///
/// Panics if no event by the given metric ID could be found.
pub(crate) fn event_test_get_value_wrapper(metric_id: u32, ping_name: Option<String>) -> Option<Vec<RecordedEvent>> {
match metric_id {
{% for metric_id, event in events_by_id.items() %}
{{metric_id}} => super::{{event}}.test_get_value(ping_name.as_deref()),
{% endfor %}
_ => panic!("No event for metric id {}", metric_id),
}
}
/// Check the provided event for errors.
///
/// # Arguments
///
/// * `metric_id` - The metric's ID to look up
/// * `ping_name` - (Optional) The ping name to look into.
/// Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
/// Returns a string for the recorded error or `None`.
///
/// # Panics
///
/// Panics if no event by the given metric ID could be found.
#[allow(unused_variables)]
pub(crate) fn event_test_get_error(metric_id: u32, ping_name: Option<String>) -> Option<String> {
#[cfg(feature = "with_gecko")]
match metric_id {
{% for metric_id, event in events_by_id.items() %}
{{metric_id}} => test_get_errors_string!(super::{{event}}, ping_name),
{% endfor %}
_ => panic!("No event for metric id {}", metric_id),
}
#[cfg(not(feature = "with_gecko"))]
{
return None;
}
}
pub(crate) mod submetric_maps {
use std::sync::{
atomic::AtomicU32,
RwLock,
};
use super::*;
pub(crate) const MIN_LABELED_SUBMETRIC_ID: u32 = {{min_submetric_id}};
pub(crate) static NEXT_LABELED_SUBMETRIC_ID: AtomicU32 = AtomicU32::new(MIN_LABELED_SUBMETRIC_ID);
pub(crate) static LABELED_METRICS_TO_IDS: ::glean::private::__export::Lazy<RwLock<HashMap<(u32, String), u32>>> = ::glean::private::__export::Lazy::new(||
RwLock::new(HashMap::new())
);
{% for typ, metrics in metric_by_type.items() %}
{% if typ.0 in ('BOOLEAN_MAP', 'COUNTER_MAP', 'STRING_MAP') %}
pub static {{typ.0}}: ::glean::private::__export::Lazy<RwLock<HashMap<MetricId, Labeled{{typ.1}}>>> = ::glean::private::__export::Lazy::new(||
RwLock::new(HashMap::new())
);
{% endif %}
{% endfor%}
}
}
{% endif %}

View file

@ -18,6 +18,7 @@ from typing import Any, Callable, Dict, Iterable, List, Optional
from . import lint
from . import parser
from . import javascript
from . import javascript_server
from . import kotlin
from . import markdown
from . import metrics
@ -54,6 +55,8 @@ class Outputter:
OUTPUTTERS = {
"javascript": Outputter(javascript.output_javascript, []),
"typescript": Outputter(javascript.output_typescript, []),
"javascript_server": Outputter(javascript_server.output_javascript, []),
"typescript_server": Outputter(javascript_server.output_typescript, []),
"kotlin": Outputter(kotlin.output_kotlin, ["*.kt"]),
"markdown": Outputter(markdown.output_markdown, []),
"swift": Outputter(swift.output_swift, ["*.swift"]),

8
third_party/python/poetry.lock generated vendored
View file

@ -391,14 +391,14 @@ files = [
[[package]]
name = "glean-parser"
version = "7.2.1"
version = "8.1.0"
description = "Parser tools for Mozilla's Glean telemetry"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "glean_parser-7.2.1-py3-none-any.whl", hash = "sha256:651cfee34422ea1db90bbf1cb03732bd8c598773bf95daa289a62addeaf10295"},
{file = "glean_parser-7.2.1.tar.gz", hash = "sha256:11496ac004fe421b914c7fbdc9a1d620e4821d56e1d9f65523d3858cdb907bbd"},
{file = "glean_parser-8.1.0-py3-none-any.whl", hash = "sha256:bcb4b04f3d5d8fbc1cba452e0ea19d4f89a0f3363b78dabd410c26618632ffa1"},
{file = "glean_parser-8.1.0.tar.gz", hash = "sha256:b9ef84ec1afdb3a1080219fe3768411a824921b7fcd637314fc78d47b5537752"},
]
[package.dependencies]
@ -1415,4 +1415,4 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (
[metadata]
lock-version = "2.0"
python-versions = "^3.7"
content-hash = "c409903aba82ee97e2d690aeaf190c01fe6c26adbc472572dc2fd9fa2cb7cc8f"
content-hash = "195fb2c45ba5de3c5c98f5d2fa44b5ce64fd3dd45c324d528477637245413d0f"

View file

@ -14,7 +14,7 @@ ecdsa==0.15
esprima==4.0.1
fluent.migrate==0.12.0
fluent.syntax==0.19.0
glean_parser==7.2.1
glean_parser==8.1.0
importlib-metadata==6.0.0
jsmin==3.0.0
json-e==2.7.0

View file

@ -104,9 +104,9 @@ fluent-syntax==0.19.0 ; python_version >= "3.7" and python_version < "4.0" \
giturlparse==0.10.0 ; python_version >= "3.7" and python_version < "4.0" \
--hash=sha256:04ba1a3a099c3093fa8d24a422913c6a9b2c2cd22bcffc939cf72e3e98f672d7 \
--hash=sha256:2595ab291d30717cda8474b874c9fd509f1b9802ad7f6968c36a45e4b13eb337
glean-parser==7.2.1 ; python_version >= "3.7" and python_version < "4.0" \
--hash=sha256:11496ac004fe421b914c7fbdc9a1d620e4821d56e1d9f65523d3858cdb907bbd \
--hash=sha256:651cfee34422ea1db90bbf1cb03732bd8c598773bf95daa289a62addeaf10295
glean-parser==8.1.0 ; python_version >= "3.7" and python_version < "4.0" \
--hash=sha256:b9ef84ec1afdb3a1080219fe3768411a824921b7fcd637314fc78d47b5537752 \
--hash=sha256:bcb4b04f3d5d8fbc1cba452e0ea19d4f89a0f3363b78dabd410c26618632ffa1
idna==2.10 ; python_version >= "3.7" and python_version < "4.0" \
--hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \
--hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0

File diff suppressed because one or more lines are too long

View file

@ -13,7 +13,7 @@
edition = "2021"
rust-version = "1.62"
name = "glean-core"
version = "53.1.0"
version = "53.2.0"
authors = [
"Jan-Erik Rediger <jrediger@mozilla.com>",
"The Glean Team <glean-team@mozilla.com>",
@ -33,10 +33,9 @@ readme = "README.md"
keywords = ["telemetry"]
license = "MPL-2.0"
repository = "https://github.com/mozilla/glean"
resolver = "1"
[package.metadata.glean]
glean-parser = "7.1.0"
glean-parser = "8.1.0"
[dependencies.bincode]
version = "1.2.1"

View file

@ -1080,7 +1080,7 @@ mod test {
let metric_id_pattern = "telemetry_test.single_metric";
// Write sample metrics to the database.
let lifetimes = vec![Lifetime::User, Lifetime::Ping, Lifetime::Application];
let lifetimes = [Lifetime::User, Lifetime::Ping, Lifetime::Application];
for lifetime in lifetimes.iter() {
for value in &["retain", "delete"] {

View file

@ -894,7 +894,7 @@ mod test {
},
execution_counter: None,
};
let timestamps = vec![20, 40, 200];
let timestamps = [20, 40, 200];
let not_glean_restarted = StoredEvent {
event: RecordedEvent {
timestamp: timestamps[0],
@ -970,8 +970,8 @@ mod test {
// This scenario represents a run of three events followed by an hour between runs,
// followed by one final event.
let timestamps = vec![20, 40, 200, 12];
let ecs = vec![0, 1];
let timestamps = [20, 40, 200, 12];
let ecs = [0, 1];
let some_hour = 16;
let startup_date = FixedOffset::east(0)
.ymd(2022, 11, 24)
@ -1095,8 +1095,8 @@ mod test {
// This scenario represents a run of two events followed by negative one hours between runs,
// followed by two more events.
let timestamps = vec![20, 40, 12, 200];
let ecs = vec![0, 1];
let timestamps = [20, 40, 12, 200];
let ecs = [0, 1];
let some_hour = 10;
let startup_date = FixedOffset::east(0)
.ymd(2022, 11, 25)

View file

@ -136,7 +136,7 @@ fn snapshot_correctly_clears_the_stores() {
let snapshot2 = glean
.event_storage()
.snapshot_as_json(&glean, "store2", false);
for s in vec![snapshot, snapshot2] {
for s in [snapshot, snapshot2] {
assert!(s.is_some());
let s = s.unwrap();
assert_eq!(1, s.as_array().unwrap().len());

View file

@ -1 +1 @@
{"files":{"Cargo.toml":"889c822c32b84930908f4280a9e73808cd0f57fa9f6cea2f9b61da017235e664","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"5627cc81e6187ab6c2b4dff061af16d559edcab64ba786bac39daa69c703c595","src/common_test.rs":"de47b53dcca37985c0a2b8c02daecbf32309aa54f5a4dd9290719c2c1fd0fa55","src/configuration.rs":"c82d1d4c32baa878e0fae566c10cbe348531f2983657aee39276fd78ea0c9b1d","src/core_metrics.rs":"dd17b482613894af08b51a2cff6dc1e84a6dbd853c14a55566e6698348941ced","src/lib.rs":"727a078a27c4985adb4022f2fc64fda647c996078f995c1fec85c208cd3337bc","src/net/http_uploader.rs":"43812a70d19a38e8d7a093c8076c2b6345372c3c861b0f3511428762700a65e0","src/net/mod.rs":"e36e170a8e53530f8705988eea694ed7c55f50bb0ce403c0facbfb75ce03ac7f","src/private/event.rs":"02bbebf545695812e5055741cc0b5f3c99eda2039e684e26fcdd5f087ed15fe3","src/private/mod.rs":"eb8fe4e588bb32a54617324db39319920c627e6fc23c23cf4da5c17c63e0afed","src/private/ping.rs":"cbdc57f41fc9d46e56b4dfff91ac683753d1f8b3ecd0aa9bc3419e3595b8b81b","src/system.rs":"6eae5b41c15eba9cad6dbd116abe3519ee3e1fe034e79bdd692b029829a8c384","src/test.rs":"356890df8f88096b4a62e396d9810f9bc20790e7b7fb446c73801109ea1c26b1","tests/common/mod.rs":"37cd4c48e140c793b852ae09fb3e812da28a4412977295015bcbffd632fcf294","tests/init_fails.rs":"28fd7726e76ca1295eb0905eca0b2ec65b0accfa28432c9ff90ec8f92616fc79","tests/never_init.rs":"1f33b8ce7ca3514b57b48cc16d98408974c85cf8aa7d13257ffc2ad878ebb295","tests/no_time_to_init.rs":"e7df75b47897fbf2c860a2e1c1c225b57598b8d1a39125ca897fe8d825bf0338","tests/overflowing_preinit.rs":"7ad4b2274dd9240b53430859a4eb1d2597cf508a5a678333f3d3abbadd2ed4a7","tests/persist_ping_lifetime.rs":"81415dc1d74743f02269f0d0dfa524003147056853f080276972e64a0b761d3c","tests/persist_ping_lifetime_nopanic.rs":"18379d3ffbf4a2c8c684c04ff7a0660b86dfbbb447db2d24dfed6073cb7ddf8f","tests/schema.rs":"1b7b19aec54a24c2bdd4738cf33c16802c19c83504c4d0e6bcfc19142877acdb","tests/simple.rs":"b099034b0599bdf4650e0fa09991a8413fc5fbf397755fc06c8963d4c7c8dfa6","tests/test-shutdown-blocking.sh":"9b16a01c190c7062474dd92182298a3d9a27928c8fa990340fdd798e6cdb7ab2","tests/upload_timing.rs":"d044fce7c783133e385671ea37d674e5a1b4120cae7b07708dcd825addfa0ee3"},"package":"0efbf048a79e634cd5ccd224f972018e3f217c72d4071bbe6ebee382037bffcb"}
{"files":{"Cargo.toml":"6c5dc80704945076018237c096a850a6bd0d48d392e1f47e2d3437bdc673ea28","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"5627cc81e6187ab6c2b4dff061af16d559edcab64ba786bac39daa69c703c595","src/common_test.rs":"de47b53dcca37985c0a2b8c02daecbf32309aa54f5a4dd9290719c2c1fd0fa55","src/configuration.rs":"5be1eaf4a2e1d1b35d9c13387f8f59b5f5d96a874b984396b7fae907e2938a8c","src/core_metrics.rs":"fef8fb4e5fa57c179836c6eb2cf59278fe3b8b036dbe57b0ff02971b4acd822f","src/lib.rs":"7bffe31c2b59d9e26b0869264af9a098be6eccb086f146e46777a288ad5e505f","src/net/http_uploader.rs":"43812a70d19a38e8d7a093c8076c2b6345372c3c861b0f3511428762700a65e0","src/net/mod.rs":"612a9f13ade0b202c8762bccc7b5dc288101cb3820e47be2755331911a221c55","src/private/event.rs":"02bbebf545695812e5055741cc0b5f3c99eda2039e684e26fcdd5f087ed15fe3","src/private/mod.rs":"eb8fe4e588bb32a54617324db39319920c627e6fc23c23cf4da5c17c63e0afed","src/private/ping.rs":"cbdc57f41fc9d46e56b4dfff91ac683753d1f8b3ecd0aa9bc3419e3595b8b81b","src/system.rs":"6eae5b41c15eba9cad6dbd116abe3519ee3e1fe034e79bdd692b029829a8c384","src/test.rs":"c877fa637e9049834818c3354f8b9b8b7a8136fb71ab3b0f9f0ea88115cbbd4b","tests/common/mod.rs":"08fb9483d9b6ed9fe873b4395245166ae8a15263be750c7a8e298c41d9604745","tests/init_fails.rs":"28fd7726e76ca1295eb0905eca0b2ec65b0accfa28432c9ff90ec8f92616fc79","tests/never_init.rs":"1f33b8ce7ca3514b57b48cc16d98408974c85cf8aa7d13257ffc2ad878ebb295","tests/no_time_to_init.rs":"e7df75b47897fbf2c860a2e1c1c225b57598b8d1a39125ca897fe8d825bf0338","tests/overflowing_preinit.rs":"7ad4b2274dd9240b53430859a4eb1d2597cf508a5a678333f3d3abbadd2ed4a7","tests/persist_ping_lifetime.rs":"81415dc1d74743f02269f0d0dfa524003147056853f080276972e64a0b761d3c","tests/persist_ping_lifetime_nopanic.rs":"18379d3ffbf4a2c8c684c04ff7a0660b86dfbbb447db2d24dfed6073cb7ddf8f","tests/schema.rs":"b1d78ac189c5f7309badbc37451fb428abce357e630a55e4ef5b0ef14d118f67","tests/simple.rs":"b099034b0599bdf4650e0fa09991a8413fc5fbf397755fc06c8963d4c7c8dfa6","tests/test-shutdown-blocking.sh":"9b16a01c190c7062474dd92182298a3d9a27928c8fa990340fdd798e6cdb7ab2","tests/test-thread-crashing.sh":"ff1bc8e5d7e4ba3a10d0d38bef222db8bfba469e7d30e45b1053d177a4084f09","tests/upload_timing.rs":"d044fce7c783133e385671ea37d674e5a1b4120cae7b07708dcd825addfa0ee3"},"package":"df470f5f41c8fc6113bd48fa18e0f1a8193e1e2b1f3942f8255b064e60db42ff"}

View file

@ -13,7 +13,7 @@
edition = "2021"
rust-version = "1.62"
name = "glean"
version = "53.1.0"
version = "53.2.0"
authors = [
"Jan-Erik Rediger <jrediger@mozilla.com>",
"The Glean Team <glean-team@mozilla.com>",
@ -33,7 +33,6 @@ keywords = [
]
license = "MPL-2.0"
repository = "https://github.com/mozilla/glean"
resolver = "1"
[dependencies.chrono]
version = "0.4.10"
@ -43,7 +42,7 @@ features = ["serde"]
version = "0.5"
[dependencies.glean-core]
version = "53.1.0"
version = "53.2.0"
[dependencies.inherent]
version = "1"
@ -88,6 +87,9 @@ version = "1.0.19"
[dev-dependencies.jsonschema-valid]
version = "0.5.0"
[dev-dependencies.libc]
version = "0.2"
[dev-dependencies.tempfile]
version = "3.1.0"

View file

@ -39,7 +39,7 @@ pub struct Configuration {
/// The internal logging level.
pub log_level: Option<LevelFilter>,
/// The rate pings may be uploaded before they are throttled.
pub rate_limit: Option<glean_core::PingRateLimit>,
pub rate_limit: Option<crate::PingRateLimit>,
}
/// Configuration builder.
@ -79,7 +79,7 @@ pub struct Builder {
pub log_level: Option<LevelFilter>,
/// Optional: The internal ping upload rate limit.
/// Default: `None`
pub rate_limit: Option<glean_core::PingRateLimit>,
pub rate_limit: Option<crate::PingRateLimit>,
}
impl Builder {

View file

@ -13,6 +13,8 @@ pub struct ClientInfoMetrics {
pub app_display_version: String,
/// The product-provided release channel (e.g. "beta").
pub channel: Option<String>,
/// The locale of the application during initialization (e.g. "es-ES").
pub locale: Option<String>,
}
impl ClientInfoMetrics {
@ -22,6 +24,7 @@ impl ClientInfoMetrics {
app_build: "Unknown".to_string(),
app_display_version: "Unknown".to_string(),
channel: None,
locale: None,
}
}
}
@ -32,6 +35,7 @@ impl From<ClientInfoMetrics> for glean_core::ClientInfoMetrics {
app_build: metrics.app_build,
app_display_version: metrics.app_display_version,
channel: metrics.channel,
locale: metrics.locale,
os_version: system::get_os_version(),
windows_build_number: system::get_windows_build_number(),
architecture: system::ARCH.to_string(),

View file

@ -36,8 +36,8 @@ pub use configuration::{Builder as ConfigurationBuilder, Configuration};
pub use core_metrics::ClientInfoMetrics;
pub use glean_core::{
metrics::{Datetime, DistributionData, MemoryUnit, Rate, RecordedEvent, TimeUnit, TimerId},
traits, CommonMetricData, Error, ErrorType, Glean, HistogramType, Lifetime, RecordedExperiment,
Result,
traits, CommonMetricData, Error, ErrorType, Glean, HistogramType, Lifetime, PingRateLimit,
RecordedExperiment, Result,
};
mod configuration;

View file

@ -138,9 +138,26 @@ impl UploadManager {
Ordering::SeqCst,
Ordering::SeqCst,
);
})
.expect("Failed to spawn Glean's uploader thread");
*handle = Some(thread);
});
match thread {
Ok(thread) => *handle = Some(thread),
Err(err) => {
log::warn!("Failed to spawn Glean's uploader thread. This will be retried on next upload. Error: {err}");
// Swapping back the thread state. We're the ones setting it to `Running`, so
// should be able to set it back.
let state_change = self.inner.thread_running.compare_exchange(
State::Running,
State::Stopped,
Ordering::SeqCst,
Ordering::SeqCst,
);
if state_change.is_err() {
log::warn!("Failed to swap back thread state. Someone else jumped in and changed the state.");
}
}
};
}
pub(crate) fn shutdown(&self) {

View file

@ -1319,7 +1319,7 @@ fn configure_ping_throttling() {
})
.build();
let pings_per_interval = 10;
cfg.rate_limit = Some(glean_core::PingRateLimit {
cfg.rate_limit = Some(crate::PingRateLimit {
seconds_per_interval: 1,
pings_per_interval,
});

View file

@ -45,6 +45,7 @@ pub fn initialize(cfg: Configuration) {
app_build: "1.0.0".to_string(),
app_display_version: "1.0.0".to_string(),
channel: Some("testing".to_string()),
locale: Some("xx-XX".to_string()),
};
glean::initialize(cfg, client_info);

View file

@ -84,6 +84,7 @@ fn validate_against_schema() {
app_build: env!("CARGO_PKG_VERSION").to_string(),
app_display_version: env!("CARGO_PKG_VERSION").to_string(),
channel: Some("testing".to_string()),
locale: Some("xx-XX".to_string()),
};
glean::initialize(cfg, client_info);

View file

@ -0,0 +1,32 @@
#!/bin/bash
# Test harness for testing the RLB processes from the outside.
#
# Some behavior can only be observed when properly exiting the process running Glean,
# e.g. when an uploader runs in another thread.
# On exit the threads will be killed, regardless of their state.
# Remove the temporary data path on all exit conditions
cleanup() {
if [ -n "$datapath" ]; then
rm -r "$datapath"
fi
}
trap cleanup INT ABRT TERM EXIT
tmp="${TMPDIR:-/tmp}"
datapath=$(mktemp -d "${tmp}/glean_long_running.XXXX")
RUSTFLAGS="-C panic=abort" \
RUST_LOG=debug \
cargo run --example crashing-threads -- "$datapath"
ret=$?
count=$(ls -1q "$datapath/pending_pings" | wc -l)
if [[ $ret -eq 0 ]] && [[ "$count" -eq 1 ]]; then
echo "test result: ok."
exit 0
else
echo "test result: FAILED."
exit 101
fi

View file

@ -6,7 +6,7 @@ edition = "2018"
license = "MPL-2.0"
[dependencies]
glean = "53.1.0"
glean = "53.2.0"
log = "0.4"
nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" }

View file

@ -9,7 +9,7 @@ license = "MPL-2.0"
[dependencies]
bincode = "1.0"
chrono = "0.4.10"
glean = "53.1.0"
glean = "53.2.0"
inherent = "1.0.0"
log = "0.4"
nsstring = { path = "../../../../xpcom/rust/nsstring", optional = true }

View file

@ -47,6 +47,7 @@ fn setup_glean(tempdir: Option<tempfile::TempDir>) -> tempfile::TempDir {
app_build: "test-build".into(),
app_display_version: "1.2.3".into(),
channel: None,
locale: None,
};
glean::test_reset_glean(cfg, client_info, true);

View file

@ -10,7 +10,9 @@ use std::path::PathBuf;
use firefox_on_glean::{metrics, pings};
use nserror::{nsresult, NS_ERROR_FAILURE};
use nsstring::{nsACString, nsCString, nsString};
use xpcom::interfaces::{nsIFile, nsIPrefService, nsIProperties, nsIXULAppInfo, nsIXULRuntime};
use xpcom::interfaces::{
mozILocaleService, nsIFile, nsIPrefService, nsIProperties, nsIXULAppInfo, nsIXULRuntime,
};
use xpcom::{RefPtr, XpCom};
use glean::{ClientInfoMetrics, Configuration};
@ -127,12 +129,13 @@ fn build_configuration(
};
let data_path = PathBuf::from(&data_path_str);
let (app_build, app_display_version, channel) = get_app_info()?;
let (app_build, app_display_version, channel, locale) = get_app_info()?;
let client_info = ClientInfoMetrics {
app_build,
app_display_version,
channel: Some(channel),
locale: Some(locale),
};
log::debug!("Client Info: {:#?}", client_info);
@ -220,16 +223,18 @@ fn get_data_path() -> Result<String, nsresult> {
Ok(data_path)
}
/// Return a tuple of the build_id, app version, and build channel.
/// Return a tuple of the build_id, app version, build channel, and locale.
/// If the XUL Runtime isn't a XULAppInfo (e.g. in xpcshell),
/// build_id ad app_version will be "unknown".
/// Other problems result in an error being returned instead.
fn get_app_info() -> Result<(String, String, String), nsresult> {
fn get_app_info() -> Result<(String, String, String, String), nsresult> {
let xul: RefPtr<nsIXULRuntime> =
xpcom::components::XULRuntime::service().map_err(|_| NS_ERROR_FAILURE)?;
let pref_service: RefPtr<nsIPrefService> =
xpcom::components::Preferences::service().map_err(|_| NS_ERROR_FAILURE)?;
let locale_service: RefPtr<mozILocaleService> =
xpcom::components::Locale::service().map_err(|_| NS_ERROR_FAILURE)?;
let branch = xpcom::getter_addrefs(|p| {
// Safe because:
// * `null` is explicitly allowed per documentation
@ -261,6 +266,7 @@ fn get_app_info() -> Result<(String, String, String), nsresult> {
"unknown".to_owned(),
"unknown".to_owned(),
channel.to_string(),
"unknown".to_owned(),
))
}
};
@ -275,10 +281,18 @@ fn get_app_info() -> Result<(String, String, String), nsresult> {
app_info.GetVersion(&mut *version).to_result()?;
}
let mut locale = nsCString::new();
unsafe {
locale_service
.GetAppLocaleAsBCP47(&mut *locale)
.to_result()?;
}
Ok((
build_id.to_string(),
version.to_string(),
channel.to_string(),
locale.to_string(),
))
}