Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Api Logs Laravel Package

codetech/laravel-api-logs

A lightweight Laravel package that logs every authenticated API request — URL, method, IP, payload, headers, response, and duration — linked to the user who made it. Sensitive values are redacted before storage, and a HasApiLogs trait exposes each user's history.

View on GitHub
Deep Wiki
Context7

Getting Started

Treat this as an audit/history table for authenticated API traffic, not as a replacement for your application log. The package writes one row per authenticated request after the response is sent, linked polymorphically to the authenticated model.

Version check first. The 3.x line requires PHP ≥ 8.2 and supports Laravel 11–13 (Laravel 13 requires PHP ≥ 8.3). If you are on Laravel 7–10, stay on ^2.0; the setup differs slightly because middleware registration lives in app/Http/Kernel.php rather than bootstrap/app.php.

Install and publish:

composer require codetech/laravel-api-logs

php artisan vendor:publish \
  --provider="CodeTech\ApiLogs\Providers\ApiLogServiceProvider" \
  --tag=migrations

php artisan migrate

Register the middleware on the api group in bootstrap/app.php:

use CodeTech\ApiLogs\Http\Middleware\LogApiRequest;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->appendToGroup('api', LogApiRequest::class);
    })
    ->create();

Then opt the authenticatable model in:

use CodeTech\ApiLogs\Traits\HasApiLogs;

class User extends Authenticatable
{
    use HasApiLogs;
}

Your first useful use case is customer support: given a user, show their recent API calls with method, URL, payload, response, IP, and duration. $user->apiLogs is the entry point; from there you can filter by URL or timestamp, or build a small “request history” panel in your admin area. That single screen usually pays for the integration.

Look at these first:

  1. The published migration — it defines the actual columns you will query and index.
  2. The published config file (--tag=config) — redaction lists, replacement string, and the guard option added in v3.2.0.
  3. The ApiLog model — casts and the polymorphic causer relation.
  4. The docs site linked from the README — the README is intentionally minimal and the docs cover guards and redaction in more detail.

Implementation Patterns

Support and debugging view. Query ApiLog by causer/user, newest first, and expose a detail view with request payload, headers, and response. Because headers and payloads are already redacted, this is usually safe to show to support staff, but still gate it behind an admin permission. Prefer pagination and select only the columns you render; payload and response columns can be large.

Slow endpoint monitoring. The duration field lets you find the worst offenders without an APM:

ApiLog::query()
    ->where('created_at', '>=', now()->subDay())
    ->orderByDesc('duration')
    ->limit(50)
    ->get(['method', 'url', 'duration', 'created_at']);

Group by route or URL prefix in SQL if the table is large. Use this for prioritising optimisation work, not for precise latency SLOs — it measures the full request lifecycle including middleware and response handling.

Selective logging. The package is designed to be appended to the whole api group. If you only want it on specific routes, register LogApiRequest as a route middleware alias and attach it to chosen routes instead of the group. If you want to exclude known noisy endpoints while keeping group-level registration, wrap or subclass the middleware and return early for excluded paths before delegating to the parent. There is no documented route allow/deny list or sampling rate in the README, so verify the docs before assuming configuration covers this.

Multiple guards and model types. The causer relation is polymorphic, so any model can own logs provided it uses HasApiLogs. This is useful when your API authenticates both users and machine clients (for example, a Client model). Set the guard option in the published config to the guard your API actually uses; otherwise the middleware may resolve no authenticated user and skip logging. If you have more than one authentication path, test each one explicitly.

Auditing. Treat these rows as append-only audit records for authenticated API activity. Do not let application code update or delete individual logs as part of normal business logic. If you need long-term retention, export to cold storage first.

Retention and reporting. There is no built-in pruning command documented. Add a scheduled job that deletes in chunks to avoid long locks:

ApiLog::query()
    ->where('created_at', '<', now()->subDays(90))
    ->delete(); // chunk this with ->chunkById() for large tables

Add indexes that match your actual access patterns — usually (causer_type, causer_id, created_at) and created_at — but create them in a separate migration so upgrades remain predictable.

