Reporting exceptions
QuietGuard\Monitor\Reporter is the platform-neutral client. Adapters wire it to a host's exception and logging hooks; you can also drive it by hand.
Wiring a Reporter
The Reporter constructor takes a Config, an Http\HttpClient, a Support\Scrubber, a Payload\ExceptionPayloadBuilder, and an optional PSR LoggerInterface:
Sending data
The Reporter exposes three send methods, each returning bool and never throwing, monitoring must not break the host application:
| Method | Endpoint | Notes |
|---|---|---|
reportException(Throwable $e, array $context = []) | POST /api/v1/ingest | Builds the payload, scrubs the context, sends it. |
sendLogs(array $logs) | POST /api/v1/logs | Wraps logs as {"logs": [...]} and scrubs them. Returns true immediately when $logs is empty. |
sendDependencies(array $packages) | POST /api/v1/dependencies | Wraps as {"packages": [...]}. Returns false immediately when $packages is empty; dependencies are not scrubbed. |
A send returns true only on an HTTP 2xx. If the client is not configured (Config::isConfigured() is false) the methods return false without touching the network. Any transport exception is caught, logged through the optional logger as a warning, and turned into false.
A refusal is logged too, and that is what the logger is for. A non-2xx answer is written as a warning naming the status and the address: a 401 on a mistyped key, a 402 while a payment is outstanding, a 403 on a plan gate. CurlHttpClient never throws, it returns 0, so the catch above could not fire on those and each of them arrived at the caller as one silent false. Pass a logger in a host that has no console of its own, or the client has no way at all to tell you it has never worked.
The exception payload
Payload\ExceptionPayloadBuilder::build() turns a Throwable into the wire shape:
From version 0.3, the trace starts at the throw site: the exception's own file and line form frame zero, since getTrace() starts at the caller and never held the failing line. The trace is sliced to traceLimit frames, that frame included (1 = the site alone), each reduced to class, type, function, file, line. Application frames also carry a code object, produced by Payload\SourceSnippet: start, the number of the first line, and lines, the five lines on each side of the frame's line, read from the file that ran and cut at 500 characters, on at most ten frames per report (ExceptionPayloadBuilder::MAX_SNIPPET_FRAMES). A frame whose path contains /vendor/ never carries one, and any read that fails answers null rather than losing the report. codeSnippets: false on Config (and on the builder, which reads it as its third argument) sends file and line only. The Reporter passes the snippets through Scrubber::scrubSnippets(): a line that names a key of the scrub list, or a word of Scrubber::LINE_NEEDLES, and gives it a value is masked whole as [scrubbed], see Scrubbing. When release is non-null it is merged into context (your explicit context keys win on collision). The exact wire contract is described by the ingestion API (/docs/tool/1.0/ingestion-api).
Four fields are trimmed to the length the server accepts, by Support\Cut, with … [truncated] marking the cut: message at 8192 characters, class at 255, file at 1024 and release at 255. The endpoint validates them and answers 422 for the whole report, so a QueryException carrying its entire SQL statement, or an exception from an anonymous class whose name holds an absolute path, used to be exactly the report that vanished. The cut counts characters rather than bytes, so a multi-byte character is never split into a payload the server rejects as malformed UTF-8.
The transport abstraction
Http\HttpClient is a deliberately minimal contract:
The bundled Http\CurlHttpClient implements it with ext-curl and no other dependency: it JSON-encodes the body and sends Content-Type: application/json, Accept: application/json and Authorization: Bearer <token>. It returns 0 (rather than throwing) when JSON encoding or curl_init() fails.
Bringing your own client
Because HttpClient is a plain interface, not PSR-18 itself, you can wrap any client (a PSR-18 client, Guzzle, the framework HTTP client) behind it. Implementations must not throw and must return the status code (or 0):
Pass your implementation as the http argument to the Reporter. To capture errors automatically in a plain-PHP app, see the global error handler; to keep secrets out of payloads, see scrubbing.
You are reading the PHP Core v1.0 documentation.