psr-discovery/log-implementations
Discover available PSR-3 logger implementations at runtime without hard dependencies. Searches for well-known classes and returns the first compatible LoggerInterface instance, ideal for SDKs and libraries; supports multiple popular loggers and mocking/testing options.
Install the Package:
composer require psr-discovery/log-implementations
Add to composer.json under require (not require-dev) if logging is a core feature.
First Use Case: Inject logging into a Laravel service or SDK class:
use PsrDiscovery\Discover;
use Psr\Log\LoggerInterface;
class MyService
{
public function __construct(private ?LoggerInterface $logger = null)
{
$this->logger ??= Discover::log();
}
public function doWork()
{
$this->logger->info('Service is working');
}
}
Testing Setup:
Ensure mock implementations are installed in require-dev:
composer require --dev psr-mock/log-implementation
The package will auto-prioritize mocks in tests, so no additional config is needed.
public function __construct()
{
$this->logger = Discover::log();
}
$this->logger ??= app(LoggerInterface::class);
Prefer a Specific Implementation (e.g., for cloud logging):
use PsrDiscovery\Implementations\Psr3\Logs;
Logs::prefer('google/cloud-logging');
$logger = Discover::log(); // Uses Google Cloud Logging if available
Force a Mock in Tests:
Logs::use('psr-mock/log-implementation');
$mockLogger = Discover::log();
use PsrDiscovery\Discover;
use Psr\Log\LoggerInterface;
public function register()
{
$this->app->singleton(LoggerInterface::class, function () {
return Discover::log(singleton: true) ?? app('log');
});
}
Discover::logs() to inspect available implementations for runtime decisions:
$availableLoggers = Discover::logs();
if (count($availableLoggers) > 1) {
// Enable multi-logger features (e.g., audit + analytics)
}
{
"scripts": {
"test:logger": "php -r \"use PsrDiscovery\\Discover; echo Discover::log() ? 'OK' : 'FAIL';\""
}
}
Logs::prefer() in bootstrap/app.php to prioritize loggers based on environment variables:
if (app()->environment('staging')) {
Logs::prefer('monolog/monolog');
}
Log facade to delegate to Discover::log():
use Illuminate\Support\Facades\Facade;
class Log extends Facade
{
protected static function getFacadeAccessor()
{
return Discover::log() ?? parent::getFacadeAccessor();
}
}
Mock Leakage:
psr-mock/log-implementation) may be discovered in production if not explicitly excluded.Logs::use() to force a real logger in production:
Logs::use('monolog/monolog'); // Overrides auto-discovery
Singleton Conflicts:
Discover::log(singleton: true) may create a singleton outside Laravel’s control.$this->app->singleton(LoggerInterface::class, fn() => Discover::log());
Version Mismatches:
composer.json:
{
"conflict": {
"monolog/monolog": "3.0"
}
}
Discovery Order:
Logs::prefer('monolog/monolog');
$loggers = Discover::logs();
foreach ($loggers as $logger) {
echo "Found: {$logger->getPackage()} v{$logger->getVersion()}\n";
}
try {
$logger = Discover::log();
} catch (\Exception $e) {
report($e); // Laravel-specific
throw new \RuntimeException('Logger discovery failed', 0, $e);
}
Custom Discovery Rules:
use PsrDiscovery\Discover;
use PsrDiscovery\Implementations\Psr3\Logs;
class CustomLoggerDiscoverer
{
public static function discover(): ?LoggerInterface
{
// Add custom logic (e.g., check for environment variables)
if (app()->environment('local')) {
Logs::prefer('monolog/monolog');
}
return Discover::log();
}
}
Add New Implementations:
Laravel-Specific Fallbacks:
Log::shouldEmergency() or Log::shouldReport() to create conditional logging:
$logger = Discover::log() ?? app('log');
if ($logger && Log::shouldReport()) {
$logger->error('Critical error');
}
composer.json:
{
"autoload": {
"psr-4": {
"PsrDiscovery\\": "vendor/psr-discovery/log-implementations/src"
}
}
}
1.0.1:
composer require psr-discovery/log-implementations:1.0.1
How can I help you explore Laravel packages today?