Capture SDKs
HTTP ingest is the transport; these are the three ways to actually produce events for it — so an error reports itself the instant it happens, with full context, instead of waiting for a log file to be written and later parsed. All three require the HTTP ingest plugin enabled and an ingest key from Settings → Plugins → HTTP ingest.
The richer event model
Every capture path posts the same shape. Only message is required:
{
"events": [{
"message": "Cannot read property 'total' of null",
"exception_class": "TypeError",
"severity": "ERROR",
"stack": "#0 app/Services/Checkout.php(88): thrown\n#1 ...",
"environment": "production",
"release": "v2.4.0",
"fingerprint": "checkout-null-total",
"tags": ["checkout", "frontend"],
"module": "billing",
"request": {"method": "POST", "url": "https://app/checkout", "route": "checkout.store", "ip": "203.0.113.9"},
"breadcrumbs": [
{"category": "nav", "message": "/cart", "timestamp": "10:02:41"},
{"category": "http", "message": "POST /checkout → 500", "timestamp": "10:02:43"}
]
}]
}
request/user render as clean key-value sections in the issue's Context tab; breadcrumbs renders as a timeline leading up to the error. Don't send secrets — these fields are stored and displayed verbatim, so omit auth headers, passwords, tokens, and full request bodies.
Laravel: automatic, zero code changes
The bundled reporter hooks Laravel's exception handler, so unhandled exceptions report themselves — with request, user, and release context attached automatically. Events send after the response is already returned (a terminating callback), so capture adds no request latency.
LOG_LENS_REPORTER=true
LOG_LENS_RELEASE=v2.4.0 # optional — attaches a release to every event automatically
LOG_LENS_REPORTER_DRIVER=local # default: write straight into an embedded Log Lens, no network/key
# LOG_LENS_REPORTER_DRIVER=http # or report to a *separate* Log Lens install:
# LOG_LENS_INGEST_URL="https://logs.example.com/?api=ingest&app=my-app"
# LOG_LENS_INGEST_KEY="llk_…"
Use local when Log Lens is mounted inside this same Laravel app (see the Laravel package) — no ingest key needed at all, since it's writing in-process. Use http to report to a standalone Log Lens elsewhere.
Manual reporting anywhere in the app:
use LogLens\Laravel\LogLens;
LogLens::capture($exception);
LogLens::message('Nightly reconciliation drifted', 'WARNING');
Automatic capture still respects the app's own shouldReport exclusions — an exception your app already ignores stays ignored here too — and a failure inside the reporter itself never surfaces to your error handling.
Generic PHP: one dependency-free file
For anything that isn't Laravel — scripts, queue workers, other frameworks — LogLens\Client\LogLensClient needs nothing but ext-curl and ext-json:
use LogLens\Client\LogLensClient;
$logs = new LogLensClient(
'https://logs.example.com/?api=ingest&app=api',
'llk_…',
['environment' => 'production', 'release' => 'v2.4.0', 'channel' => 'worker'],
);
$logs->install(); // catches uncaught exceptions + fatal errors from here on
try {
charge($order);
} catch (\Throwable $e) {
$logs->captureException($e, ['tags' => ['billing'], 'user' => ['id' => $order->userId]]);
}
$logs->captureMessage('Queue backlog is high', 'WARNING');
install() registers both an exception handler and a shutdown handler (for fatal errors) and flushes automatically on shutdown. Delivery is best-effort over cURL and never throws back into your application — a Log Lens outage can't take your worker down with it.
Browser: catches what your log files never will
Frontend errors — a broken render, a failed fetch, an unhandled promise rejection — never show up in a server log at all. Embed loglens.js once:
<script src="https://logs.example.com/loglens.js"
data-ingest-url="https://logs.example.com/?api=ingest&app=web"
data-key="llk_…"
data-release="v2.4.0"></script>
It captures uncaught errors and unhandled promise rejections on its own, and exposes:
window.LogLens.captureException(error, { tags: ["checkout"] });
window.LogLens.captureMessage("Payment widget failed to mount", "WARNING");
Two details that matter for a browser context specifically: it sends via navigator.sendBeacon (falling back to fetch) so a report survives the page unloading right after the error, and it posts cross-origin without triggering a CORS preflight. The ingest key here is meant to be public — it's a write-only push token: anyone with it can add noisy events, but can't read anything back.
Why bother, given file parsing already works
Log files are inherently after-the-fact and best-effort (rotation, buffering, format drift). First-hand capture gets you: the error the moment it happens, richer structured context (request/user/breadcrumbs) than any log line format naturally carries, and frontend coverage that log parsing can never provide at all. Both paths converge on the same issue list, grouping, workflow, and alerting — capture is a better source, not a separate system.