89

In Magento 1, it was common to segment logs into different files (to separate logs for payment methods, etc.). That's as easy as changing the $file parameter of Mage::log.

Magento 2 has changed to use Monolog.

It appears that Monolog (or Magento2's implementation of it) segments all logs for the entire framework to handlers by severity. There are a few handlers that write to file:

\Magento\Framework\Logger\Handler\Debug, \Magento\Framework\Logger\Handler\Exception, \Magento\Framework\Logger\Handler\System

Logging to respective files in var/log as in Magento 1.

I could add a handler for a particular severity (IE, write notices to var/log/notice.log). Extend \Magento\Framework\Logger\Handler\Base, and register the handler in di.xml.

This article roughly describes that process: http://semaphoresoftware.kinja.com/how-to-create-a-custom-log-in-magento-2-1704130912

But how do I go about writing all logs (not just one severity) for one class (not all of Magento) to my file of choice?

It looks like I'll have to create my own version of Magento\Framework\Logger\Monolog, but then how does everything fit together for that to actually work?

If this is a big no-no in Magento 2, then what is the alternative? I want something to separate the logs for this extension for the purpose of debugging it when necessary on client sites. Having that info written to system.log, exception.log, etc. and jumbled with the logs of every other module is not practical.

17 Answers 17

149

You do not need to customize or try to extend Magento2's logging. As you said it's using Monolog with only slight customization. It is sufficient to write your own logger extending Monolog with very little effort.

Assuming your module is in YourNamespace/YourModule:

1) Write Logger class in Logger/Logger.php:

<?php
namespace YourNamespace\YourModule\Logger;

class Logger extends \Monolog\Logger
{
}

2) Write Handler class in Logger/Handler.php:

<?php
namespace YourNamespace\YourModule\Logger;

use Monolog\Logger;

class Handler extends \Magento\Framework\Logger\Handler\Base
{
    /**
     * Logging level
     * @var int
     */
    protected $loggerType = Logger::INFO;

    /**
     * File name
     * @var string
     */
    protected $fileName = '/var/log/myfilename.log';
}

Note: This is the only step that uses the Magento code. \Magento\Framework\Logger\Handler\Base extends Monolog's StreamHandler and e.g. prepends the $fileName attribute with the Magento base path.

3) Register Logger in Dependency Injection etc/di.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="YourNamespace\YourModule\Logger\Handler">
        <arguments>
            <argument name="filesystem" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument>
        </arguments>
    </type>
    <type name="YourNamespace\YourModule\Logger\Logger">
        <arguments>
            <argument name="name" xsi:type="string">myLoggerName</argument>
            <argument name="handlers"  xsi:type="array">
                <item name="system" xsi:type="object">YourNamespace\YourModule\Logger\Handler</item>
            </argument>
        </arguments>
    </type>
</config>

Note: This is not strictly required but allows the DI to pass specific arguments to the constructor. If you do not do this step, then you need to adjust the constructor to set the handler.

4) Use the logger in your Magento classes:

This is done by Dependency Injection. Below you will find a dummy class which only writes a log entry:

<?php
namespace YourNamespace\YourModule\Model;

class MyModel
{
    /**
     * Logging instance
     * @var \YourNamespace\YourModule\Logger\Logger
     */
    protected $_logger;

    /**
     * Constructor
     * @param \YourNamespace\YourModule\Logger\Logger $logger
     */
    public function __construct(
        \YourNamespace\YourModule\Logger\Logger $logger
    ) {
        $this->_logger = $logger;
    }

