Magento 2: Replacement for Mage::log method?

67

27

In Magento 1, if you wanted to send a message to the logs, you'd use a static method on the global Mage class.

Mage::log($message, Zend_Log::DEBUG, "my-log-file.log");

Is there an equivalent in Magento 2? I've googled through the dev docs site and haven't seen anything obvious that pops out. There's this Inchoo article, but it's from almost a year ago and so much has changed since then.

As a Magento 2 module developer, if I want to replace code like the following in Magento 1

Mage::log($message, Zend_Log::DEBUG, "my-log-file.log");

What's the bare minimum I need to do?

Alan Storm

Posted 2015-12-03T04:06:07.193

Reputation: 26 515

Answers

81

protected $logger;
public function __construct(\Psr\Log\LoggerInterface $logger)
{
    $this->logger = $logger;
}

You use debug, exception, system for PSR Logger for example:

$this->logger->info($message);
$this->logger->debug($message);

Pratik

Posted 2015-12-03T04:06:07.193

Reputation: 1 656

8+1 Thank you, that's a useful interface/class/type to know about -- but it's not clear from your answer where the information will be logged and how (if possible) to change that location. – Alan Storm – 2015-12-03T07:21:20.677

You check Manager.php for following class Magento\Framework\Event and add this line $this->logger->debug($eventName); than after refresh page and check debug.txt file you get all evant name for specific page. – Pratik – 2015-12-03T07:30:59.110

2Technically, this is the 'correct' way to instantiate a logger in your own custom classes - particularly if you intend to keep it around rather than just some quick debug. However, there are several core classes - particularly Block classes - which automatically instantiate and store a _logger property. If you extend one of these core classes then there's no need to repeat the logic.

Other answers dig into creating handlers to define your own log file, but the default logs are always /var/log/system.log or /var/log/debug.log. I believe the specific logging function determines which is used. – Jeremy Rimpo – 2017-07-06T16:11:36.397

4For me, the "debug" level started to work only when I enabled "Log to file" in Configuration > Advanced > Developer > Debug. Using 2.2 – Omer Sabic – 2017-11-15T09:45:14.053

57

In magento2, You can also write to the logs using the Zend library like below :

$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/test.log');
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);
$logger->info('Your text message');

Edited

You can also print PHP objects and arrays like below :

$logger->info(print_r($yourArray, true));

Manashvi Birla

Posted 2015-12-03T04:06:07.193

Reputation: 5 083

6+1 Useful -- do you know if Zend logger will automatically format PHP arrays/objects etc? – Alan Storm – 2015-12-03T07:21:54.093

1@AlanStorm - Yes you can, check my updated answer.! – Manashvi Birla – 2015-12-03T11:05:34.803

@Manashvibirla I think what Alan Storm wanted to know is will the method automatically format when called as: $logger->info($someArrayOrObject); – MagePsycho – 2015-12-09T10:36:27.217

2@Manashvibirla : PHP objects are not printing... – zed Blackbeard – 2016-03-01T06:43:23.210

@Manashvibirla If you have to do log in windows then you have to change /(slash) like \var\log\test.log. Any way still I am using this logging method for my development purpose. thanks :) – Keyur Shah – 2016-11-16T09:11:46.633

1@KeyurShah The solution was provided by keeping ubuntu in mind, as i was using ubuntu.! thanks for the feedback – Manashvi Birla – 2016-11-16T09:43:58.723

1great for temp debug – Minesh Patel – 2017-02-02T12:58:39.787

2Several of these answers have their place and use. Obviously this solution requires almost as much code as using DI to instantiate the standard logger -- but it's a simple in-place drop-in which lets you set your own log file.

Sometimes it's rather annoying to search through the standard log files - which tend to get cluttered - to find your own logs. So this is a nice 'quick' solution for that. – Jeremy Rimpo – 2017-07-06T16:17:11.263

1i was looking for something easy like this.. works great, thanks! – Kalpesh – 2017-07-08T18:19:03.700

36

