Example #1
0
def chained_exception(interface: ChainedException, event: Event,
                      context: GroupingContext,
                      **meta: Any) -> ReturnedVariants:
    # Case 1: we have a single exception, use the single exception
    # component directly to avoid a level of nesting
    exceptions = interface.exceptions()
    if len(exceptions) == 1:
        return context.get_grouping_component(exceptions[0],
                                              event=event,
                                              **meta)

    # Case 2: produce a component for each chained exception
    by_name: Dict[str, List[GroupingComponent]] = {}

    for exception in exceptions:
        for name, component in context.get_grouping_component(exception,
                                                              event=event,
                                                              **meta).items():
            by_name.setdefault(name, []).append(component)

    rv = {}

    for name, component_list in by_name.items():
        rv[name] = GroupingComponent(
            id="chained-exception",
            values=component_list,
            tree_label=calculate_tree_label(reversed(component_list)),
        )

    return rv
Example #2
0
def threads_legacy(interface: Threads, event: Event, context: GroupingContext,
                   **meta: Any) -> ReturnedVariants:
    thread_count = len(interface.values)
    if thread_count != 1:
        return {
            context["variant"]:
            GroupingComponent(
                id="threads",
                contributes=False,
                hint="ignored because contains %d threads" % thread_count,
            )
        }

    stacktrace = interface.values[0].get("stacktrace")
    if not stacktrace:
        return {
            context["variant"]:
            GroupingComponent(id="threads",
                              contributes=False,
                              hint="thread has no stacktrace")
        }

    return {
        context["variant"]:
        GroupingComponent(id="threads",
                          values=[
                              context.get_grouping_component(stacktrace,
                                                             event=event,
                                                             **meta)
                          ])
    }
Example #3
0
def _single_stacktrace_variant(stacktrace: Stacktrace, event: Event,
                               context: GroupingContext,
                               meta: Dict[str, Any]) -> ReturnedVariants:
    variant = context["variant"]

    frames = stacktrace.frames

    values: List[GroupingComponent] = []
    prev_frame = None
    frames_for_filtering = []
    for frame in frames:
        with context:
            context["is_recursion"] = is_recursion_v1(frame, prev_frame)
            frame_component = context.get_grouping_component(frame,
                                                             event=event,
                                                             **meta)
        if not context[
                "hierarchical_grouping"] and variant == "app" and not frame.in_app:
            frame_component.update(contributes=False, hint="non app frame")
        values.append(frame_component)
        frames_for_filtering.append(frame.get_raw_data())
        prev_frame = frame

    # Special case for JavaScript where we want to ignore single frame
    # stacktraces in certain cases where those would be of too low quality
    # for grouping.
    if (len(frames) == 1 and values[0].contributes
            and get_behavior_family_for_platform(
                frames[0].platform or event.platform) == "javascript"
            and not frames[0].function and frames[0].is_url()):
        values[0].update(contributes=False,
                         hint="ignored single non-URL JavaScript frame")

    main_variant, inverted_hierarchy = context.config.enhancements.assemble_stacktrace_component(
        values,
        frames_for_filtering,
        event.platform,
        exception_data=context["exception_data"],
        similarity_self_encoder=_stacktrace_encoder,
    )

    if inverted_hierarchy is None:
        inverted_hierarchy = stacktrace.snapshot

    inverted_hierarchy = bool(inverted_hierarchy)

    if not context["hierarchical_grouping"]:
        return {variant: main_variant}

    all_variants: ReturnedVariants = get_stacktrace_hierarchy(
        main_variant, values, frames_for_filtering, inverted_hierarchy)

    # done for backwards compat to find old groups
    all_variants["system"] = main_variant

    return all_variants
Example #4
0
def stacktrace_legacy(interface: Stacktrace, event: Event,
                      context: GroupingContext,
                      **meta: Any) -> ReturnedVariants:
    variant = context["variant"]
    frames = interface.frames
    contributes = None
    hint = None
    all_frames_considered_in_app = False

    # TODO(dcramer): this should apply only to platform=javascript
    # Browser JS will often throw errors (from inlined code in an HTML page)
    # which contain only a single frame, no function name, and have the HTML
    # document as the filename. In this case the hash is often not usable as
    # the context cannot be trusted and the URL is dynamic (this also means
    # the line number cannot be trusted).
    if len(frames) == 1 and not frames[0].function and frames[0].is_url():
        contributes = False
        hint = "ignored single frame stack"
    elif variant == "app":
        total_frames = len(frames)
        in_app_count = sum(1 if f.in_app else 0 for f in frames)
        if in_app_count == 0:
            in_app_count = total_frames
            all_frames_considered_in_app = True

        # if app frames make up less than 10% of the stacktrace discard
        # the hash as invalid
        if total_frames > 0 and in_app_count / float(total_frames) < 0.10:
            contributes = False
            hint = "less than 10% of frames are in-app"

    values = []
    prev_frame: Optional[Frame] = None
    frames_for_filtering = []
    for frame in frames:
        frame_component: GroupingComponent = context.get_grouping_component(
            frame, event=event, variant=variant, **meta)
        if variant == "app" and not frame.in_app and not all_frames_considered_in_app:
            frame_component.update(contributes=False, hint="non app frame")
        elif prev_frame is not None and is_recursion_legacy(frame, prev_frame):
            frame_component.update(contributes=False,
                                   hint="ignored due to recursion")
        elif variant == "app" and not frame.in_app and all_frames_considered_in_app:
            frame_component.update(
                hint="frame considered in-app because no frame is in-app")
        values.append(frame_component)
        frames_for_filtering.append(frame.get_raw_data())
        prev_frame = frame

    rv, _ = context.config.enhancements.assemble_stacktrace_component(
        values, frames_for_filtering, event.platform)
    rv.update(contributes=contributes, hint=hint)
    return {variant: rv}