    public function doSomething()
    {
        $this->_logger->info('I did something');
    }
}
13
  • 3
    I was asking something similar to one of the architects the other day, so thanks for this example! I was wondering about adding support based on class name so DI framework could inject the "right" logger to different classes, and having switches in the Admin to turn flags on/off without code changes like this. How useful would this sort of functionality be to people?
    – Alan Kent
    Commented Jul 29, 2015 at 21:16
  • 1
    Manoj, if the template you are refering to has a block class with logger then you can write a public method which then passes the message to the logger. Your example wont work since _logger is protected if it exists at all
    – halk
    Commented Jul 30, 2015 at 11:54
  • 3
    In my opinion, the current approach is a step back from what M1 had. Logging should also be a developer tool, it is not only meant for monitoring a live application. I can see how an optional multi-purpose simplified library could be created for being used in development overriding the current implementation and then replaced for production use
    – barbazul
    Commented Dec 10, 2015 at 16:34
  • 2
    @AlanKent I agree with barbazul here - the ability to easily log to whichever file you wanted to, specifying the level quickly in M1 was great. This is not as flexible (dynamically) which is a shame. Would be good to have the filename as a parameter to the default logger calls. Thanks for the answer halk!
    – scrowler
    Commented Dec 15, 2015 at 21:06
  • 3
    For me it's always taking the /var/log/system.log, any idea why?
    – MagePsycho
    Commented Mar 25, 2016 at 20:46
44

the custom log file created using below code

for Magento 2.4.2 before version use this

$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/custom.log');
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);
$logger->info('Custom message'); 
$logger->info(print_r($object->getData(), true));

for Magento 2.4.2 or after version use this

$writer = new \Laminas\Log\Writer\Stream(BP . '/var/log/custom.log');
$logger = new  \Laminas\Log\Logger();
$logger->addWriter($writer);
$logger->info('text message');
$logger->info(print_r($object->getData(), true));

for Magento 2.4.3 version use this

$writer = new \Zend_Log_Writer_Stream(BP . '/var/log/custom.log');
$logger = new \Zend_Log();
$logger->addWriter($writer);
$logger->info('text message');
$logger->info(print_r($object->getData(), true));
0
41

We can log data in file like this.

$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/templog.log');
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);

$logger->info("Info". $product->getSku() . "----- Id  ". $product->getId() );
$logger->info("preorder qty ". $product->getPreorderQty());
5
  • @Pandurang for me not working, any help thanks paste.ofcode.org/yr83DQAUMaZmGuAHTXDdh2 line 30 - 33
    – zus
    Commented Mar 14, 2020 at 9:30
  • 3
    from Magento 2.4 it will be \Laminas\Log\Writer\Stream and \Laminas\Log\Logger
    – Arshad M
    Commented Dec 30, 2020 at 19:30
  • Even though this is possible, this is not the Magento-way of doing it. E.g., you should not use new, but the object manager.
    – Simon
    Commented Jul 29, 2021 at 6:27
  • 2
    This is not working in 2.4.3 laminas and zend framework both Commented Aug 16, 2021 at 9:58
  • this is removed in Magento 2.4.3
    – HaFiz Umer
    Commented Aug 31, 2021 at 8:24
21

In addition to Halk's and Pradeep Kumar's answers: If indeed your only concern is to log to a different file, there is a slightly easier way, especially if you want to incorporate that to multiple modules or if you want different log files within your module. With this method, you don't have to create custom handlers.

