mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			263 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			263 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# 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/.
 | 
						|
 | 
						|
from mach.decorators import Command, CommandArgument
 | 
						|
 | 
						|
TODO_FILL_DESCRIPTION = "TODO: Fill in this description. You _can_ use **markdown**."
 | 
						|
GENERATED_BY_DESCRIPTION = (
 | 
						|
    "This {metric} was generated to correspond to the Legacy Telemetry {kind} {name}."
 | 
						|
)
 | 
						|
DESCRIPTION_INDENT = "      "
 | 
						|
LIST_INDENT = "      - "
 | 
						|
BUG_URL_TEMPLATE = "https://bugzil.la/{}"
 | 
						|
 | 
						|
GLEAN_METRIC_TEMPLATE = """
 | 
						|
  {name}:
 | 
						|
    type: {metric_type}
 | 
						|
    description: >
 | 
						|
{multiline_description}
 | 
						|
    bugs:{bugs_alias}{bugs_list}
 | 
						|
    data_reviews:{data_alias}{bugs_list}
 | 
						|
    notification_emails:{emails_alias}{emails_list}
 | 
						|
    expires: {expiry}
 | 
						|
    {extra}telemetry_mirror: {legacy_enum}
 | 
						|
""".strip(
 | 
						|
    "\n"
 | 
						|
)
 | 
						|
 | 
						|
EXTRA_KEY_DESCRIPTION_INDENT = DESCRIPTION_INDENT + "    "
 | 
						|
VALUE_EXTRA_DESCRIPTION = "The `value` of the event. Mirrors to the Legacy Telemetry event's `value` parameter."
 | 
						|
