uga/php4logger-bundle
A Symfony bundle that integrates the php4logger library into your application, registering logger services and configuration through the framework container. It provides dependency injection wiring for consistent, configurable application logging with minimal setup.
uga/php4logger-bundle is not a logging library in its own right — it is thin Symfony2 bundle glue around Apache log4php, exposing a logger service wired through Symfony's DI container and configured from a log4php.properties (or .xml) file. It has 0 stars, 0 dependents, no releases since February 2017, and no documentation beyond the repository name. Treat everything below as "what the bundle conventions imply," and expect to read the source before trusting it.
The uncomfortable first step for a Laravel developer: a Symfony bundle is registered in AppKernel/bundles.php and bootstrapped by the Symfony kernel. Laravel has no equivalent lifecycle hook. You cannot composer require this and have it work. Two viable paths:
apache/log4php library directly (it is abandoned but still on Packagist) or, better, skip it and use Laravel's built-in Monolog.Bundle subclass. This is the only way to reuse the code as-is.If you are evaluating it for a Symfony2 legacy app that is being migrated, the minimal steps are:
composer require uga/php4logger-bundle
// app/AppKernel.php (Symfony 2.x only)
$bundles = [
// ...
new Uga\PHP4LoggerBundle\UgaPHP4LoggerBundle(),
];
# app/config/config.yml
uga_php4_logger:
config_file: %kernel.root_dir%/config/log4php.properties
# log4php.properties
log4php.rootLogger=WARN, default
log4php.appender.default=LoggerAppenderRollingFile
log4php.appender.default.file=/var/log/app.log
log4php.appender.default.layout=LoggerLayoutPattern
log4php.layout.default.conversionPattern=%date{Y-m-d H:i:s} %-5level %logger %msg%n
First use case — inject the logger into a service rather than reaching for a global:
use Uga\PHP4LoggerBundle\Logger\Logger; // name is a guess; check the service id
class InvoiceService
{
private $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function send(array $invoice)
{
$this->logger->info('Sending invoice', ['id' => $invoice['id']]);
}
}
For Laravel specifically, the equivalent first move is a named Monolog channel in config/logging.php, which gives you the same pattern without the abandoned dependency:
'invoices' => [
'driver' => 'daily',
'path' => storage_path('logs/invoices.log'),
'level' => 'warning',
'days' => 14,
],
Configuration-file-first. The whole bundle is designed around log4php's hierarchical config: a root logger plus named child loggers, each with appenders (LoggerAppenderRollingFile, LoggerAppenderSyslog, LoggerAppenderEcho, LoggerAppenderMail) and layouts (LoggerLayoutPattern, LoggerLayoutSimple, LoggerLayoutXml). Loggers are looked up by dotted name — log4php.logger.app.invoices=INFO, invoices — and inherit from their parent. If you are porting, keep that naming convention; it is the only thing about this package worth preserving.
Constructor injection over the static Logger::getLogger(). log4php's original API is static (Logger::getLogger('foo')), which is untestable. The bundle's value-add is a container-managed instance. In Laravel, mirror this with a small wrapper class that resolves the channel from Log::channel():
final class InvoicesLogger
{
public function info(string $message, array $context = []): void
{
Log::channel('invoices')->info($message, $context);
}
}
Bridging log4php config into a Laravel app. If you must keep .properties files because ops owns them, write a custom Monolog handler that reads the same appender settings and translates them. This keeps the operational contract (file paths, rotation, pattern) while removing the Symfony bundle:
// config/logging.php — translate log4php appender settings
'channels' => [
'legacy' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\RotatingFileHandler::class,
'handler_with' => [
'filename' => env('LOG4PHP_FILE', storage_path('logs/app.log')),
'maxFiles' => 30,
],
],
],
Per-request vs. per-job scoping. Rolling-file appenders open a file handle on first write. In long-running queue workers and Octane workers, construct the logger once per worker, not per job, and force a flush (LoggerAppender::close()) on shutdown — otherwise buffered events vanish when the worker is killed.
Test pattern. Never assert against the concrete logger. Bind an in-memory array handler in your test container and assert on records; that is the only way to keep a 2017-era logging stack from polluting the test suite with real files.
bundle.php. composer require will succeed and do nothing useful. If you see this in a composer.json review, it is dead weight.create_function-era code, and E_STRICT/E_DEPRECATED noise on modern PHP.config_file paths resolve against the Symfony kernel root, not your CWD, so CLI/console commands may silently fall back to log4php's default console appender. Use absolute paths from %kernel.root_dir% or base_path().log4php.debug=true) as the very first debugging step, then disable it — it prints configuration dumps to stdout and will corrupt HTTP responses.LoggerAppenderRollingFile fails quietly when the log directory is not writable by the PHP-FPM/CLI user. A common failure is web writes succeeding while queue writes disappear; check both users, and remember storage/logs is often excluded from the container image.log4php.logger.app=DEBUG does not enable children configured at log4php.logger.app.invoices=WARN — the child's own level wins. In Laravel/Monolog terms there is no exact analogue, so translations are approximate and can cause "why is this filtered" hunts.LoggerAppenderFile (or LoggerAppenderAbstract) and register it via log4php.appender.x=MyAppender with a PSR-0 autoloaded class name; log4php resolves appenders by class string, so no bundle config change is needed. Do this rather than editing vendor code.monolog/monolog) regardless of framework. Laravel already ships it; Symfony already ships it. The only defensible reason to touch uga/php4logger-bundle is reading it to understand an existing legacy config so you can faithfully reimplement the appenders and conversion patterns when you cut over.How can I help you explore Laravel packages today?