Assuming your module is in MyNamespace/MyModule and the class which you want to log to a custom file, is called MyClass. If the constructor of the class already injects \Psr\Log\LoggerInterface skip to step 2). Otherwise you need to inject it in the constructor:

  1. Inject LoggerInterface in your class MyClass.php:

    <?php
    
     namespace MyNamespace\MyModule;
    
     use Psr\Log\LoggerInterface;
    
     class MyClass
     {
         /**
          * @var \Psr\Log\LoggerInterface
          */
         protected $logger;
    
         public function __construct(
             LoggerInterface $logger
         ) {
             $this->logger = $logger;
         }
     }
    

    If you extend a class which already includes a logger (like \Magento\Framework\App\Helper\AbstractHelper) you might as well overwrite that member (usually $_logger) instead of using a separate one. Simply add $this->_logger = $logger after the parent constructor directive.

    <?php
    
    namespace MyNamespace\MyModule;
    
    use Magento\Framework\App\Helper\Context;
    use Psr\Log\LoggerInterface;
    
    class MyClass extends \Magento\Framework\App\Helper\AbstractHelper
    {
        public function __construct(
            Context $context,
            LoggerInterface $logger
        ) {
            parent::__construct(
                $context
            );
    
            $this->_logger = $logger;
        }
    }
    
  2. Configure logger via dependency injection etc/di.xml:

    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
        <virtualType name="MyNamespace\MyModule\Logger\Handler" type="Magento\Framework\Logger\Handler\Base">
            <arguments>
                <argument name="filesystem" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument>
                <argument name="fileName" xsi:type="string">/var/log/mymodule.log</argument>
            </arguments>
        </virtualType>
        <virtualType name="MyNamespace\MyModule\Logger\Logger" type="Magento\Framework\Logger\Monolog">
            <arguments>
                <argument name="name" xsi:type="string">MyModule Logger</argument>
                <argument name="handlers" xsi:type="array">
                    <item name="system" xsi:type="object">MyNamespace\MyModule\Logger\Handler</item>
                </argument>
            </arguments>
        </virtualType>
    
        <type name="MyNamespace\MyModule\MyClass">
            <arguments>
                <argument name="logger" xsi:type="object">MyNamespace\MyModule\Logger\Logger</argument>
            </arguments>
        </type>
    </config>
    

    This will log everything to /var/log/mymodule.log.

    If you need to log to a different file for a different class, you can simply create another virtual logger with another virtual handler and inject it into that class.

19

The simplest possible way:

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

If you need it within your single class only:

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'));
}
5
  • pushHandler is not exposed method on the interface and the implementation does not seem to work...
    – George
    Commented Jan 28, 2019 at 13:00
  • Your Magento version?
    – mshakeel
    Commented Jan 28, 2019 at 13:05
  • Magento CE 2.2.0
    – George
    Commented Jan 28, 2019 at 13:10
  • I will try it on CE 2.2.0 and get back to you. I have used it on 2.1
    – mshakeel
    Commented Jan 29, 2019 at 7:22
  • 2
    This solution worked for me in Open Source 2.3.4-p2. I love this solution for my API endpoint. I needed to be able to log the status changes the endpoint did. This is a lightweight, easy to understand solution that keeps all of my logging logic in one file.
    – PromInc
    Commented Jul 2, 2020 at 18:23
7

From Magento 2.3.5-p1, the code should be:

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

Zend is now Laminas

0
7

If you are using logger for only testing purpose, you can use direct code either use proper magento standard

With Magento 2.4.4 & plus versions use below code:

$writer = new \Zend_Log_Writer_Stream(BP . '/var/log/testing.log');
$logger = new \Zend_Log();
$logger->addWriter($writer);
$logger->info('Test the log');

For Magento 2.4.2 & lower versions:

$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/custom.log');
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);
$logger->info('Custom message'); 
$logger->info(print_r($object->getData(), true));

NOTE: Recommended to use Magento standard this is only for testing purpose

5

Try this:

$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/yyr.log');
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);
$logger->info('Your text message');
$logger->info(print_r($object->getData(), true));
2
$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/custom.log');
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);
$logger->info('Custom text message');  /** To Print Only String **/ 
$logger->info(print_r($object->getData(), true));  /** To Print Object Data **/
0
2

I tried this below logger object code in a third-party module where I want to get log info there I placed and get them into the custom.log file, check this code, you get the logs into your custom log file.

$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/custom.log');
$logger = new \Zend\Log\Logger();
$logger->addWriter($writer);
$logger->info('Your log details: ' .$variable);
$logger->info(print_r($object->getData(), true));

If the above solution not worked try the below one. directly we inject psr logs into our custom code.

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

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

If you still finding any issues please let me know. Also, I was updated your code.

2
  • Can i get help thanks : paste.ofcode.org/yr83DQAUMaZmGuAHTXDdh2 line from 30- 33 log not created at var/log/custom.log @Jdprasad V
    – zus
    Commented Mar 14, 2020 at 9:16
  • 1
    I have updated your code please check and let me know.
    – Jdprasad V
    Commented Mar 16, 2020 at 10:26
1

Try "praxigento/mage2_ext_logging" module. This module adds "Monolog Cascade" support to Magento 2. "Monolog Cascade" allows you to configure you logging output with single configuration file. You can print out your logs to different files, databases, send email alerts and etc without modifications of your own code.

