How to create custom Log file in Magento 2?
Pergunta
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.
Solução
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="../../../../../lib/internal/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');
}
}
Outras dicas
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());
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');
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 seperate 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.
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'));
}
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');
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
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]
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 custom.log file, check this code, you definitely 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);
If above solution not worked try below one. directly we inject psr logs in 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.
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
$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**