<h1 align="center">BoopErrorTracker</h1>

<p align="center">
  <a href="https://hex.pm/packages/boop_error_tracker"><img src="https://img.shields.io/hexpm/v/boop_error_tracker.svg" alt="Hex.pm"></a>
  <a href="https://hexdocs.pm/boop_error_tracker"><img src="https://img.shields.io/badge/hex-docs-blue.svg" alt="Hex Docs"></a>
  <a href="https://hex.pm/packages/boop_error_tracker"><img src="https://img.shields.io/hexpm/dt/boop_error_tracker.svg" alt="Downloads"></a>
  <a href="https://github.com/chrisgreg/boop_error_tracker/blob/main/LICENSE"><img src="https://img.shields.io/hexpm/l/boop_error_tracker.svg" alt="License"></a>
</p>

<p align="center"><strong>Your Elixir app raised. Your phone knows.</strong></p>

Sends [ErrorTracker](https://github.com/elixir-error-tracker/error-tracker) errors to
your iPhone through [Boop](https://github.com/chrisgreg/boop), the tiny self-hosted
push notification inbox.

ErrorTracker deliberately does not do notifications. This package attaches to the
telemetry events it already emits, so you install it next to ErrorTracker and
nothing about ErrorTracker changes.

## Installation

```elixir
def deps do
  [
    {:error_tracker, "~> 0.9"},
    {:boop_ex, "~> 1.0"},
    {:boop_error_tracker, "~> 1.0"}
  ]
end
```

Set up ErrorTracker as usual (repo, migration, router). Then configure the Boop client and this package:

```elixir
# config/runtime.exs
config :boop_ex,
  url: System.fetch_env!("BOOP_URL"),
  api_key: System.fetch_env!("BOOP_API_KEY")

config :boop_error_tracker,
  environment: config_env(),      # tagged on every event
  source: "my_app",               # shown next to the error in Boop; default "error_tracker"
  enabled: config_env() == :prod
```

That's it. The handler attaches when the application starts.

## What you get

Each push opens a full error page in the Boop app: exception type and message, the stacktrace with your app's frames highlighted, ErrorTracker's context and breadcrumbs, and tags for environment, source function/line and the ErrorTracker ids so you can jump to the ErrorTracker UI.

| Event | Default | Meaning |
| --- | --- | --- |
| `:new` | on | An error ErrorTracker has never seen before |
| `:unresolved` | on | An error you marked resolved in the ErrorTracker UI happened again |
| `:occurrence` | off | Every occurrence of any error, throttled per error (`throttle`, default 10 minutes) |

Errors muted in ErrorTracker are never sent.

## Options

```elixir
config :boop_error_tracker,
  enabled: true,
  environment: "prod",
  source: "my_app",
  level: :error,                  # :critical makes pushes prominent
  notify_on: [:new, :unresolved], # add :occurrence for ongoing errors
  throttle: :timer.minutes(10),   # minimum gap between :occurrence pushes for the same error
  in_app: [:my_app, :my_app_web], # which OTP apps count as "your code" in stacktraces
  tags: %{team: "web"}            # static tags on every event
```

Sending goes through `Boop.send_async/2`: it never blocks the process that raised, never raises, and logs failures at `:warning`.

## Usage rules for AI agents

Ships a [`usage-rules.md`](usage-rules.md) for [usage_rules](https://hexdocs.pm/usage_rules): `mix usage_rules.sync AGENTS.md --all --link-to-folder deps`.

## Licence

MIT.