This is a sample of the configuration file ('var/log/logging.yaml' by default):

disable_existing_loggers: true
formatters:
    dashed:
        class: Monolog\Formatter\LineFormatter
        format: "%datetime%-%channel%.%level_name% - %message%\n"
handlers:
    debug:
        class: Monolog\Handler\StreamHandler
        level: DEBUG
        formatter: dashed
        stream: /.../var/log/cascade_debug.log
    system:
        class: Monolog\Handler\StreamHandler
        level: INFO
        formatter: dashed
        stream: /.../var/log/cascade_system.log
    exception:
        class: Monolog\Handler\StreamHandler
        level: EMERGENCY
        formatter: dashed
        stream: /.../log/cascade_exception.log
processors:
    web_processor:
        class: Monolog\Processor\WebProcessor
loggers:
    main:
        handlers: [debug, system, exception]
        processors: [web_processor]
0
0

If there is no logic change and only need to change a custom log file name then no need to create custom logger class also just follow below steps

1. in di.xml

 <type name="Magento\Framework\Logger\Monolog">
        <arguments>
            <argument name="name" xsi:type="string">test</argument>
            <argument name="handlers"  xsi:type="array">
                <item name="test" xsi:type="object">NAME_SPACE\Test\Model\Logger\Handler\Debug</item>
            </argument>
        </arguments>
    </type>

2. Handler

<?php
/**
 * Copyright © 2017 Alshaya, LLC. All rights reserved.
 * See LICENSE.txt for license details.
 *
 */
namespace NAME_SPACE\Test\Model\Logger\Handler;

use Magento\Framework\Logger\Handler\Base;

/**
 * Log handler for reports
 */
class Debug extends Base
{
    /**
     * @var string
     */
    protected $fileName = '/var/log/test.log';
}

where ever you needed to log the data you need to call default PSR log
that is

<?php
/**
 *
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace NAME_SPACE\Test\Controller\Index;

use Psr\Log\LoggerInterface;
class Index extends \Magento\Framework\App\Action\Action
{


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

    /**
     * Show Contact Us page
     *
     * @return void
     */


    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        LoggerInterface $logger
    ) {
        parent::__construct($context);
        $this->logger = $logger;
    }


    public function execute()
    {
        $this->logger->critical((string) 'Test');
        $this->_view->loadLayout();
        $this->_view->renderLayout();
    }
}

so above example will log all debug data to test.log if you needed to change system also you can add below line in di.xml

0

I found one form to use this in one line:

<?php
namespace Vendor\Module\Helper;

class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
    public function __construct(
        \Magento\Framework\App\Helper\Context $context
    ){
        parent::__construct($context);
        $this->_logger = $this->_logger->withName("myCustomName")->setHandlers(['system' => \Magento\Framework\App\ObjectManager::getInstance()->create('\Magento\Framework\Logger\Handler\Base', ['fileName' => '/var/log/myCustomLog.log'])->setLevel(\Monolog\Logger\Logger::INFO)]);
    }
}
0
  $writer = new \Laminas\Log\Writer\Stream(BP . '/var/log/test.log');
  $logger = new  \Laminas\Log\Logger();
  $logger->addWriter($writer);
  $logger->info('text message');

for 2.4.2 above code can be used for custom log.

0
$writer = new \Zend_Log_Writer_Stream(BP . '/var/log/custom.log');
$logger = new \Zend_Log();
$logger->addWriter($writer);
$logger->info('text message');
0

You can use several formats.

Default logger

1: In Magento default format

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));

}

Custom logger

2: This will work with the older version of Magento 2.4.3:

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

3: This will work with the newest Magento 2.4.3+. it is tested on Magento 2.4.4, 2.4.6 version.

$writer = new \Zend_Log_Writer_Stream(BP . '/var/log/custom.log');
$logger = new \Zend_Log();
$logger->addWriter($writer);
$logger->info('Your text message');
$logger->info(print_r($array, true)); //to log the array

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.