Full auto-instrumentation parity for the Laravel integration. After a one-line
Composer require and an API key, a Laravel app now emits errors, inbound HTTP,
logs, DB queries, queue-job spans, console/scheduler spans, cache/Redis/view
breadcrumbs, outbound HTTP, and trace propagation with no code changes.
Every collector is ON by default, individually toggleable via config/allstak.php
(or an ALLSTAK_* env var), and guarded by class_exists() / event-presence
checks so optional framework pieces degrade gracefully across Laravel 9–12. No
existing behavior was changed — all additions are net-new listeners.
JobProcessing now opens a span and breadcrumb,
JobProcessed finishes it (with duration), and JobFailed/JobExceptionOccurred
promote to captureException (unchanged). Each job runs under a fresh trace so
job telemetry is correlated end-to-end. Toggle: capture_queue (default true).CommandStarting opens a span +
breadcrumb, CommandFinished finishes it and records the exit code. Toggle:
capture_console (default true).scheduled_task_heartbeat (default true) in addition to
capture_scheduled_tasks.CacheHit / CacheMissed / KeyWritten /
KeyForgotten recorded as breadcrumbs (guarded). Toggle: capture_cache
(default true).CommandExecuted recorded as a breadcrumb
with command + duration (guarded; requires Redis events enabled). Toggle:
capture_redis (default true).composing view events recorded as light
breadcrumbs (guarded). Toggle: capture_views (default true).RequestReceived
event resets trace/scope/buffers between pooled requests so telemetry never
leaks across requests (guarded). Toggle: octane_reset (default true).capture_livewire
(default true).MessageLogged entries at error/critical/
alert/emergency that carry a Throwable in their context are promoted to
captureException (in addition to being shipped as a log), so logged
exceptions surface as grouped errors.User-Agent header — the transport now sends the mandated
User-Agent: allstak-<sdk>/<version> on every ingest and management request.
The header is derived from the active SDK identity (sdkName / sdkVersion
on Options), so a framework SDK that wraps this core — passing its own
identity — is attributed under its own name on the wire (e.g.
allstak-symfony/0.1.0), matching the sdkName/sdkVersion already carried
in the JSON body. Falls back to the core's own constants when unset. Covered
by an end-to-end transport test against the mock ingest server.Three feature waves landed on top of v1.2.3, bringing the PHP SDK to
release-health + offline-store + value-pattern-scrubbing parity with the rest of
the AllStak SDK ecosystem. No Options::VERSION bump is included in this section
— the release version is chosen at the release gate.
AllStak\Session\SessionTracker). On
init() the SDK posts a /sessions/start envelope carrying a distinct session
id, the resolved release (falling back to the SDK version), and the SDK
identifier; on shutdown it posts /sessions/end with the final status and total
duration. Errored/crashed transitions are recorded in-memory only (no per-error
network I/O) — the backend marks the session errored/crashed server-side from the
sessionId carried on every error event, enabling crash-free-session rates. One
session per process/request, re-entrancy safe, never sampled, and fully
fail-open. New opt-out: enableAutoSessionTracking (default true).AllStak\Transport\FileSpool). When the
shutdown drain cannot deliver (network outage, retries exhausted, process
exiting), un-sent telemetry is persisted to a filesystem spool (one PII-scrubbed
JSON file per envelope under an API-key-namespaced subdir of sys_get_temp_dir()
by default) and replayed through the normal retry transport on the next init().
Bounded by count + bytes + max-age with drop-oldest eviction; payloads are scrubbed
via the existing Sanitizer before they touch disk. Session lifecycle calls
are never spooled (live-only best-effort). A read-only / sandboxed / serverless FS
degrades silently to in-memory — never throws, never blocks init()/capture. New
config: enableOfflineQueue (default true), offlineQueuePath,
offlineQueueMaxEvents (100), offlineQueueMaxBytes (5 MiB),
offlineQueueMaxAgeSeconds (48h).Privacy\Sanitizer::maskMetadata:
sendDefaultPii setting): Luhn-valid credit-card runs and
hyphenated US SSNs are redacted. Luhn-invalid digit runs and bare 9-digit
numbers are preserved to avoid corrupting order/tracking ids.sendDefaultPii = true: emails and octet-validated IPv4 addresses
are redacted.
Structural keys (user, stack frames, filename/absPath/function,
release/sdk, span/trace ids, url/path/host, timestamps) are exempt so the
explicit setUser object, frame locations, and release tags are never corrupted.
Value scanning is depth- and length-bounded and fail-open. The flag is threaded to
both the wire chokepoint (HttpClient) and the offline spool (FileSpool) so disk
and wire scrub identically.Options::sendDefaultPii (default false, privacy-safe by default). When
false, auto-collected client IP from the Laravel integrations is dropped and the
email/IPv4 value scrubbers are active; when true, those value scrubbers are
disabled and the auto-collected IP is allowed.AllStak\Monolog\AllStakHandler) and a Symfony bundle
(AllStak\Symfony\AllStakBundle) integration, usable in any PHP app / plain
Symfony via Monolog.autoRegisterRelease), with the resolved
release/dist/commitSha/branch threaded through session and event envelopes.User-Agent header. The transport now stamps every ingest
and management request with User-Agent: allstak-php/<version> (e.g.
allstak-php/1.4.0). Previously the header was documented but never actually
sent, so requests went out with curl's default agent. Set via both the request
header array and CURLOPT_USERAGENT, and asserted end-to-end against the mock
ingest server (records the inbound User-Agent).sendDefaultPii = true. Key-name redaction behavior is unchanged.Options::VERSION aligned to 1.2.3 to match the released v1.2.3 git tag (the source of truth Packagist resolves from). The v1.2.3 release shipped the README quickstart correction below but left the runtime constant and this CHANGELOG at 1.2.2, so sdk.version and the User-Agent stamp under-reported the actual released version on the wire. The version-consistency test now passes against the tagged release.Transport\RetryHandler now honors the Retry-After response header on 429/503. Previously the 429 branch only fell through to exponential backoff and never read the header despite a comment claiming otherwise. HttpClient now captures response headers via CURLOPT_HEADERFUNCTION and surfaces retryAfter; the new pure RetryHandler::parseRetryAfter() resolves integer-seconds and HTTP-date forms, clamps to 300s, and falls back to backoff when the header is absent or invalid.\AllStak\Facade::captureError(...) (the only valid static SDK calls are AllStak::init() plus the Facade methods). Clarified that server-side sanitization scrubs sensitive fields on the canonical denylist.Sanitizer::SENSITIVE_METADATA_KEYS expanded to canonical 25 terms used across the AllStak SDK ecosystem. Added: proxy-authorization, set-cookie, pwd, api_key/apikey, x-api-key/x-allstak-key/x-auth-token/x-access-token, bearer, jwt, session, credit_card, card_number, cvv, ssn.[MASKED] → [REDACTED] for ecosystem-wide consistency.Sanitizer::maskMetadata now wired into HttpClient::doPost — wire chokepoint scrubs every payload before json_encode. One chokepoint protects every telemetry type. Pure, fail-open.Options::VERSION bumped to 1.2.1 so the runtime constant, this CHANGELOG entry, and the User-Agent stamp all agree. Closes the 1.0.0 / 1.2.0 version drift documented in the 2026-05-17 audit.Privacy\Sanitizer:
SENSITIVE_HEADERS extended with proxy-authorization, set-cookie, x-access-token.SENSITIVE_QUERY_PATTERNS extended with csrf, session.SENSITIVE_METADATA_KEYS extended with passwd, cookie, csrf, session_id, sessionid.maskMetadata now recurses into nested arrays so JSON-style payloads are fully redacted.AllStak::shutdown() now calls fastcgi_finish_request() first when available so the HTTP response is on the wire before the blocking drain runs. PHP-FPM workers are no longer held for the duration of the buffer flush. CLI / Octane / Swoole runtimes are unaffected because the function does not exist there. Internal drain logic moved to drainShutdownBuffers().tests/Unit/VersionTest.php) asserts Options::VERSION is non-empty and matches the CHANGELOG's top entry.https://api.allstak.sa accepted captureError and returned event IDs (1456c5f7-19e0-4364-82c4-4d037475b8dc, d801e98e-ee60-4292-9cc0-38338e7491f2). Sensitive metadata (authorization, stripe_api_key, nested password, csrf) was redacted to [MASKED] on the wire; safe metadata (order_id, nested city) was preserved.First public release of the AllStak PHP SDK on Packagist.
ALLSTAK_API_KEY in .env, zero code changes for the basics. The package is auto-discovered via extra.laravel.providers.AllStak\Config\Options::INGEST_HOST).AllStak\Laravel\AllStakServiceProvider):
config/allstak.php (publishable) on Laravel app start.AllStak\Laravel\AllStakRequestMiddleware onto the HTTP kernel to capture every inbound request, set the per-request context, open a root trace span, and auto-attach the authenticated user.Foundation\Exceptions\Handler::reportable() so unhandled exceptions are captured automatically with the authenticated user attached.Illuminate\Log\Events\MessageLogged to ship every Laravel Log::* call as a structured log entry.DB::listen callback to capture every Eloquent / raw SQL query with normalized SQL.Http::globalMiddleware() to capture every outbound Http:: call with true round-trip timing (success and failure).ScheduledTaskStarting, ScheduledTaskFinished, ScheduledTaskFailed, ScheduledTaskSkipped) to auto-instrument scheduled tasks as cron heartbeats — no manual startJob/finishJob calls needed.app->terminating(...) to flush all SDK buffers before Laravel sends the response.ALLSTAK_CAPTURE_* env flags to disable individual capture channels.Options::VERSION constant exposing the SDK version string.config/allstak.php publishable config file with sensible defaults pulled from APP_NAME / APP_ENV.Options::INGEST_HOST is now https://api.allstak.sa. To self-host AllStak or run integration tests against a local backend, set ALLSTAK_HOST=http://localhost:8080 (or wherever) in your Laravel .env — the provider will pass that through to AllStak::init().Options constructor no longer requires a host field. The optional host config key is accepted only for tests / self-hosted setups; passing nothing defaults to INGEST_HOST.production HTTPS required check in Options — superseded by the static INGEST_HOST policy.Http\Client\Events\ResponseReceived, which exposes no timing — every captured outbound row landed on the dashboard with durationMs=0. The new implementation wraps the Guzzle handler chain via Http::globalMiddleware() and times the actual $handler($request, $options) call with microtime(true) deltas. Both successful responses and failed requests (connection refused, timeout, DNS, etc.) are captured.UserContext with id / email / ip) automatically. The provider attaches the user inside the reportable() callback at error-report time, when Laravel's session middleware has already loaded the auth state.If you're upgrading from an earlier internal version: set ALLSTAK_API_KEY and remove any old ALLSTAK_HOST=... line from your .env (unless you self-host AllStak). Everything else is automatic. After upgrading you should immediately see:
durationMs on outbound HTTP rows (Requests page)Schedule::call(...) and Schedule::command(...) (Cron Jobs page)No code changes are required.
ValidationException, AuthorizationException, and ModelNotFoundException are intentionally not reported by Laravel's Foundation\Exceptions\Handler (they're 4xx user-input cases) and therefore do not appear on the Errors page. They are still visible on the Requests page (as 4xx rows) and on the Logs page if you log them. This is correct framework behavior, not an SDK gap.reportable() hook; high-throughput workers may want to call AllStak::getInstance()?->flush() after each job to keep buffers small.AllStak::init() static facadeInternal pre-1.0 development happened across the allstak/sdk-php repository before this public release. There were no published Packagist versions prior to 1.0.0.
How can I help you explore Laravel packages today?