PropertyDamage.Telemetry (PropertyDamage v0.2.0)

View Source

Telemetry events for PropertyDamage test runs.

PropertyDamage emits telemetry events during test execution that can be used for monitoring, dashboards, and observability.

Events

All events are prefixed with [:property_damage, ...].

Run Lifecycle

  • [:property_damage, :run, :start] - Test run started

    • Measurements: %{system_time: integer()}
    • Metadata: %{model: module(), adapter: module(), max_runs: integer(), max_commands: integer(), seed: integer()}
  • [:property_damage, :run, :stop] - Test run completed

    • Measurements: %{duration: integer(), total_commands: integer()}
    • Metadata: %{model: module(), adapter: module(), result: :ok | :error, runs_completed: integer()}

  • [:property_damage, :run, :exception] - Test run crashed

    • Measurements: %{duration: integer()}
    • Metadata: %{model: module(), adapter: module(), kind: atom(), reason: term(), stacktrace: list()}

Sequence Execution

  • [:property_damage, :sequence, :start] - Sequence execution started

    • Measurements: %{system_time: integer()}
    • Metadata: %{run_number: integer(), command_count: integer(), branching: boolean()}
  • [:property_damage, :sequence, :stop] - Sequence execution completed

    • Measurements: %{duration: integer()}
    • Metadata: %{run_number: integer(), success: boolean(), commands_executed: integer()}

Command Execution

  • [:property_damage, :command, :start] - Command execution started

    • Measurements: %{system_time: integer()}
    • Metadata: %{command: module(), index: integer(), run_number: integer()}
  • [:property_damage, :command, :stop] - Command execution completed

    • Measurements: %{duration: integer()}
    • Metadata: %{command: module(), index: integer(), success: boolean(), events_count: integer()}

Check Execution

  • [:property_damage, :check, :start] - Check evaluation started

    • Measurements: %{system_time: integer()}
    • Metadata: %{check_name: atom(), projection: module()}
  • [:property_damage, :check, :stop] - Check evaluation completed

    • Measurements: %{duration: integer()}
    • Metadata: %{check_name: atom(), passed: boolean(), message: String.t() | nil}

Shrinking

  • [:property_damage, :shrink, :start] - Shrinking started

    • Measurements: %{system_time: integer()}
    • Metadata: %{original_length: integer()}
  • [:property_damage, :shrink, :iteration] - Shrink iteration completed

    • Measurements: %{iteration: integer()}
    • Metadata: %{current_length: integer(), success: boolean()}
  • [:property_damage, :shrink, :stop] - Shrinking completed

    • Measurements: %{duration: integer(), iterations: integer()}
    • Metadata: %{original_length: integer(), shrunk_length: integer()}

Progress (DR-022)

In addition to the fine-grained spans above, every long-running operation emits a coarse progress/result heartbeat derived from the unified PropertyDamage.Progress projection. For run/1 these are distinct from and additional to the per-unit sequence/command/check/shrink spans: the spans instrument each unit of work, the progress events are a campaign-level heartbeat.

  • [:property_damage, :test_run, :progress] - An intermediate run update

    • Measurements: %{at: integer(), elapsed_ms: non_neg_integer()}
    • Metadata: %{data: PropertyDamage.Progress.RunUpdate.t(), run_id: term()}
  • [:property_damage, :test_run, :result] - The terminal run result

    • Measurements: %{at: integer(), elapsed_ms: non_neg_integer()}
    • Metadata: %{data: PropertyDamage.Progress.RunResult.t(), run_id: term()}
  • [:property_damage, :load_test, :progress] - A load-test metrics snapshot

    • Measurements: %{at: integer(), elapsed_ms: non_neg_integer()}
    • Metadata: %{data: PropertyDamage.Progress.LoadUpdate.t(), run_id: term()}
  • [:property_damage, :load_test, :result] - The terminal load-test report

    • Measurements: %{at: integer(), elapsed_ms: non_neg_integer()}
    • Metadata: %{data: PropertyDamage.Progress.LoadResult.t(), run_id: term()}
  • [:property_damage, :mutation, :progress] - A per-mutation update

    • Measurements: %{at: integer(), elapsed_ms: non_neg_integer()}
    • Metadata: %{data: PropertyDamage.Progress.MutationUpdate.t(), run_id: term()}
  • [:property_damage, :mutation, :result] - The terminal mutation report

    • Measurements: %{at: integer(), elapsed_ms: non_neg_integer()}
    • Metadata: %{data: PropertyDamage.Progress.MutationResult.t(), run_id: term()}
  • [:property_damage, :differential, :progress] - A differential run update

    • Measurements: %{at: integer(), elapsed_ms: non_neg_integer()}
    • Metadata: %{data: PropertyDamage.Progress.DifferentialUpdate.t(), run_id: term()}
  • [:property_damage, :differential, :result] - The terminal differential result

    • Measurements: %{at: integer(), elapsed_ms: non_neg_integer()}
    • Metadata: %{data: PropertyDamage.Progress.DifferentialResult.t(), run_id: term()}

