Excessibility.TelemetryCapture.Analyzer behaviour (Excessibility v0.17.0)

Copy Markdown View Source

Behaviour for timeline analyzers.

Analyzers detect patterns across complete timelines and return structured findings. Analyzers declare their enricher dependencies via requires_enrichers/0.

Example

defmodule MyApp.CustomAnalyzer do
  @behaviour Excessibility.TelemetryCapture.Analyzer

  def name, do: :custom
  def default_enabled?, do: false
  def requires_enrichers, do: [:memory, :duration]
  def depends_on, do: [:memory]  # runs after memory analyzer

  def analyze(timeline, opts) do
    # Access prior analyzer results via opts[:prior_results]
    prior = Keyword.get(opts, :prior_results, %{})
    memory_stats = get_in(prior, [:memory, :stats])

    %{
      findings: [...],
      stats: %{...}
    }
  end
end

Callbacks

  • name/0 - Returns atom identifier for this analyzer
  • default_enabled?/0 - Whether analyzer runs by default without explicit flag
  • requires_enrichers/0 - (Optional) List of enricher names this analyzer needs
  • depends_on/0 - (Optional) List of analyzer names that must run first
  • analyze/2 - Takes complete timeline and options, returns analysis results

Types

Analysis results contain:

  • :findings - List of issues found (warnings, errors, info)
  • :stats - Summary statistics for the analysis

Summary

Functions

Gets analyzer dependencies for an analyzer module. Returns empty list if not defined.

Gets required enrichers for an analyzer module. Returns empty list if not defined.

Splits a timeline's events into per-view sublists, preserving order.

Topologically sorts analyzers based on their dependencies. Returns analyzers in execution order (dependencies first).

Types

analysis_result()

@type analysis_result() :: %{findings: [finding()], stats: map()}

finding()

@type finding() :: %{
  severity: :info | :warning | :critical,
  message: String.t(),
  events: [integer()],
  metadata: map()
}

Callbacks

analyze(timeline, opts)

@callback analyze(timeline :: map(), opts :: keyword()) :: analysis_result()

default_enabled?()

@callback default_enabled?() :: boolean()

depends_on()

(optional)
@callback depends_on() :: [atom()]

name()

@callback name() :: atom()

requires_enrichers()

(optional)
@callback requires_enrichers() :: [atom()]

Functions

get_dependencies(analyzer_module)

Gets analyzer dependencies for an analyzer module. Returns empty list if not defined.

get_required_enrichers(analyzer_module)

Gets required enrichers for an analyzer module. Returns empty list if not defined.

group_by_view(events)

@spec group_by_view([map()]) :: [[map()]]

Splits a timeline's events into per-view sublists, preserving order.

A journey test drives several LiveViews, so a raw timeline interleaves unrelated processes. Analyzers that compare consecutive events (memory growth, render efficiency) must not treat a UserLoginLive mount sitting next to a MarketplaceLive.Index render as a transition — that's an artifact of the interleaving, not the code (issue #142).

Events are grouped by :view_module; groups appear in first-seen order and events keep their relative order within a group. Events without a :view_module (older fixtures, single-view timelines) collapse to one group, so the ungrouped case is unchanged.

sort_by_dependencies(analyzers)

Topologically sorts analyzers based on their dependencies. Returns analyzers in execution order (dependencies first).