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.
18 Answers 18
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');
}
}
-
3I 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– Alan Kent2015年07月29日 21:16:05 +00:00Commented Jul 29, 2015 at 21:16
-
1Manoj, 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 allhalk– halk2015年07月30日 11:54:15 +00:00Commented Jul 30, 2015 at 11:54
-
3In 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 usebarbazul– barbazul2015年12月10日 16:34:21 +00:00Commented 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– scrowler2015年12月15日 21:06:57 +00:00Commented Dec 15, 2015 at 21:06
-
3For me it's always taking the /var/log/system.log, any idea why?MagePsycho– MagePsycho2016年03月25日 20:46:48 +00:00Commented Mar 25, 2016 at 20:46
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));
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());
-
@Pandurang for me not working, any help thanks paste.ofcode.org/yr83DQAUMaZmGuAHTXDdh2 line 30 - 33zus– zus2020年03月14日 09:30:26 +00:00Commented Mar 14, 2020 at 9:30
-
3from Magento 2.4 it will be \Laminas\Log\Writer\Stream and \Laminas\Log\LoggerArshad M– Arshad M2020年12月30日 19:30:34 +00:00Commented 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– Simon2021年07月29日 06:27:38 +00:00Commented Jul 29, 2021 at 6:27 -
2This is not working in 2.4.3 laminas and zend framework bothSagar Dobariya– Sagar Dobariya2021年08月16日 09:58:10 +00:00Commented Aug 16, 2021 at 9:58
-
this is removed in Magento 2.4.3HaFiz Umer– HaFiz Umer2021年08月31日 08:24:46 +00:00Commented Aug 31, 2021 at 8:24
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:
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 = $loggerafter 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; } }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.
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));
-
use
Leminasinstead ofzendinMagento 2.4.Ali Raza– Ali Raza2021年01月13日 07:41:50 +00:00Commented Jan 13, 2021 at 7:41 -
Duplicate of magento.stackexchange.com/a/196426/142.Simon– Simon2021年07月29日 06:26:31 +00:00Commented Jul 29, 2021 at 6:26
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'));
}
-
pushHandler is not exposed method on the interface and the implementation does not seem to work...George– George2019年01月28日 13:00:49 +00:00Commented Jan 28, 2019 at 13:00
-
Your Magento version?mshakeel– mshakeel2019年01月28日 13:05:35 +00:00Commented Jan 28, 2019 at 13:05
-
-
I will try it on CE 2.2.0 and get back to you. I have used it on 2.1mshakeel– mshakeel2019年01月29日 07:22:15 +00:00Commented Jan 29, 2019 at 7:22
-
2This 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– PromInc2020年07月02日 18:23:50 +00:00Commented Jul 2, 2020 at 18:23
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
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
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));
$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 **/
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.
-
Can i get help thanks : paste.ofcode.org/yr83DQAUMaZmGuAHTXDdh2 line from 30- 33 log not created at var/log/custom.log @Jdprasad Vzus– zus2020年03月14日 09:16:35 +00:00Commented Mar 14, 2020 at 9:16
-
1I have updated your code please check and let me know.Jdprasad V– Jdprasad V2020年03月16日 10:26:43 +00:00Commented Mar 16, 2020 at 10:26
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]
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
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)]);
}
}
$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.
$writer = new \Zend_Log_Writer_Stream(BP . '/var/log/custom.log');
$logger = new \Zend_Log();
$logger->addWriter($writer);
$logger->info('text message');
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
I always use this simple one liner:
file_put_contents('var/log/goivvy_logger.txt',date('Y-m-d h:i:s')."\n",(FILE_APPEND | LOCK_EX));