EXTRA_KEY_TEMPLATE = """
 | 
						|
      {name}:
 | 
						|
        description: >
 | 
						|
{description}
 | 
						|
        type: string
 | 
						|
""".strip(
 | 
						|
    "\n"
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
@Command(
 | 
						|
    "gifft",
 | 
						|
    category="misc",
 | 
						|
    description="Generate a Glean metric definition for a given Legacy Telemetry probe. Only supports Events and Scalars.",
 | 
						|
)
 | 
						|
@CommandArgument(
 | 
						|
    "telemetry_probe_name", help="Telemetry probe name (e.g. readermode.view)"
 | 
						|
)
 | 
						|
def mach_gifft(command_context, telemetry_probe_name):
 | 
						|
    from os import path
 | 
						|
 | 
						|
    telemetrydir = path.join(
 | 
						|
        command_context.topsrcdir, "toolkit", "components", "telemetry"
 | 
						|
    )
 | 
						|
 | 
						|
    import re
 | 
						|
 | 
						|
    def to_snake_case(camel):
 | 
						|
        return re.sub("([A-Z]+)", r"_\1", camel).lower().replace("__", "_").strip("_")
 | 
						|
 | 
						|
    import itertools
 | 
						|
    import sys
 | 
						|
 | 
						|
    sys.path.append(path.join(telemetrydir, "build_scripts"))
 | 
						|
    import textwrap
 | 
						|
 | 
						|
    from mozparsers import parse_events, parse_scalars
 | 
						|
 | 
						|
    events = parse_events.load_events(path.join(telemetrydir, "Events.yaml"), True)
 | 
						|
    for e in events:
 | 
						|
        if e.category + "." + e.name == telemetry_probe_name:
 | 
						|
            # There are four levels of identification in Legacy Telemetry event
 | 
						|
            # definitions: category, name/family, method, and object.
 | 
						|
            # If not present, the `method` property will supply the name/family.
 | 
						|
            # GIFFT will mirror to a specific identified C++ enum, which means
 | 
						|
            # we need to generate Glean events for every combination of method
 | 
						|
            # and object.
 | 
						|
            category = e.category
 | 
						|
            emails_alias = bugs_alias = data_alias = extra_alias = ""
 | 
						|
            print(f"{to_snake_case(category)}:")
 | 
						|
            for m, o in itertools.product(e.methods, e.objects):
 | 
						|
                legacy_name = category + "." + m + "#" + o
 | 
						|
                name = m + "_" + o
 | 
						|
                description = e._definition.get("description", TODO_FILL_DESCRIPTION)
 | 
						|
                multiline_description = textwrap.fill(
 | 
						|
                    description,
 | 
						|
                    width=80 - len(DESCRIPTION_INDENT),
 | 
						|
                    initial_indent=DESCRIPTION_INDENT,
 | 
						|
                    subsequent_indent=DESCRIPTION_INDENT,
 | 
						|
                )
 | 
						|
                multiline_description += "\n"
 | 
						|
                multiline_description += textwrap.fill(
 | 
						|
                    GENERATED_BY_DESCRIPTION.format(
 | 
						|
                        metric="event", kind="event", name=legacy_name
 | 
						|
                    ),
 | 
						|
                    width=80 - len(DESCRIPTION_INDENT),
 | 
						|
                    initial_indent=DESCRIPTION_INDENT,
 | 
						|
                    subsequent_indent=DESCRIPTION_INDENT,
 | 
						|
                )
 | 
						|
 | 
						|
                alias_prefix = category.replace(".", "_") + f"_{m}_"
 | 
						|
                if bugs_alias:
 | 
						|
                    bugs_list = ""
 | 
						|
                else:
 | 
						|
                    bugs_alias = f"{alias_prefix}bugs"
 | 
						|
                    data_alias = f"{alias_prefix}data_reviews"
 | 
						|
                    bugs_list = "\n" + textwrap.indent(
 | 
						|
                        "\n".join(
 | 
						|
                            map(
 | 
						|
                                lambda b: BUG_URL_TEMPLATE.format(b),
 | 
						|
                                e._definition.get("bug_numbers", []),
 | 
						|
                            )
 | 
						|
                        ),
 | 
						|
                        LIST_INDENT,
 | 
						|
                    )
 | 
						|
                if emails_alias:
 | 
						|
                    emails_list = ""
 | 
						|
                else:
 | 
						|
                    emails_alias = f"{alias_prefix}emails"
 | 
						|
                    emails_list = "\n" + textwrap.indent(
 | 
						|
                        "\n".join(e._definition.get("notification_emails", [])),
 | 
						|
                        LIST_INDENT,
 | 
						|
                    )
 | 
						|
 | 
						|
                # expiry_version is a string like `"123.0a1"` or `"never"`,
 | 
						|
                # but Glean wants a number like `123` or `never`.
 | 
						|
                expiry = e.expiry_version.strip('"').split(".")[0]
 | 
						|
 | 
						|
                if extra_alias:
 | 
						|
                    extra_keys = ""
 | 
						|
                else:
 | 
						|
                    extra_alias = f"{alias_prefix}extra"
 | 
						|
                    multiline_extra_description = textwrap.fill(
 | 
						|
                        VALUE_EXTRA_DESCRIPTION,
 | 
						|
                        width=80 - len(EXTRA_KEY_DESCRIPTION_INDENT),
 | 
						|
                        initial_indent=EXTRA_KEY_DESCRIPTION_INDENT,
 | 
						|
                        subsequent_indent=EXTRA_KEY_DESCRIPTION_INDENT,
 | 
						|
                    )
 | 
						|
                    extra_keys = "\n" + EXTRA_KEY_TEMPLATE.format(
 | 
						|
                        name="value", description=multiline_extra_description
 | 
						|
                    )
 | 
						|
                    for key_name, key_description in e._definition.get(
 | 
						|
                        "extra_keys", {}
 | 
						|
                    ).items():
 | 
						|
                        extra_keys += "\n"
 | 
						|
                        extra_keys += EXTRA_KEY_TEMPLATE.format(
 | 
						|
                            name=key_name,
 | 
						|
                            description=textwrap.indent(
 | 
						|
                                key_description, EXTRA_KEY_DESCRIPTION_INDENT
 | 
						|
                            ),
 | 
						|
                        )
 | 
						|
 | 
						|
                legacy_enum = (
 | 
						|
                    parse_events.convert_to_cpp_identifier(category, ".") + "_"
 | 
						|
                )
 | 
						|
                legacy_enum += parse_events.convert_to_cpp_identifier(m, "_") + "_"
 | 
						|
                legacy_enum += parse_events.convert_to_cpp_identifier(o, "_")
 | 
						|
 | 
						|
                def generate_alias(list, alias):
 | 
						|
                    if len(e.methods) == 1 and len(e.objects) == 1:
 | 
						|
                        return ""
 | 
						|
                    if list:
 | 
						|
                        return f" &{alias}"
 | 
						|
                    else:
 | 
						|
                        return f" *{alias}"
 | 
						|
 | 
						|
                print(
 | 
						|
                    GLEAN_METRIC_TEMPLATE.format(
 | 
						|
                        name=to_snake_case(name),
 | 
						|
                        metric_type="event",
 | 
						|
                        multiline_description=multiline_description,
 | 
						|
                        bugs_alias=generate_alias(bugs_list, bugs_alias),
 | 
						|
                        bugs_list=bugs_list,
 | 
						|
                        data_alias=generate_alias(bugs_list, data_alias),
 | 
						|
                        emails_alias=generate_alias(emails_list, emails_alias),
 | 
						|
                        emails_list=emails_list,
 | 
						|
                        expiry=expiry,
 | 
						|
                        extra=f"extra_keys:{generate_alias(extra_keys, extra_alias)}{extra_keys}\n    ",
 | 
						|
                        legacy_enum=legacy_enum,
 | 
						|
                    )
 | 
						|
                )
 | 
						|
                print()  # We want a newline between event definitions.
 | 
						|
            break
 | 
						|
 | 
						|
    scalars = parse_scalars.load_scalars(path.join(telemetrydir, "Scalars.yaml"))
 | 
						|
    for s in scalars:
 | 
						|
        if s.category + "." + s.name == telemetry_probe_name:
 | 
						|
            category = s.category
 | 
						|
            name = s.name
 | 
						|
            legacy_name = category + "." + name
 | 
						|
            description = s._definition.get("description", TODO_FILL_DESCRIPTION)
 | 
						|
            multiline_description = textwrap.fill(
 | 
						|
                description,
 | 
						|
                width=80 - len(DESCRIPTION_INDENT),
 | 
						|
                initial_indent=DESCRIPTION_INDENT,
 | 
						|
                subsequent_indent=DESCRIPTION_INDENT,
 | 
						|
            )
 | 
						|
            multiline_description += "\n"
 | 
						|
            multiline_description += textwrap.fill(
 | 
						|
                GENERATED_BY_DESCRIPTION.format(
 | 
						|
                    name=legacy_name, metric="metric", kind="scalar"
 | 
						|
                ),
 | 
						|
                width=80 - len(DESCRIPTION_INDENT),
 | 
						|
                initial_indent=DESCRIPTION_INDENT,
 | 
						|
                subsequent_indent=DESCRIPTION_INDENT,
 | 
						|
            )
 | 
						|
 | 
						|
            bugs_list = "\n" + textwrap.indent(
 | 
						|
                "\n".join(
 | 
						|
                    map(
 | 
						|
                        lambda b: BUG_URL_TEMPLATE.format(b),
 | 
						|
                        s._definition.get("bug_numbers", []),
 | 
						|
                    )
 | 
						|
                ),
 | 
						|
                LIST_INDENT,
 | 
						|
            )
 | 
						|
            emails_list = "\n" + textwrap.indent(
 | 
						|
                "\n".join(s._definition.get("notification_emails", [])),
 | 
						|
                LIST_INDENT,
 | 
						|
            )
 | 
						|
 | 
						|
            # expires is a string like `"123.0a1"` or `"never"`,
 | 
						|
            # but Glean wants a number like `123` or `never`.
 | 
						|
            expiry = s.expires.strip('"').split(".")[0]
 | 
						|
 | 
						|
            metric_type = s.kind
 | 
						|
            if metric_type == "uint":
 | 
						|
                if s.keyed:
 | 
						|
                    metric_type = "labeled_counter"
 | 
						|
                else:
 | 
						|
                    # The caller will replace "counter" with "quantity" in the output when needed.
 | 
						|
                    metric_type = "counter"
 | 
						|
            elif s.keyed:
 | 
						|
                if metric_type == "boolean":
 | 
						|
                    metric_type = "labeled_boolean"
 | 
						|
                elif metric_type == "string":
 | 
						|
                    metric_type = "labeled_string"
 | 
						|
 | 
						|
            extra = ""
 | 
						|
            if s.keys:
 | 
						|
                extra = (
 | 
						|
                    "labels:\n"
 | 
						|
                    + "\n".join(map(lambda key: "      - " + key, s.keys))
 | 
						|
                    + "\n    "
 | 
						|
                )
 | 
						|
 | 
						|
            print(f"{to_snake_case(category)}:")
 | 
						|
            print(
 | 
						|
                GLEAN_METRIC_TEMPLATE.format(
 | 
						|
                    name=to_snake_case(name),
 | 
						|
                    metric_type=metric_type,
 | 
						|
                    multiline_description=multiline_description,
 | 
						|
                    bugs_alias="",
 | 
						|
                    bugs_list=bugs_list,
 | 
						|
                    data_alias="",
 | 
						|
                    emails_alias="",
 | 
						|
                    emails_list=emails_list,
 | 
						|
                    extra=extra,
 | 
						|
                    expiry=expiry,
 | 
						|
                    legacy_enum=s.enum_label,
 | 
						|
                )
 | 
						|
            )
 | 
						|
            print()  # We want a newline between metric definitions.
 |