expdev07/laravel-logsnag
expdev07/laravel-logsnag is a Laravel wrapper for LogSnag that makes it easy to send events, insights, and notifications from your app via a clean facade and config. Supports channels, user identification, and real-time activity tracking.
1. Install
composer require expdev07/laravel-logsnag
The service provider and facade are auto-discovered via package discovery — no manual registration needed on Laravel 5.5+.
2. Publish the config
php artisan vendor:publish --provider="Expdev07\Logsnag\LogsnagServiceProvider"
# or, depending on the package tag:
php artisan vendor:publish --tag=logsnag-config
This creates config/logsnag.php. Read this file first — it is the single source of truth for the project name, token, default channel, and any queue/HTTP settings. Then skim src/Logsnag.php (or whatever the main client class is called) to confirm the exact method signatures, since the package predates the official SDK and method names vary between releases.
3. Configure .env
LOGSNAG_TOKEN=your_project_token
LOGSNAG_PROJECT=your-project-name
The token is a project token from the LogSnag dashboard (Project → Settings → API), not your account API key. The project name is the slug shown in the dashboard URL, not the display name.
4. First use case — publish one event
The fastest way to verify wiring end-to-end is to push a single event from php artisan tinker:
use Logsnag;
Logsnag::publish(
channel: 'general',
event: 'Hello from Laravel',
description: 'Wiring up LogSnag',
icon: '👋',
);
If you see the event in the LogSnag feed, the rest is just finding the right places in your app to call it. Add notify: true to the same call and install the LogSnag mobile app to confirm push works before you start wiring real events.
The most maintainable pattern is to publish from a listener rather than sprinkling Logsnag::publish() through controllers. This keeps the third-party call off the request path (queue the listener) and makes it trivial to disable or swap later.
// app/Listeners/LogOrderPlaced.php
class LogOrderPlaced
{
public function handle(OrderPlaced $event): void
{
Logsnag::publish(
channel: 'orders',
event: 'New order',
description: "Order #{$event->order->id} — {$event->order->total}",
icon: '🛒',
tags: ['plan' => $event->order->plan, 'country' => $event->order->country],
);
}
}
Register it under EventServiceProvider::$listen with ShouldQueue. Now every event(new OrderPlaced($order)) in your codebase feeds LogSnag with no extra call sites.
Treat channel as your feed filter. LogSnag subscribers subscribe per-channel, so decide the taxonomy up front and stick to it:
errors — everything caught by your exception handler (this is usually the highest-value channel; subscribe push only to this one)orders, signups, payments — business eventsdeploys, jobs — operational eventsinsights — reserved for the insights API, don't publish events hereLowercase, hyphenated, stable names. Renaming a channel later orphans push subscriptions.
The highest-leverage single integration is a single call in app/Exceptions/Handler.php (or bootstrap/app.php on Laravel 11+) that publishes every unhandled exception, with notify: true:
public function register(): void
{
$this->reportable(function (Throwable $e) {
Logsnag::publish(
channel: 'errors',
event: class_basename($e),
description: Str::limit($e->getMessage(), 250),
icon: '🚨',
notify: $e instanceof CriticalException,
);
});
}
This is where push notifications earn their keep — you get a phone alert when production breaks, not a dashboard you have to remember to open.
Use the insights API rather than publishing events for anything you want to chart. Typical pattern: increment on signup, decrement on deletion, and record daily snapshots from a scheduled command.
Logsnag::insight([
'title' => 'Total Users',
'value' => User::count(),
'icon' => '👥',
]);
Because LogSnag is stateless, you send the absolute value on a schedule (e.g. php artisan schedule:run hourly) and LogSnag draws the line. Don't try to compute deltas yourself unless the package exposes a dedicated increment helper.
tags are string key/value pairs. Use them for things you'll actually filter on in the LogSnag UI — user_id, plan, tenant, environment, release. Keep values short; they are not meant for free-text.
If you ship a single app across environments, don't let staging pollute production. Either point LOGSNAG_PROJECT at a separate project per environment, or prefix the channel (staging-orders). The former is cleaner because push subscriptions are per-project — you can subscribe to production errors only.
Http::fake()Since the package goes through Laravel's HTTP client, you can assert calls without hitting the network:
Http::fake(['api.logsnag.com/*' => Http::response(['status' => 200])]);
$this->post('/orders', [...])->assertSuccessful();
Http::assertSent(fn ($request) => $request['event'] === 'New order');
Add this to your feature tests for any listener that publishes — it catches payload regressions cheaply.
Never let LogSnag break a request. This is a third-party HTTP call. Depending on the release, the client may throw on non-2xx responses or on connection timeouts. Wrap direct calls in try/catch (\Throwable) and report() (or log locally), or always publish from queued listeners. A marketing event tracker taking down checkout is the classic failure mode.
The package likely does not swallow failures for you. Check src/ — if it calls ->throw(), an expired token or a LogSnag outage becomes a 500. Verify the response yourself in critical paths.
Config cache bites. After changing LOGSNAG_PROJECT or LOGSNAG_TOKEN, run php artisan config:clear in local and re-deploy with config:cache in production. A stale cached project name is the most common "it silently stopped working" cause.
Token vs project mismatch is silent. LogSnag will 401/403 if the token doesn't belong to the named project. The package may not surface that clearly — enable HTTP debug logging or Http::fake-based tests to confirm the request body. Test in tinker before wiring events everywhere.
notify: true is a no-op unless subscribed. Push requires (a) the LogSnag app installed, (b) a subscription on that exact channel or event in the dashboard. If pushes aren't arriving, check the subscription in the LogSnag UI before debugging code.
Respect field limits. LogSnag truncates or rejects long description values, caps tag counts/lengths, and has channel/event naming rules (no spaces in channels, length limits). Str::limit($message, 250) in the exception handler is not paranoia — exception messages plus stack context blow past the limit easily.
Icons must be a single emoji. Passing a string that isn't a valid emoji can render oddly or be rejected. Keep them to one character; no variation selectors mixed with text.
Rate limits and queue throughput. The free tier is rate-limited. If you publish per-request at scale, put the listener on a dedicated queue with a rateLimited middleware (RateLimiter::for('logsnag', fn () => Limit::perMinute(60))) so a traffic spike doesn't produce a wall of 429s.
Low maintenance, small userbase. Fourteen stars and a single maintainer means: read the source (it's small), pin the version in composer.json, and don't assume upstream will chase LogSnag API changes. If you need longevity, consider a thin custom client over Http — the API surface is three endpoints — or the official SDK. Wrapping the package behind your own LogSnag service class lets you swap the implementation without touching call sites.
No automatic Laravel event hooks. Despite the tagline, the package almost certainly ships zero out-of-the-box integrations — it's a client, not a profiler. Every event you see in the feed is one you explicitly sent. Budget an afternoon to instrument the handful of events that matter (signups, payments, exceptions, deploys) rather than everything.
Extension point: swap the HTTP client binding. If the package binds its client as a singleton, you can $this->app->bind(Logsnag::class, fn () => new FakeLogsnag()) in tests instead of Http::fake(), giving you more granular assertions and no JSON shape coupling.
How can I help you explore Laravel packages today?