What is the best way to inject a logger?

I’m working on several projects right now that I’d like to be able to generate logs from.

The problem is that logging isn’t required for the code to work, so how do I configure it?

I see three possibilities:

Required in Constructor


  • Cleanest implementation


  • Requires user to pass in a logger even if they don’t want to log anything

Optional in Constructor


  • No longer requires the user to pass in a logger


  • Clutters up the constructor
  • I consider using null a code smell

Optional setLogger Method


  • Clean constructor
  • No nulls


  • Instantiating with a logger requires two statements


I’m leaning toward the third method for my packages even though it doesn’t feel like an ideal solution. It just feels better than the other two.

How are you handling injecting an optional logger? Let me know in the comments.

24 Replies to “What is the best way to inject a logger?”

    1. How many loggers are you willing to add? Iterating over a larger number of loggers will increase the number of calls to the logging method, also increase the number of log messages that need to be created. Having multiple back ends for logging probably should be the task of the one logger to be injected.

        1. While I do use Monolog, I don’t like to rely on proprietary features – it makes it harder to switch to a different logging framework, and the freedom to easily migrate to a different implementation is part of the value proposal of using PSR-3 in the first place.
          Either way, what I proposed is an alternative to setter-injection, and has two important advantages over it: avoiding the null-check, and avoiding potential side-effects from accidentally overriding an existing logger. I will never personally get to “a large number of loggers” – this concern seems like a bit of a stretch? But I work in a large team, and I’d like to avoid breaking the logging-features by accidentally overwriting somebody else’s logger, or vice-versa. Works for me 🙂

    1. Nice and simple, although not really my style. I try to stay away from global static methods. It’s nice that it’s just a facade so you can easily swap out what is being returned. Similar to what they do in Laravel.

  1. What about Getter Injection?
    A similar solution would be this facade function (in combination with a dependency injection container). Example:
    function logger()
    $logger = container()->get(Logger::class);
    if (!$logger) {
    $logger = new Logger(‘app’);
    $logFile = config()->get(‘log_file’);
    $handler = new RotatingFileHandler($logFile, 0, Logger::ERROR, true, 0775);
    container()->set(Logger::class, $logger);
    return $logger;
    logger()->error(‘My error message);
    There are pros and cons, of course.

  2. I guess I don’t understand the problem you’re trying to solve. In case #1 you have the minimum code and if you don’t what to log anything you pass a NullLogger instance. Your con is solved. For case #2 and case #3 you add a punch of code/complexity for no real benefit. In both cases it clutters the creation of the class.
    However, why is the developer making the choice whether or not the class should be logging. The is a runtime choice and therefore should be a configuration file so it can adjusted by the end-user.

      1. Of course, so initialize $this->logger = new NullLogger() in your constructor.
        Or use an array like I suggested – using foreach eliminates the need to check if a logger was added.
        Honestly, my personal preference these days is to just use constructor-injection, even for optional dependencies like this one – you can make the $logger constructor argument default to null, and initialize with $this->logger = $logger ?: new NullLogger() in the constructor.
        IMO, that’s the simplest, most obvious solution – treat the logger like you would treat any other dependency; in my general experience, regarding any component as “infrastructure” (or any other special status) leads only to less obvious or more complex solutions. (That said, I do tend to use the addLogger() approach, since, at the moment, I don’t know of a good PSR-3 filtering/aggregation library and, as said, would prefer not to rely on e.g. Monolog handlers or other proprietary architecture.)

  3. As a developer that consumes libraries, I would would like to wrap MyClass with MyClassLogger. Then I can handle logging on my terms. I see two problems with this, but i think it will keep the library code cleaner and concise:
    1) I don’t have fine grained control of logging at very specific points in the code, only before or after calls.
    2) private methods would not be available to wrap, easily fixed by marking all private methods as protected.
    That being said, within my own apps I have borrowed Laravel’s static proxy idea and actually use it for logging when I need it for diagnostics, but don’t want to inject the logger just for that.

  4. I think the simplest way to go is to create a setter and do a simple if statement to check if there is a logger before logging (you can create a private log method that wraps the if statement to avoid putting if’s all over your code or even create a trait for that), as the logger is not necessary to make things work. I use the constructor only for things that are really necessary for a class to work. I would do the same for something like a EventManager, that it is optional.

  5. I have developing PHP composer package I want to log error and exception on log file.
    but now, I can able to log inside my package log file.I am using monolog package.
    but I want to Log error on Project log file, for example error message store on laravel framework’s log file.Not my package log file. how it is possible, any solution or suggestion.
    use Monolog\Logger;
    use Monolog\Handler\StreamHandler;
    use Monolog\Handler\FirePHPHandler;
    use Monolog\Formatter\LineFormatter;
    class Log
    private static $logger;
    function __construct()
    // Create the logger
    self::$logger = new Logger(‘Msg91_Package_Log’);

    [2017-08-23T10:48:56.417391+05:30] Package_Log.DEBUG: TRACE:August 23, 2017, 10:48 am {“DEBUG”:”test log”} []

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.