v4codes/observer
最新稳定版本:v0.1.1
Composer 安装命令:
composer require v4codes/observer
包简介
Self-hosted APM for Laravel with zero external dependencies. Parent/child observability over native Laravel events, stored in your existing RDBMS.
README 文档
README
Zero external dependencies. Parent/child observability built entirely on native Laravel events, stored in the relational database you already run (MySQL / MariaDB / PostgreSQL).
Observer is a single installable package that gives you full application-performance coverage — requests, queries, jobs, exceptions, logs, mail, notifications, cache, commands, scheduled tasks, outbound HTTP, users and host metrics — with correlated traces, exception grouping into issues, aggregated dashboards and internal alerting. No SaaS, no third-party agent, no external service.
Screenshots
The parent's self-hosted dashboard (Blade + Tailwind, no build step):
| Fleet overview | Project dashboard |
|---|---|
![]() |
![]() |
| Trace timeline (N+1 flagged) | Issues |
![]() |
![]() |
How it works
One app runs as the parent (ingests, stores, aggregates, exposes read contracts). Every other app runs as a child (observes its own lifecycle via native Laravel events and ships batches to the parent). Capture is fully decoupled from delivery:
request lifecycle ──> in-memory buffer ──(terminate)──> obs_outbox ──(observer:ship daemon)──> parent /ingest
The request path never does network I/O or heavy serialization. If the parent is offline the outbox accumulates and drains later — the host app never breaks (RNF-2).
Getting started
You need one parent app (collects + shows the data) and one or more children (the apps you want to observe). The same package powers both — only the mode differs.
Step 1 — Set up the parent
composer require v4codes/observer
php artisan observer:install --parent # writes .env, publishes, migrates
That's it — the parent is in parent mode, the dashboard is live at
https://apm.example.com/observer, and the maintenance schedule
(aggregate / evaluate / partition / prune) is auto-registered. Just make
sure the Laravel scheduler cron is running (Forge configures it by default):
* * * * * cd /path && php artisan schedule:run >> /dev/null 2>&1
Step 2 — Create a project (in the dashboard)
Open the dashboard, go to Manage projects, and create one project per child. On create, the panel shows a ready-to-run install command (the secret is shown only once) — copy it. You can also do it from the CLI:
php artisan observer:project "My App" # scheduler delivery (default) php artisan observer:project "My App" --delivery=daemon
Step 3 — Connect a child
Paste the command from Step 2 into the child app (or into your Forge deploy script — it is fully non-interactive):
php artisan observer:install --child \ --parent-url=https://apm.example.com \ --project=my-app \ --token=Yz3... \ --secret=9aF...
This writes the child's .env, publishes, migrates, and — with delivery=scheduler
(the default) — auto-registers observer:ship --once every minute. With the
scheduler cron already running, nothing else is needed.
High volume? Create the project with
--delivery=daemon(or setOBSERVER_DELIVERY=daemon) and supervisephp artisan observer:shipunder Supervisor / a Forge Daemon for near-real-time delivery.
Step 4 — Verify
Generate some traffic on the child (load a page, run a job). Within a minute you'll see the
project light up on the parent's overview, with traces, slow queries, issues and host metrics.
Tune what's captured and how long it's kept in config/observer.php.
Tuning knobs (most common)
OBSERVER_SAMPLE_REQUEST=1.0 # keep 100% of request traces (lower for high volume) OBSERVER_ALWAYS_KEEP_MS=1000 # always keep traces slower than this, regardless of sampling OBSERVER_RAW_RETENTION_DAYS=7 # how long raw events live OBSERVER_AGG_RETENTION_DAYS=90 # how long aggregates live OBSERVER_DELIVERY=scheduler # scheduler (cron) or daemon (supervised observer:ship)
Disable a noisy recorder entirely, or sample a category, in config/observer.php
(child.recorders and child.sample.type_gate).
Commands
| Command | Mode | What it does |
|---|---|---|
observer:install --parent|--child |
both | Write .env, publish config + migrations, migrate |
observer:project {name} |
parent | Create a project (mints token + secret); --list to list |
observer:ship |
child | Drain the outbox and ship batches (daemon; --once for the scheduler) |
observer:aggregate |
parent | Roll raw events into aggregates + group exceptions into issues |
observer:evaluate |
parent | Evaluate heartbeats/issues, open/resolve incidents, fire alerts |
observer:partition |
parent | Ensure/pre-create obs_events partitions (MySQL) |
observer:prune |
parent | Apply retention (drop old raw events + aggregates) |
The parent's maintenance schedule and the child's shipping (
schedulerdelivery) are auto-registered by the package — you only need the Laravel scheduler cron running. SetOBSERVER_PARENT_SCHEDULE=false/OBSERVER_CHILD_SCHEDULE=falseto opt out and wire them by hand.
Dashboard
The parent serves a self-contained dashboard (Blade + Tailwind via CDN — no build step, no NPM, no Composer package outside Laravel core) at the route prefix:
https://apm.example.com/observer
It reads exclusively through the read layer (ObserverRepository / DashboardRepository) and
covers an overview of all projects (health, throughput, error rate, p95), per-project drill-down
(requests, slow queries + N+1, jobs/queues, cache hit rate, schedule + heartbeats, outgoing HTTP,
logs, mail/notifications, host metrics), grouped issues with stack traces, and a span-waterfall
trace viewer. Access is guarded by the viewObserver ability — define it in a service provider
to open it beyond the local environment:
use Illuminate\Support\Facades\Gate; Gate::define('viewObserver', fn ($user) => $user->isAdmin());
Write actions (creating/rotating projects, triggering maintenance commands) are
guarded by a separate manageObserver ability. Define it the same way:
use Illuminate\Support\Facades\Gate; Gate::define('manageObserver', fn ($user) => $user->isAdmin());
Alerting
Incidents (a dead scheduler, an error spike) fire through internal channels
listed in observer.alerts.channels. By default that's the Database channel
(the incident surfaces in the dashboard) and the Log channel. To also send
e-mail, enable the mail channel and set recipients — it uses the parent app's
own mailer (config/mail.php / your .env SMTP), no separate transport:
OBSERVER_ALERT_EMAILS=ops@example.com,oncall@example.com
// config/observer.php — observer.alerts.channels \Stochero\Observer\Alerting\Channels\MailAlertChannel::class,
Quality
composer test # PHPUnit — acceptance criteria from the spec (§15) + dashboard render composer phpstan # PHPStan at level max (Larastan), green
Static analysis runs at level max with no baseline — zero errors. mixed from
config(), json_decode() and query-builder rows is narrowed at the edges with typed
helpers (Support\Cast, Support\Json) and precise array-shape / generic annotations, so
the type information flows all the way through. PHPStan and Larastan are dev-only — they
don't affect the zero runtime-dependency guarantee.
See config/observer.php for the full configuration surface and
docs/ARCHITECTURE.md for the design.
Scaling & databases
- MySQL / MariaDB:
obs_eventsis RANGE-partitioned onoccurred_date;observer:prunedrops whole partitions (cheap at any volume). - PostgreSQL / SQLite: a single table pruned with chunked DELETEs — fine for moderate volume; for very high volume prefer MySQL partitioning.
- The parent ingest is a single write path. Past roughly 5–10M events/day on one node, scale the parent's database (faster disk, more IOPS) first.
- High shipping volume? Create the project with
--delivery=daemonand lowerobserver:ship --batchif individual traces are large — the parent rejects a POST overOBSERVER_MAX_BODY_BYTESorOBSERVER_MAX_EVENTSwith HTTP 413.
Security
- Children authenticate with a per-project token and sign each batch body with an HMAC-SHA256 secret (timing-safe comparison, anti-replay window). Secrets are stored encrypted and shown only once.
- Sensitive keys (
observer.child.scrub) are redacted from query bindings, request input, headers, log context and exception messages; stack-trace file paths are relativized to the app base path. - Dashboard read access is gated by the
viewObserverability; write actions (managing projects, triggering maintenance) by a separatemanageObserver. Define both in a service provider to open access beyond local.
License
MIT.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-09