These events fire only when a handler is attached for them, preserving the zero-cost-when-unobserved guarantee on the hot loop.

Usage

Attach handlers using :telemetry.attach/4:

:telemetry.attach(
  "my-handler",
  [:property_damage, :run, :stop],
  &MyModule.handle_event/4,
  nil
)

Or use PropertyDamage.Telemetry.Dashboard for a pre-built LiveView dashboard.

Summary

Functions

Emit a check start event.

Emit a check stop event.

Emit a command start event.

Emit a command stop event.

Build a telemetry consumer for the unified progress projection (DR-022), or nil when nothing is listening.

Emit a run start event.

Emit a run stop event.

Emit a sequence start event.

Emit a sequence stop event.

Emit a shrink iteration event.

Emit a shrink start event.

Emit a shrink stop event.

Execute a function with telemetry span instrumentation.

Functions

check_start(metadata)

@spec check_start(map()) :: :ok

Emit a check start event.

check_stop(start_time, metadata)

@spec check_stop(integer(), map()) :: :ok

Emit a check stop event.

command_start(metadata)

@spec command_start(map()) :: :ok

Emit a command start event.

command_stop(start_time, metadata)

@spec command_stop(integer(), map()) :: :ok

Emit a command stop event.

progress_consumer(operations)

@spec progress_consumer([atom()]) :: (PropertyDamage.Progress.t() -> :ok) | nil

Build a telemetry consumer for the unified progress projection (DR-022), or nil when nothing is listening.

Given the operations to cover (e.g. [:test_run]), it checks whether a handler is attached for any of their :progress/:result events. If so it returns a (PropertyDamage.Progress.t -> :ok) that emits the corresponding coarse event; otherwise it returns nil so the reporter stays inert and the hot loop builds no %Progress{} (the zero-cost guarantee).

run_exception(start_time, kind, reason, stacktrace, metadata)

@spec run_exception(integer(), atom(), term(), list(), map()) :: :ok

Emit a run exception event.

run_start(metadata)

@spec run_start(map()) :: :ok

Emit a run start event.

run_stop(start_time, metadata)

@spec run_stop(integer(), map()) :: :ok

Emit a run stop event.

sequence_start(metadata)

@spec sequence_start(map()) :: :ok

Emit a sequence start event.

sequence_stop(start_time, metadata)

@spec sequence_stop(integer(), map()) :: :ok

Emit a sequence stop event.

shrink_iteration(iteration, metadata)

@spec shrink_iteration(integer(), map()) :: :ok

Emit a shrink iteration event.

shrink_start(metadata)

@spec shrink_start(map()) :: :ok

Emit a shrink start event.

shrink_stop(start_time, metadata)

@spec shrink_stop(integer(), map()) :: :ok

Emit a shrink stop event.

span(event_type, metadata, fun)

@spec span(atom(), map(), (-> result)) :: result when result: term()

Execute a function with telemetry span instrumentation.

Emits start and stop (or exception) events around the function.

Examples

Telemetry.span(:run, %{model: MyModel}, fn ->
  # run logic
  {:ok, result}
end)