Installation:
composer require beutsing/log-entry-bundle
php bin/console make:migration
php bin/console doctrine:migrations:migrate
Verify the log_entry table exists in your database.
First Log Entry:
Inject LogEntryService into a controller or service:
use Beutsing\LogEntryBundle\Service\LogEntryService;
public function __construct(private LogEntryService $logEntryService) {}
public function someAction(): Response
{
$this->logEntryService->createLogEntry(
userIdentifier: 'user@example.com',
action: 'user.created',
message: 'New user registered via admin panel',
companyId: 'company_123' // Optional
);
return new Response('Logged!');
}
Where to Look First:
LogEntryService (primary interface for logging).LogEntry (check src/Entity/LogEntry.php for field details).config/packages/beutsing_log_entry.yaml (if it exists; otherwise, check for bundle defaults).Manual Logging:
Use LogEntryService in controllers/services to log actions explicitly:
$this->logEntryService->createLogEntry(
userIdentifier: $user->getEmail(),
action: 'order.updated',
message: sprintf('Updated order #%s', $order->getId()),
metadata: ['order_id' => $order->getId()] // Optional (if supported)
);
Automatic Logging with Events:
Subscribe to Symfony events (e.g., KernelEvents::VIEW, Doctrine\ORM\Event\LifecycleEventArgs) to log entity changes:
// src/EventListener/LogEntityChangesListener.php
use Beutsing\LogEntryBundle\Service\LogEntryService;
use Doctrine\ORM\Event\LifecycleEventArgs;
class LogEntityChangesListener
{
public function __construct(private LogEntryService $logEntryService) {}
public function postPersist(LifecycleEventArgs $args): void
{
$entity = $args->getObject();
$this->logEntryService->createLogEntry(
userIdentifier: 'system', // Or fetch from security context
action: 'entity.persisted',
message: sprintf('%s created', get_class($entity))
);
}
}
Register the listener in services.yaml:
services:
App\EventListener\LogEntityChangesListener:
tags:
- { name: doctrine.event_listener, event: postPersist }
Security Context Integration: Fetch the current user in listeners/controllers:
$user = $this->getUser(); // In controllers
// OR
$user = $this->security->getUser(); // In services
$this->logEntryService->createLogEntry(
userIdentifier: $user->getEmail(),
action: 'dashboard.accessed'
);
Bulk Logging: For batch operations (e.g., imports), log once per batch:
$this->logEntryService->createLogEntry(
userIdentifier: $user->getEmail(),
action: 'users.imported',
message: 'Imported 100 users via CSV'
);
Custom Fields:
Extend the LogEntry entity to add fields (e.g., ipAddress, metadata as JSON):
// src/Entity/LogEntry.php
#[ORM\Column(type: 'json', nullable: true)]
private ?array $metadata = null;
Update the migration and service to handle the new field.
Logging Middleware: Create middleware to log API requests:
// src/Middleware/LogApiRequestsMiddleware.php
public function handle(Request $request, callable $next): Response
{
$response = $next($request);
$this->logEntryService->createLogEntry(
userIdentifier: $request->attributes->get('user')?->getEmail(),
action: $request->getMethod() . ':' . $request->getPathInfo(),
message: $request->getContent()
);
return $response;
}
Symfony Messenger: Dispatch log entries as messages for async processing:
use Beutsing\LogEntryBundle\Message\CreateLogEntry;
$this->messageBus->dispatch(
new CreateLogEntry(
userIdentifier: $user->getEmail(),
action: 'payment.processed',
message: 'Paid $100'
)
);
API Responses: Include log IDs in API responses for debugging:
{
"data": { ... },
"log_id": 42
}
Missing User Context:
userIdentifier if not passed explicitly.abstract class BaseController extends AbstractController
{
protected function getUserIdentifier(): string
{
return $this->getUser()?->getEmail() ?: 'anonymous';
}
}
Performance:
Migration Conflicts:
LogEntry fields may break migrations.Security:
message.metadata field for non-sensitive data.Bundle Updates:
LogEntry entity changes.Check Logs: Enable debug mode to see if logs are created:
php bin/console debug:container beutsing_log_entry
Verify the LogEntryService is registered.
Database Inspection:
Query the log_entry table:
SELECT * FROM log_entry ORDER BY createdAt DESC LIMIT 10;
Event Listeners:
Ensure listeners are tagged correctly in services.yaml:
tags:
- { name: doctrine.event_listener, event: postUpdate }
Custom Actions:
Create a LogEntryAction enum or use strings with prefixes (e.g., user., order.).
Log Entry Filters: Add repository methods to filter logs:
// src/Repository/LogEntryRepository.php
public function findByActionAndUser(string $action, string $userIdentifier): array
{
return $this->createQueryBuilder('le')
->where('le.action = :action')
->andWhere('le.userIdentifier = :user')
->setParameters([
'action' => $action,
'user' => $userIdentifier
])
->getQuery()
->getResult();
}
Export Functionality: Add a command to export logs to CSV/JSON:
// src/Command/ExportLogsCommand.php
use Beutsing\LogEntryBundle\Entity\LogEntry;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ExportLogsCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
$logs = $this->logEntryRepository->findAll();
// Export logic (e.g., to CSV)
return Command::SUCCESS;
}
}
Web Interface: Create a CRUD controller to view logs in the admin panel:
// src/Controller/Admin/LogEntryController.php
class LogEntryController extends AbstractController
{
public function index(LogEntryRepository $repo): Response
{
$logs = $repo->findBy([], ['createdAt' => 'DESC]);
return $this->render('admin/log_entry/index.html.twig', ['logs' => $logs]);
}
}
Testing:
Mock LogEntryService in tests:
$this->logEntryService = $this->createMock(LogEntryService::class);
$this->logEntryService->expects($this->once())
->method('createLogEntry')
->with(
$this->callback(function ($args) {
return $args['action'] === 'test.action';
})
);
How can I help you explore Laravel packages today?