\Magento\Framework\App\ObjectManager::getInstance()
    ->get(\Psr\Log\LoggerInterface::class)->debug('message');

Mage2.PRO

Posted 2015-12-03T04:06:07.193

Reputation: 3 078

5+1 Thank you, that's a useful interface/class/type to know about -- but it's not clear from your answer where the information will be logged and how (if possible) to change that location. – Alan Storm – 2015-12-03T07:21:25.767

That's the correct answer. – medina – 2016-05-23T05:14:10.077

4I would not advice to use the ObjectManager directly. Use DI instead – 7ochem – 2016-11-04T12:18:48.547

7While I agree with @7ochem if you're creating a permanent logging function, it can be necessary to inject temporary logging into core (or third party) classes from time to time to debug issues. Going through the arduous process of adding a Logger class to the constructor is pointlessly over-complicated in these cases. For a simple, single-line debug function this is probably the best solution. However, you will have to deal with searching through the default log files to find your own debug output. – Jeremy Rimpo – 2017-07-06T16:02:43.363

Also keep in mind that there are several core classes - particularly Block classes - which have a _logger property you can access without instantiating a new copy. – Jeremy Rimpo – 2017-07-06T16:06:10.020

+1 As a Dev, I understand that I'm not to use the Obj Manager directly, but this piece of code also teaches me to get the resolved class from the interface – NathanielR – 2017-08-05T14:51:01.230

+1 that is perfect replacement for Mage::log() – jaydip sinh Parmar – 2017-09-12T10:48:56.547

10

If you want to use default logger but custom file for logging (or other custom logic) you need to use custom logger handler:

class Logger extends Magento\Framework\Logger\Handler\Base
{
  /**
   * @var string
   */
  protected $fileName = '/var/log/my-log-file.log';

  /**
   * @var int
   */
  protected $loggerType = MonologLogger::DEBUG;
}

Then add it as handler somewhere within your code:

protected function addCustomLogHandler()
{
    $logger = Data::getCustomLogger();
    if(isset($this->_logger)){
        $this->_logger->pushHandler($logger);
    }
}

A step back in convenience IMO

Petar Dzhambazov

Posted 2015-12-03T04:06:07.193

Reputation: 1 198

+1 Useful information, thank you! However, it's not clear how you use this logger context with the PSR-3 autoloader interface - i.e. if you're logging with $this->logger->info($message, $level); -- how do you say "use my context"? – Alan Storm – 2015-12-03T17:39:36.893

2Well the thing is that all handlers that are available to Monolog are looped and first that can handle the level of record (DEBUG, INFO etc.) is used. So the only way I see to be absolutely sure that your handler is used, is to push it before you need it, so its at top of the stack and comes first in the loop. Another way would be to just SET it as handler, removing all others, but that won't be very friendly thing to do. – Petar Dzhambazov – 2015-12-03T18:42:34.417

If you try to introduce additional handlers in the 2.0.0 GA, or work with specifying the handlers in di.xml you may want to be aware of this issue https://github.com/magento/magento2/issues/2529

I ran into this issue trying to get a custom logger to have a custom log file handle, and a custom handler that writes some entries to a database table.

– mttjohnson – 2015-12-07T03:47:18.863

7

Temporary print log with new file

$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/logfile.log');
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);
$logger->info('Simple Text Log'); // Simple Text Log
$logger->info('Array Log'.print_r($myArrayVar, true)); // Array Log

Factory Method

You need to inject \Psr\Log\LoggerInterface class into constructor to call logger object

protected $_logger;
public function __construct(
...
\Psr\Log\LoggerInterface $logger
...
) {
    $this->_logger = $logger;
}

public function logExample() {

    //To print string Output in debug.log
    $this->_logger->addDebug('Your Text Or Variables'); 

    // To print array Output in system.log
    $this->_logger->log('600', print_r($yourArray, true));

}

Or you directly use this code in phtml file:

To print string Output in debug.log

\Magento\Framework\App\ObjectManager::getInstance()
   ->get('Psr\Log\LoggerInterface')->debug('Your Message');

To print array Output in system.log

$myArray = array('test1'=>'123', 'test2'=>'123', 'test3'=>'123');
$level = '100'; // use one of: 100, 200, 250, 300, 400, 500, 550, 600
\Magento\Framework\App\ObjectManager::getInstance()
    ->get('Psr\Log\LoggerInterface')
    ->log($level, print_r($myArray, true));

Prince Patel

Posted 2015-12-03T04:06:07.193

Reputation: 8 970

5

No, there is no direct equivalent. It's a bit complicated now.

See: Logging to a custom file in Magento 2

Ryan Hoerr

Posted 2015-12-03T04:06:07.193

Reputation: 6 953

1+1, thank you! However -- other answers make it sound like there may be a single logger, and the extend/create a handle approach is no longer necessary. Do you know if that's true? – Alan Storm – 2015-12-03T07:22:45.740

2

If you are looking for elegant custom log handler, I recommend you to create a helper (which able to use anywhere in your project by injecting it in your constructors).

Inspired from answer of Petar Dzhambazov and halk, ladies and gentlement I introduced you a better and shorter way instead of duplicated custom log code all the time.

Stackoverflow\Core\Helper\Data.php

<?php
/**
 * Copyright © 2016 Toan Nguyen <https://nntoan.github.io>. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Stackoverflow\Core\Helper;

use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\ObjectManagerInterface;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;

/**
 * Stackoverflow Core Helper
 *
 * @package Stackoverflow\Core\Helper
 * @author  Toan Nguyen <https://github.com/nntoan>
 */
class Data extends AbstractHelper
{
    /**
     * @var \Magento\Framework\ObjectManagerInterface
     */
    protected $objectManager;
    /**
     * @var \Psr\Log\LoggerInterface
     */
    protected $defaultLogger;
    /**
     * @var string
     */
    protected $channelName;
    /**
     * @var string
     */
    protected $logFile;

    /**
     * Data constructor.
     *
     * @param Context                $context       Context
     * @param ObjectManagerInterface $objectManager Object manager
     */
    public function __construct(
        Context $context,
        ObjectManagerInterface $objectManager
    ) {
        parent::__construct($context);
        $this->objectManager = $objectManager;
        $this->defaultLogger = $context->getLogger();
        $this->channelName = 'stackoverflow-default-channel';
        $this->logFile = BP . '/var/log/stackoverflow.log';
    }

    /**
     * Get default/custom logger instance
     *
     * @param string $type
     * @param string|null $channelName
     * @param string|null $logFile
     *
     * @return \Psr\Log\LoggerInterface
     */
    public function getLogger($type = 'default', $channelName = null, $logFile = null)
    {
        switch ($type) {
            case 'custom':
                $logger = $this->getCustomLogger($channelName, $logFile);
                break;
            case 'default':
            default:
                $logger = $this->getDefaultLogger();
                break;
        }

        return $logger;
    }

    /**
     * Return default logger instance
     *
     * @return \Psr\Log\LoggerInterface
     */
    private function getDefaultLogger()
    {
        return $this->defaultLogger;
    }

    /**
     * Create new logger instance with custom channel name
     * and log file name
     *
     * @param string|null $channelName
     * @param string|null $logFile
     *
     * @return \Monolog\Logger
     */
    private function getCustomLogger($channelName = null, $logFile = null)
    {
        if (!empty($channelName) && !empty($logFile)) {
            $this->channelName = $channelName;
            $this->logFile = BP . $logFile;
        }

        /** @var \Monolog\Logger $logger */
        $logger = $this->objectManager->create(Logger::class, [$this->channelName]);

        /** @var \Monolog\Handler\StreamHandler $streamHandler */
        $streamHandler = $this->objectManager->create(StreamHandler::class, [$this->logFile]);
        $logger->pushHandler($streamHandler);

        return $logger;
    }
}

USAGE

Vendor\Something\Model\DonaldTrump.php

<?php
/**
 * Copyright © 2016 Toan Nguyen <https://nntoan.github.io>. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Vendor\Something\Model;

use Stackoverflow\Core\Helper\Data as SoHelper;

/**
 * DonaldTrump business logic file
 *
 * @package Vendor\Something\Model
 * @author  Toan Nguyen <https://github.com/nntoan>
 */
class DonaldTrump
{
    /**
     * @var \Psr\Log\LoggerInterface
     */
    private $logger;

    const LOG_CHANNEL_NAME = 'donald-trump';
    const LOG_FILEPATH = '/var/log/donald-trump/make-america-great-again.log';

    /**
     * DonaldTrump constructor.
     *
     * @param SoHelper    $soHelper
     */
    public function __construct(
        SoHelper $soHelper
    ) {
        $this->logger = $soHelper->getLogger('custom', self::LOG_CHANNEL_NAME, self::LOG_FILEPATH);
    }

    /**
     * Test my custom log
     *
     * @return void
     */
    public function test()
    {
        $this->logger->error('MAKE AMERICA GREAT AGAIN !?');
    }
}

As you see, whenever test() method called, the string MAKE AMERICA GREAT AGAIN !? will be log as error into a custom file (create folder automatically if doesn't exist) in <magento_dir>/var/log/donald-trump/make-america-great-again.log

In other model, e.g HillaryClinton you can create simple declare new custom log and so on...

Hope this helps ;)

Toan Nguyen

Posted 2015-12-03T04:06:07.193

Reputation: 1 278

Is this code implementing PSI? (Political Statements Injection) :P – 7ochem – 2017-05-12T14:56:42.203

@7ochem Oh yes, it is :v – Toan Nguyen – 2017-05-14T06:23:32.147

2

Include psr logger class in your file using use and then call addDebug() method. This will print log message in var/log/debug.log file

use Psr\Log\LoggerInterface;

class demo {
  function demo()
  {
    //EDIT: Using debug instead of addDebug for PSR compatiblity
    $this->_objectManager->get('Psr\Log\LoggerInterface')->debug("your message goes here");
  }

}

chirag dodia

Posted 2015-12-03T04:06:07.193

Reputation: 587

2you shouldn't ujse addDebug as that's not psr logger compatible. use just debug instead. – Maciej Paprocki – 2016-11-09T15:56:39.440

1

If you need it within your single class with custom log file:

public function __construct(\Psr\Log\LoggerInterface $logger, \Magento\Framework\App\Filesystem\DirectoryList $dir) 
{
    $this->logger = $logger;
    $this->dir = $dir;

    $this->logger->pushHandler(new \Monolog\Handler\StreamHandler($this->dir->getRoot().'/var/log/custom.log'));
}

mshakeel

Posted 2015-12-03T04:06:07.193

Reputation: 271

1

There is one update for logger in 2.2. You can enable logger for production mode by run SQL:

 "INSERT INTO core_config_data (scope, scope_id, path, value) VALUES ('default', '0', 'dev/debug/debug_logging', '1');"

Then you can use \Psr\Log\LoggerInterface for print log just like above answers:

protected $logger;

public function __construct(
  \Psr\Log\LoggerInterface $logger
) {
    $this->logger = $logger;
  }

public function yourFunction() {
    $data = ["test" => "testing"];
    $this->logger->debug(var_export($data, true));
}

Yogesh Karodiya

Posted 2015-12-03T04:06:07.193

Reputation: 1 684

0

  1. Inject $logger class in constructor \Psr\Log\LoggerInterface $logger
    This is achieved by passing $logger as argument.

  2. Initialize $logger in constructor

    $this->logger = $logger
    
  3. In function within the class you want to log use the below line

    $this->logger->debug($message);
    $this->logger->log($level, $message);
    

oscprofessionals

Posted 2015-12-03T04:06:07.193

Reputation: 349

0

Place PSR logger code in your constructor:

protected $logger;
public function __construct(\Psr\Log\LoggerInterface $logger)
{
    $this->logger = $logger;
}

then you can use in your function like:

$this->logger->info($message);

user49289

Posted 2015-12-03T04:06:07.193

Reputation: