tl;dr

If you want to add a new logger to Laravel just create new service extending `Logger`, inject it via the constructor and you’re good to go!


I’m a Symfony guy, but from some time I have to use Laravel. There is the neverending war between those, but what do. It’s not that bad eventually…

Recently I was supposed to add logs to some command, for debugging purpose. Laravel has its logger as facade but I didn’t want to use this one, I just wanted to log some data to separate log file, debug the command and delete the log. And there is no way I found to create a separate log by using the `Log` facade.

As it turns out, Laravel’s log facade uses monolog, so there is a possibility to access monolog itself and create a new logger. I’ve searched the web for the best solution, some suggest creating a new facade and using it, some suggests creating logger “in place” and use it only in my command. But none of it seems right.

But! Laravel has dependency injection with autowire! So I added a new logger to services, injected it to my command via the constructor and… It works! 🙂 And it is how.

Create new logger inside `services` directory

// app/Services/TestLogger.php
<?php

namespace App\Services;

use Monolog\Handler\StreamHandler;
use Monolog\Logger;

class TestLogger extends Logger
{
    public function __construct()
    {
        $handler = new StreamHandler(storage_path('logs/test.log'));
        parent::__construct("test_logger", [$handler]);
    }
}

As you can see it is easy and somewhat ugly. I created a new class extending from `Monolog\Logger` and use the constructor to set the logger’s name and handler. this way I have fully functional logger with its own name and its own logger file.
Logs looks as usual:

$ tail -f test.log
[2018-04-16 08:04:43] test_logger.WARNING: test 1523858683 [] []
[2018-04-16 08:04:45] test_logger.WARNING: test 1523858685 [] []

I suppose it must be a better way to do it, but it’s good enough for me.

Inject new logger to the command

Now I take advantage of autowiring and simply inject my new logger into command’s constructor

class TestCommand extends Command
{
    // ...

    /**
     * @var TestLogger
     */
    private $logger;

    public function __construct(TestLogger $logger)
    {
        parent::__construct();
        $this->logger = $logger;
    }

    // ...
}

Voila! My new logger is ready to use. Now I can use it like every logger implementing `Psr/LoggerInterface` interface. Sweet.

// ...
$this->logger->warning("test " . time());
// ...

Summary

I don’t know if it’s the best way to add logger, or if I do it completely wrong. But I think it’s always better to use dependency injection over facades. This example is fully functional and easy to reuse – I just inject my logger to other services and it’s done and it’s all I need 😉


Want more tech meat?