Testing. In feature tests using RefreshDatabase, publish or load the package migration, hit an authenticated route, and assert via assertDatabaseHas. Also assert redaction explicitly:

$this->assertDatabaseMissing('api_logs', [
    'request_data' => '{"password":"secret"}',
]);

Termination callbacks run in normal Laravel feature tests, so assertions after the request should see the row.

Forwarding to an external system. If you already have a SIEM or observability pipeline, hook into the ApiLog model’s created event and forward a sanitised summary. This avoids adding another write path inside the middleware and keeps the package’s storage behaviour untouched.

Gotchas and Tips

Only authenticated requests are logged. The package is explicitly for authenticated API traffic. Login, registration, password-reset, and unauthenticated webhook endpoints will not produce rows. If you need those, implement a separate logging path.

A missing trait silently disables logging. Since v3.0.0, an authenticated model without HasApiLogs does not throw; its requests are simply skipped. If logs are missing, check the trait first, then the guard config, then middleware registration.

Redaction is a denylist, not a guarantee. Keys like password, token, access_token, secret, and cvv are matched case-insensitively and recursively, and sensitive headers (Authorization, Proxy-Authorization, Cookie, X-Api-Key, CSRF, PHP basic-auth headers) are scrubbed. But if your API accepts a credential under a non-standard key such as auth_value, pin, or a nested proprietary field name, it will be stored. Extend the redaction lists in the published config and add a test that asserts your own credential field names are masked. Also remember that stored URLs have their query strings redacted, but the rest of the URL and payload remain.

Historical rows are not retroactively redacted. Logs written before v3.1.0 (or v2.1.0 on the 2.x line) may contain credentials. Audit, purge, or clean old rows if you upgrade into a redaction-enabled version.

The middleware writes synchronously in terminate(). This is the right place for latency, but it means one INSERT per authenticated request against your primary database. On high-traffic APIs, watch table growth and index bloat. There is no documented queue or sampling option; if this becomes a bottleneck, fork the middleware or move storage to a separate connection.

You own the migration, the vendor owns the model contract. Publishing the migration makes the table yours, but column changes in future package versions may require you to manually diff and apply them. Read the upgrade guide on every major bump; v3.0.0 changed timing storage from dynamic request properties to the api_logs.start request attribute. Any custom middleware reading $request->start must be updated.

Middleware order matters for authentication. The start time is set unconditionally in handle(), and the authenticated user is resolved at termination. If your authentication middleware runs after LogApiRequest and the guard has not been configured correctly, the request may be skipped. Append the middleware to the api group as documented, and verify your route-level auth:sanctum / auth:api middleware still executes as expected.

Non-JSON responses are handled, but not decoded. HTML error pages and XML responses are logged as raw content fixed in v2.1.1. Streamed and binary responses store response_data = null instead of attempting to decode. Do not assume response_data is always JSON when rendering it in an admin UI — render it as text with a content-type hint.

Large payloads become large rows. There is no documented request/response size cap. File uploads and bulk imports can bloat the table quickly. Consider excluding those routes with a custom middleware guard, or trim before storage in a subclass.

Do not log secrets in custom headers you control. Even though the built-in header list is sensible, internal tracing headers sometimes carry tokens. Add them to the redaction config rather than relying on the defaults.

Debugging checklist for missing rows:

  1. Is the route actually in the api middleware group?
  2. Is the user authenticated at termination time?
  3. Does the authenticated model use HasApiLogs?
  4. Does the configured guard match the guard used by the route?
  5. Did the migration run against the connection the model uses?
  6. Is an exception being thrown during terminate() and swallowed by your error handler?

Adoption risk. With zero stars and zero dependents, this is early-adopter territory. The package is small enough to vendor or fork if maintenance stalls. Before committing, pin a version, read the upgrade guide, and keep your own tests around redaction and retention so a future upgrade cannot silently change behaviour.

Extension surface is limited by design. There is no documented configurable model or repository abstraction. If you need custom columns, sampling, or queueing, subclass the middleware and fork the model rather than fighting the package. For day-to-day use, the default setup is sufficient — treat customisation as an explicit maintenance cost.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator