# 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/.
"""
Classes for each of the high-level metric types. """
import enum from typing import Any, Dict, List, Optional, Type, Union # noqa
from . import pings from . import tags from . import util
# Important: if the values are ever changing here, make sure # to also fix mozilla/glean. Otherwise language bindings may # break there. class Lifetime(enum.Enum):
ping = 0
application = 1
user = 2
self.type = type
self.category = category
self.name = name
self.bugs = bugs
self.description = description
self.notification_emails = notification_emails
self.expires = expires if metadata isNone:
metadata = {}
self.metadata = metadata if data_reviews isNone:
data_reviews = []
self.data_reviews = data_reviews
self.version = version
self.disabled = disabled
self.lifetime = getattr(Lifetime, lifetime) if send_in_pings isNone:
send_in_pings = ["default"]
self.send_in_pings = send_in_pings
self.unit = unit
self.gecko_datapoint = gecko_datapoint if no_lint isNone:
no_lint = []
self.no_lint = no_lint if data_sensitivity isnotNone:
self.data_sensitivity = [
getattr(DataSensitivity, x) for x in data_sensitivity
]
self.defined_in = defined_in if telemetry_mirror isnotNone:
self.telemetry_mirror = telemetry_mirror
# _validated indicates whether this metric has already been jsonschema # validated (but not any of the Python-level validation). ifnot _validated:
data = { "$schema": parser.METRICS_ID,
self.category: {self.name: self._serialize_input()},
} # type: Dict[str, util.JSONType] for error in parser.validate(data): raise ValueError(error)
# Store the config, but only after validation. if _config isNone:
_config = {}
self._config = _config
# Metrics in the special category "glean.internal.metrics" need to have # an empty category string when identifying the metrics in the ping. if self.category == Metric.glean_internal_metric_cat:
self.category = ""
def __init_subclass__(cls, **kwargs): # Create a mapping of all of the subclasses of this class if cls notin Metric.metric_types and hasattr(cls, "typename"):
Metric.metric_types[cls.typename] = cls
super().__init_subclass__(**kwargs)
@classmethod def make_metric(
cls,
category: str,
name: str,
metric_info: Dict[str, util.JSONType],
config: Optional[Dict[str, Any]] = None,
validated: bool = False,
): """
Given a metric_info dictionary from metrics.yaml, return a metric
instance.
:param: category The category the metric lives in
:param: name The name of the metric
:param: metric_info A dictionary of the remaining metric parameters
:param: config A dictionary containing commandline configuration
parameters
:param: validated Trueif the metric has already gone through
jsonschema validation
:return: A new Metric instance. """ if config isNone:
config = {}
def serialize(self) -> Dict[str, util.JSONType]: """
Serialize the metric back to JSON object model. """
d = self.__dict__.copy() # Convert enum fields back to strings for key, val in d.items(): if isinstance(val, enum.Enum):
d[key] = d[key].name if isinstance(val, set):
d[key] = sorted(list(val)) if isinstance(val, list) and len(val) and isinstance(val[0], enum.Enum):
d[key] = [x.name for x in val] del d["name"] del d["category"] ifnot d["unit"]:
d.pop("unit")
d.pop("_config", None)
d.pop("_generate_enums", None)
d.pop("_generate_structure", None) return d
def identifier(self) -> str: """
Create an identifier unique for this metric.
Generally, category.name; however, Glean internal
metrics only use name. """ ifnot self.category: return self.name return".".join((self.category, self.name))
def is_disabled(self) -> bool: return self.disabled or self.is_expired()
@property def allowed_extra_keys(self): # Sort keys so that output is deterministic return sorted(list(self.extra_keys.keys()))
@property def allowed_extra_keys_with_types(self): # Sort keys so that output is deterministic return sorted(
[(k, v.get("type", "string")) for (k, v) in self.extra_keys.items()],
key=lambda x: x[0],
)
@staticmethod def validate_extra_keys(extra_keys: Dict[str, str], config: Dict[str, Any]) -> None: ifnot config.get("allow_reserved") and any(
k.startswith("glean.") for k in extra_keys.keys()
): raise ValueError( "Extra keys beginning with 'glean.' are reserved for " "Glean internal use."
)
class Uuid(Metric):
typename = "uuid"
class Url(Metric):
typename = "url"
class Jwe(Metric):
typename = "jwe"
def __init__(self, *args, **kwargs): raise ValueError( "JWE support was removed. " "If you require this send an email to glean-team@mozilla.com."
)
class CowString(str): """
Wrapper classfor strings that should be represented as a `Cow<'static, str>` in Rust, or `String` in other target languages.
This wraps `str`, so unless `CowString` is specifically
handled it acts (and serializes) as a string. """
def __init__(self, val: str):
self.inner: str = val
def __init__(self, *args, **kwargs):
labels = kwargs.pop("labels", None) if labels isnotNone:
self.ordered_labels = labels
self.labels = set([CowString(label) for label in labels]) else:
self.ordered_labels = None
self.labels = None
super().__init__(*args, **kwargs)
def serialize(self) -> Dict[str, util.JSONType]: """
Serialize the metric back to JSON object model. """
d = super().serialize()
d["labels"] = self.ordered_labels del d["ordered_labels"] return d
class LabeledBoolean(Labeled, Boolean):
typename = "labeled_boolean"
class LabeledString(Labeled, String):
typename = "labeled_string"
class LabeledCounter(Labeled, Counter):
typename = "labeled_counter"
class LabeledCustomDistribution(Labeled, CustomDistribution):
typename = "labeled_custom_distribution"
class LabeledMemoryDistribution(Labeled, MemoryDistribution):
typename = "labeled_memory_distribution"
class LabeledTimingDistribution(Labeled, TimingDistribution):
typename = "labeled_timing_distribution"
class LabeledQuantity(Labeled, Quantity):
typename = "labeled_quantity"
if structure["type"] == "object": if"items"in structure: raise ValueError("`items` not allowed in object structure")
if"properties"notin structure: raise ValueError("`properties` missing for type `object`")
for key in structure["properties"]:
value = structure["properties"][key]
structure["properties"][key] = Object._validate_substructure(value)
if structure["type"] == "array": if"properties"in structure: raise ValueError("`properties` not allowed in array structure")
if"items"notin structure: raise ValueError("`items` missing for type `array`")
value = structure["items"]
structure["items"] = Object._validate_substructure(value)
return structure
@staticmethod def validate_structure(structure): ifNone: raise ValueError("`structure` needed for object metric.")
# Different from `ALLOWED_TYPES`: # We _require_ the root type to be an object or array.
allowed_types = ["object", "array"] if"type"notin structure: raise ValueError(
f"missing `type` in object structure. Allowed: {allowed_types}"
) if structure["type"] notin allowed_types: raise ValueError( "invalid `type` in object structure. found: {}, allowed: {}".format(
structure["type"], allowed_types
)
)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.