# forge_ops_tracker v0.5.0 - Table of Contents

> Elixir error reporting client for a ForgeOps instance.

## Pages

- [forge_ops_tracker (Elixir)](readme.md)

## Modules

- [ForgeOpsTracker](ForgeOpsTracker.md): Elixir error reporting client for a ForgeOps instance
- [ForgeOpsTracker.Client](ForgeOpsTracker.Client.md): Delivers one payload over HTTP via `:httpc` (Erlang's own HTTP client, part of the `:inets`
application bundled with every OTP install: no Hex dependency needed, same reasoning as
`net/http` in the Ruby gem and `java.net.http.HttpClient` in the Java/Kotlin clients). Every
failure mode (DNS, connection, timeout, TLS, a non-2xx response) is caught here and turned
into `false` rather than a raised exception, since a broken or unreachable tracker must never be
able to break the host app.

- [ForgeOpsTracker.Configuration](ForgeOpsTracker.Configuration.md): Holds a single ForgeOps DSN plus everything else the client needs to build and deliver events.
Mirrors `gems/forge_ops_tracker`'s `Configuration`: a single DSN string carries
both the ingestion URL and the project's api_key: `https://<api_key>@host/api/v1/events`.
- [ForgeOpsTracker.DeliveryQueue](ForgeOpsTracker.DeliveryQueue.md): A small bounded queue, processed one event at a time, so delivery never blocks the caller that
raised the error. The BEAM equivalent of the Ruby/Python/Java clients' own background-thread-
backed queue: except here the "queue manager" is an ordinary supervised `GenServer` (started
once, see `ForgeOpsTracker.Application`, not spun up lazily on first push, since BEAM processes
are cheap enough that there's no meaningful cost to keeping one alive idle) and each actual
delivery runs in its own short-lived `Task`, not inline in the `GenServer`'s own callback.
- [ForgeOpsTracker.EventBuilder](ForgeOpsTracker.EventBuilder.md): Turns an exception/reason plus an Elixir stacktrace into the payload shape the ingestion API
expects. Unlike the Ruby/PHP/JS clients in this repo, backtrace parsing here is not a regex
over a string: `__STACKTRACE__` is already a structured list of `{module, function, arity,
location}` tuples (`location` a keyword list with `:file`/`:line`), the same situation Python's
`traceback` module and Java's `Throwable.getStackTrace()` put those clients in. Verified
directly against a real raised-and-rescued exception's `__STACKTRACE__` before relying on this
shape, not assumed from documentation alone.

- [ForgeOpsTracker.Integrations.Ecto](ForgeOpsTracker.Integrations.Ecto.md): Times every query a given `Ecto.Repo` runs, `kind: "query"`. Call once at startup, passing the
repo module itself
- [ForgeOpsTracker.Integrations.Oban](ForgeOpsTracker.Integrations.Oban.md): Times every Oban job, `kind: "job"`, and reports a genuinely failed job (retries exhausted, or
the worker explicitly gave up) the same way an unhandled exception anywhere else already is. Call
once at startup
- [ForgeOpsTracker.Integrations.Phoenix](ForgeOpsTracker.Integrations.Phoenix.md): Times every matched Phoenix route, `kind: "controller"`. Call once at startup, e.g.
`application.ex`'s own `start/2`, before the rest of the supervision tree starts
- [ForgeOpsTracker.Integrations.Phoenix.UserContextPlug](ForgeOpsTracker.Integrations.Phoenix.UserContextPlug.md): Automatically identifies the affected user for every error reported during this request, when
`conn.assigns[:current_user]` is present: the de facto convention across Phoenix's own generated
auth (`mix phx.gen.auth`), Pow, and most hand-rolled Guardian setups alike, since Phoenix itself
(unlike Rails/Devise) has no single dominant auth library this could depend on directly.
- [ForgeOpsTracker.LoggerHandler](ForgeOpsTracker.LoggerHandler.md): Reports any process crash anywhere in the whole BEAM VM, with zero further wiring needed:
attached via `:logger.add_handler/3` (see `ForgeOpsTracker.install_handlers/0`), a standard
`:logger_handler` behaviour implementation, the same technique other logger-integrated error
trackers in the Elixir ecosystem use for the same purpose.
- [ForgeOpsTracker.PerformanceFlusher](ForgeOpsTracker.PerformanceFlusher.md): Times every request/job/query in-process, bucketed by `{transaction_name, kind}` (`kind` is
`"controller"`/`"job"`/`"query"`, see the `ForgeOpsTracker.Integrations.*` modules, each a
different telemetry source), and periodically flushes each distinct bucket as one small
aggregate report, rather than one network call per event. Mirrors `gems/forge_ops_tracker`'s own
`PerformanceFlusher` exactly in shape (the aggregation itself: count + duration sum + max over a
period), but started once as a supervised `GenServer` (a fourth child of
`ForgeOpsTracker.Application`, alongside `Configuration` and `DeliveryQueue`) rather than lazily
spawning a background thread on first use: Elixir has no per-request-process fork boundary to
dodge the way Ruby's Puma/Passenger workers do (the actual reason that gem starts lazily), and
BEAM processes are cheap enough that there's no cost to just always running one.
- [ForgeOpsTracker.PiiScrubber](ForgeOpsTracker.PiiScrubber.md): Redacts likely-sensitive content out of a payload before it ever leaves this process: the
same patterns ForgeOps itself applies again on arrival (defense in depth: this layer keeps the
data off the wire and out of any request logging in between; the server-side layer is what
actually protects the database, and doesn't depend on every reporting app running an up-to-date
version of this client). Ported from
`gems/forge_ops_tracker/lib/forge_ops_tracker/pii_scrubber.rb`: same key list, same 8 regex
patterns, unmodified from the Ruby original: Elixir's `Regex` is Erlang's `:re` module, itself a
PCRE implementation, so unlike this repo's own C client (which had to adapt every pattern to
POSIX Extended Regular Expressions: no `\d`/`\s`/`\b`), nothing here needed translating.
- [ForgeOpsTracker.QueryNaming](ForgeOpsTracker.QueryNaming.md): A low-cardinality bucket name for a database query, for the same reason every other SDK's own
`transaction_name` never uses the raw SQL text directly: every slightly different query shape
would become its own bucket, and raw SQL is a real, if usually parameterized, risk of leaking a
literal value.
- [ForgeOpsTracker.Reporter](ForgeOpsTracker.Reporter.md): Ties Configuration, EventBuilder, and DeliveryQueue together into the one thing callers actually
need: report an exception. Mirrors every other SDK's own Reporter/ErrorSubscriber: never
raises. An error reporter that itself raises while reporting an error is the worst possible
failure mode, so every path here is wrapped to guarantee this never propagates back into the
caller (or, when called from `ForgeOpsTracker.LoggerHandler`, back into the `:logger` pipeline
itself, which would be just as bad).

