Logging Entries
This guide shows how to log entries in real applications using Ledgerly.
It focuses on common patterns and recommended usage.
Basic Logging
The simplest entry:
ledgerly()
->action('invoice.created')
->log();
This records:
- action
- timestamp
- metadata collected automatically
Logging with Actor
To record who performed an action:
ledgerly()
->actor(auth()->user())
->action('invoice.created')
->log();
Actors are stored as polymorphic relationships.
Actors are optional but recommended for user-driven events.
Logging with Target
To record what was affected:
ledgerly()
->actor(auth()->user())
->target($invoice)
->action('invoice.updated')
->log();
Targets help build activity timelines and audit trails.
Logging Changes (Diffs)
When recording updates, include diffs:
ledgerly()
->actor(auth()->user())
->target($invoice)
->action('invoice.updated')
->withDiff([
'amount' => [1200, 1450],
'status' => ['draft', 'sent'],
])
->log();
Each diff entry should follow:
[field => [before, after]]
Diffs make logs much more useful for auditing.
Logging with Metadata
You can attach structured metadata:
ledgerly()
->action('invoice.sent')
->withMetadata([
'channel' => 'email',
'attempt' => 1,
])
->log();
Explicit metadata overrides automatic metadata if keys overlap.
Logging with Severity
Severity helps categorize entries:
ledgerly()
->severity('warning')
->action('invoice.overdue')
->log();
Common severity levels:
info
warning
error
critical
Severity is optional.
Logging Inside Transactions
Transactions group related entries:
ledgerly()->transaction(function () use ($invoice) {
ledgerly()->action('invoice.created')
->target($invoice)
->log();
ledgerly()->action('invoice.sent')
->target($invoice)
->log();
});
Entries inside a transaction share a correlation identifier.
Use transactions for:
- workflows
- batch processes
- multi-step operations
Logging with Context
Context automatically applies metadata:
ledgerly()->context([
'tenant_id' => 42,
]);
ledgerly()->action('invoice.created')->log();
This avoids repeating metadata.
Scoped Context
Context can be limited to a specific block:
ledgerly()->withContext(['tenant_id' => 42], function () {
ledgerly()->action('invoice.created')->log();
ledgerly()->action('invoice.sent')->log();
});
Context is restored automatically.
Automatic Actor Resolution
If your application always has an authenticated user, you can configure an actor resolver:
Ledgerly::resolveActorUsing(function () {
return auth()->user();
});
After this, actors are attached automatically when available.
Logging System Events
Some events are system-level:
ledgerly()->action('system.maintenance_started')->log();
Actors and targets are optional.
Logging in Background Jobs
Ledgerly works inside jobs:
public function handle()
{
ledgerly()
->action('import.started')
->log();
}
Metadata such as:
- source
- queue
- job_id
may be collected automatically.
Logging Failures
When handling exceptions:
try {
$service->process();
} catch (Throwable $e) {
ledgerly()
->action('payment.failed')
->severity('error')
->withMetadata([
'error' => $e->getMessage(),
])
->log();
throw $e;
}
Ledgerly records the business impact while application logs record technical details.
Logging in Middleware
Example of setting context in middleware:
ledgerly()->context([
'tenant_id' => $tenant->id,
]);
All entries in that request inherit the context.
Best Practices for Logging
Recommended:
- Log meaningful business events
- Use consistent action names
- Record diffs for updates
- Use context for shared values
- Use transactions for workflows
Avoid:
- Logging technical noise
- Logging large metadata blobs
- Logging sensitive data