Example #5
0
def chained_exception_legacy(interface: ChainedException, event: Event,
                             context: GroupingContext,
                             **meta: Any) -> ReturnedVariants:
    # Case 1: we have a single exception, use the single exception
    # component directly
    exceptions = interface.exceptions()
    if len(exceptions) == 1:
        single_variant: GroupingComponent = context.get_grouping_component(
            exceptions[0], event=event, **meta)
        return {context["variant"]: single_variant}

    # Case 2: try to build a new component out of the individual
    # errors however with a trick.  In case any exception has a
    # stacktrace we want to ignore all other exceptions.
    any_stacktraces = False
    values = []
    for exception in exceptions:
        exception_component: GroupingComponent = context.get_grouping_component(
            exception, event=event, **meta)
        stacktrace_component = exception_component.get_subcomponent(
            "stacktrace")
        if stacktrace_component is not None and stacktrace_component.contributes:
            any_stacktraces = True
        values.append(exception_component)

    if any_stacktraces:
        for value in values:
            stacktrace_component = value.get_subcomponent("stacktrace")
            if stacktrace_component is None or not stacktrace_component.contributes:
                value.update(contributes=False,
                             hint="exception has no stacktrace")

    return {
        context["variant"]:
        GroupingComponent(id="chained-exception", values=values)
    }
Example #6
0
def single_exception_legacy(interface: SingleException, event: Event,
                            context: GroupingContext,
                            **meta: Any) -> ReturnedVariants:

    type_component = GroupingComponent(
        id="type",
        values=[interface.type] if interface.type else [],
        similarity_encoder=ident_encoder,
        contributes=False,
    )
    value_component = GroupingComponent(
        id="value",
        values=[interface.value] if interface.value else [],
        similarity_encoder=text_shingle_encoder(5),
        contributes=False,
    )
    stacktrace_component = GroupingComponent(id="stacktrace")

    if interface.stacktrace is not None:
        stacktrace_component = context.get_grouping_component(
            interface.stacktrace, event=event, **meta)
        if stacktrace_component.contributes:
            if interface.type:
                type_component.update(contributes=True)
                if interface.value:
                    value_component.update(
                        hint="stacktrace and type take precedence")
            elif interface.value:
                value_component.update(hint="stacktrace takes precedence")

    if not stacktrace_component.contributes:
        if interface.type:
            type_component.update(contributes=True)
        if interface.value:
            value_component.update(contributes=True)

    return {
        context["variant"]:
        GroupingComponent(
            id="exception",
            values=[stacktrace_component, type_component, value_component])
    }
Example #7
0
def _filtered_threads(threads: List[Dict[str, Any]], event: Event,
                      context: GroupingContext,
                      meta: Dict[str, Any]) -> Optional[ReturnedVariants]:
    if len(threads) != 1:
        return None

    stacktrace = threads[0].get("stacktrace")
    if not stacktrace:
        return {
            "app":
            GroupingComponent(id="threads",
                              contributes=False,
                              hint="thread has no stacktrace")
        }

    rv = {}

    for name, stacktrace_component in context.get_grouping_component(
            stacktrace, event=event, **meta).items():
        rv[name] = GroupingComponent(id="threads",
                                     values=[stacktrace_component])

    return rv
