adamaru/monolog-fluentd-bundle
Symfony bundle adding a Fluentd handler to Monolog. Sends application logs to a Fluentd collector via host/port, with configurable level and bubbling. Integrates as a Monolog service handler for centralized logging and analysis.
Installation:
composer require adamaru/monolog-fluentd-bundle
Add the bundle to config/bundles.php:
return [
// ...
Seretalabs\MonologFluentdBundle\MonologFluentdBundle::class => ['all' => true],
];
Configure config/packages/monolog.yaml:
monolog:
handlers:
fluentd:
type: service
id: monolog_fluentd.monolog_handler
Configure Fluentd connection in config/packages/monolog_fluentd.yaml:
monolog_fluentd:
host: localhost
port: 24224
level: DEBUG
bubble: true
First Use Case:
Inject the Monolog\Logger service and log:
use Psr\Log\LoggerInterface;
class MyService
{
public function __construct(private LoggerInterface $logger) {}
public function doSomething()
{
$this->logger->info('test.fluentd', ['key' => 'value']);
}
}
Structured Logging:
Use the second argument of log methods (e.g., info(), error()) to pass structured data:
$this->logger->error('user.login.failed', [
'user_id' => 123,
'ip' => $request->ip(),
'timestamp' => now()->toIso8601String(),
]);
Fluentd will forward this as a JSON payload with the log level as the tag.
Contextual Logging:
Attach context dynamically using Monolog’s pushContext():
$this->logger->pushContext(['user_id' => $user->id]);
$this->logger->info('action.performed');
$this->logger->popContext(); // Remove context
Conditional Logging:
Use if ($this->logger->isEnabled($level)) to avoid unnecessary processing:
if ($this->logger->isEnabled(Logger::DEBUG)) {
$this->logger->debug('expensive.operation', ['data' => $expensiveData]);
}
Tagging Logs:
Prefix log messages with a tag (e.g., service.name.event) to filter in Fluentd:
$this->logger->info('order.processed', ['order_id' => 42]); // Tag: "order.processed"
Symfony Events: Log events in event listeners/subcribers:
public function onKernelRequest(GetResponseEvent $event)
{
$this->logger->info('kernel.request', [
'method' => $event->getRequest()->getMethod(),
'path' => $event->getRequest()->getPathInfo(),
]);
}
Doctrine Events: Log database operations:
public function onFlush(OnFlushEventArgs $event)
{
$this->logger->debug('doctrine.flush', [
'entities' => count($event->getEntityManager()->getUnitOfWork()->getScheduledEntityInsertions()),
]);
}
API Controllers: Log HTTP requests/responses:
public function index(Request $request)
{
$this->logger->info('api.request', [
'method' => $request->getMethod(),
'route' => $request->getPathInfo(),
'query' => $request->query->all(),
]);
// ...
}
Queue Workers: Track job processing:
public function handle(Job $job)
{
$this->logger->info('job.started', ['job_id' => $job->id]);
try {
// Process job
$this->logger->info('job.completed', ['job_id' => $job->id]);
} catch (\Exception $e) {
$this->logger->error('job.failed', [
'job_id' => $job->id,
'error' => $e->getMessage(),
]);
}
}
Connection Issues:
telnet localhost 24224
bubble: false in config and checking the main handler.Serialization Errors:
json_encode() or getAttributes() for objects:
$this->logger->info('user.data', [
'user' => json_encode($user->toArray()),
]);
Performance Overhead:
foreach iterations).Tag Collisions:
error) across services may clutter Fluentd filters. Use namespaced tags (e.g., appname.error).Deprecated Bundle:
monolog/handler-fluentd (official Monolog handler).ruflin/elastica + custom Fluentd output plugin.Check Fluentd Logs:
Monitor Fluentd’s own logs (typically /var/log/td-agent/td-agent.log) for connection/rejection errors.
Enable Monolog Debugging: Temporarily add a stream handler to debug locally:
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
fluentd:
type: service
id: monolog_fluentd.monolog_handler
Test with curl:
Verify Fluentd is accepting logs:
echo '{"json": "test"}' | nc localhost 24224
Default Level:
The bundle defaults to DEBUG, but the monolog config may override it. Explicitly set:
monolog_fluentd:
level: INFO # Override if needed
Bubbling:
Set bubble: false to prevent logs from propagating to parent handlers (useful for dedicated Fluentd handlers).
SSL/TLS: The bundle does not support encrypted connections. For secure setups, use:
forward plugin with TLS.Custom Formatter: Extend the handler to modify log structure before sending:
use Seretalabs\MonologFluentdBundle\Handler\FluentdHandler;
class CustomFluentdHandler extends FluentdHandler
{
protected function getDefaultFormatter()
{
return new \Monolog\Formatter\LineFormatter(
"[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n",
null,
true,
true
);
}
}
Register as a service in config/services.yaml:
services:
monolog.handler.fluentd.custom:
class: App\Logging\CustomFluentdHandler
arguments:
- '@monolog_fluentd.client'
tags:
- { name: monolog.logger, channel: 'main', handler: true }
Dynamic Host/Port: Override the client dynamically (e.g., for multi-environment setups):
$container->setParameter('monolog_fluentd.host', getenv('FLUENTD_HOST'));
How can I help you explore Laravel packages today?