Example #8
0
def get_grouping_variants_for_event(event, config=None):
    """Returns a dict of all grouping variants for this event."""
    # If a checksum is set the only variant that comes back from this
    # event is the checksum variant.
    checksum = event.data.get("checksum")
    if checksum:
        if HASH_RE.match(checksum):
            return {"checksum": ChecksumVariant(checksum)}

        rv = {
            "hashed-checksum": ChecksumVariant(hash_from_values(checksum), hashed=True),
        }

        # The legacy code path also supported arbitrary values here but
        # it will blow up if it results in more than 32 bytes of data
        # as this cannot be inserted into the database.  (See GroupHash.hash)
        if len(checksum) <= 32:
            rv["checksum"] = ChecksumVariant(checksum)

        return rv

    # Otherwise we go to the various forms of fingerprint handling.  If the event carries
    # a materialized fingerprint info from server side fingerprinting we forward it to the
    # variants which can export additional information about them.
    fingerprint = event.data.get("fingerprint") or ["{{ default }}"]
    fingerprint_info = event.data.get("_fingerprint_info")
    defaults_referenced = sum(1 if is_default_fingerprint_var(d) else 0 for d in fingerprint)

    if config is None:
        config = load_default_grouping_config()
    context = GroupingContext(config)

    # At this point we need to calculate the default event values.  If the
    # fingerprint is salted we will wrap it.
    components = _get_calculated_grouping_variants_for_event(event, context)

    # If no defaults are referenced we produce a single completely custom
    # fingerprint and mark all other variants as non-contributing
    if defaults_referenced == 0:
        rv = {}
        for (key, component) in components.items():
            component.update(
                contributes=False,
                contributes_to_similarity=True,
                hint="custom fingerprint takes precedence",
            )
            rv[key] = ComponentVariant(component, context.config)

        fingerprint = resolve_fingerprint_values(fingerprint, event.data)
        rv["custom-fingerprint"] = CustomFingerprintVariant(fingerprint, fingerprint_info)

    # If the fingerprints are unsalted, we can return them right away.
    elif defaults_referenced == 1 and len(fingerprint) == 1:
        rv = {}
        for (key, component) in components.items():
            rv[key] = ComponentVariant(component, context.config)

    # Otherwise we need to salt each of the components.
    else:
        rv = {}
        fingerprint = resolve_fingerprint_values(fingerprint, event.data)
        for (key, component) in components.items():
            rv[key] = SaltedComponentVariant(
                fingerprint, component, context.config, fingerprint_info
            )

    # Ensure we have a fallback hash if nothing else works out
    if not any(x.contributes for x in rv.values()):
        rv["fallback"] = FallbackVariant()

    return rv
Example #9
0
def single_exception(interface: SingleException, event: Event,
                     context: GroupingContext,
                     **meta: Any) -> ReturnedVariants:
    type_component = GroupingComponent(
        id="type",
        values=[interface.type] if interface.type else [],
        similarity_encoder=ident_encoder,
    )
    system_type_component = type_component.shallow_copy()

    ns_error_component = None

    if interface.mechanism:
        if interface.mechanism.synthetic:
            # Ignore synthetic exceptions as they are produced from platform
            # specific error codes.
            #
            # For example there can be crashes with EXC_ACCESS_VIOLATION_* on Windows with
            # the same exact stacktrace as a crash with EXC_BAD_ACCESS on macOS.
            #
            # Do not update type component of system variant, such that regex
            # can be continuously modified without unnecessarily creating new
            # groups.
            type_component.update(
                contributes=False,
                hint="ignored because exception is synthetic")
        if interface.mechanism.meta and "ns_error" in interface.mechanism.meta:
            ns_error_component = GroupingComponent(
                id="ns-error",
                values=[
                    interface.mechanism.meta["ns_error"].get("domain"),
                    interface.mechanism.meta["ns_error"].get("code"),
                ],
            )

    if interface.stacktrace is not None:
        with context:
            context["exception_data"] = interface.to_json()
            stacktrace_variants = context.get_grouping_component(
                interface.stacktrace, event=event, **meta)
    else:
        stacktrace_variants = {
            "app": GroupingComponent(id="stacktrace"),
        }

    rv = {}

    for variant, stacktrace_component in stacktrace_variants.items():
        values = [
            stacktrace_component,
            system_type_component if variant == "system" else type_component,
        ]

        if ns_error_component is not None:
            values.append(ns_error_component)

        if context["with_exception_value_fallback"]:
            value_component = GroupingComponent(
                id="value", similarity_encoder=text_shingle_encoder(5))

            value_in = interface.value
            if value_in is not None:
                value_trimmed = trim_message_for_grouping(value_in)
                hint = "stripped common values" if value_in != value_trimmed else None
                if value_trimmed:
                    value_component.update(values=[value_trimmed], hint=hint)

            if stacktrace_component.contributes and value_component.contributes:
                value_component.update(
                    contributes=False,
                    contributes_to_similarity=True,
                    hint="ignored because stacktrace takes precedence",
                )

            if (ns_error_component is not None
                    and ns_error_component.contributes
                    and value_component.contributes):
                value_component.update(
                    contributes=False,
                    contributes_to_similarity=True,
                    hint="ignored because ns-error info takes precedence",
                )

            values.append(value_component)

        rv[variant] = GroupingComponent(id="exception", values=values)

    return rv