When creating a magento module logging in often required for multiple reasons. Logging can be done in multiple ways, specially if you want o logging to a custom file rather than to the default magento files.
Let’s assume we are creating a sample module to display some content in the frontend. As always, to create a module we will need a registration.php
file and an etc/module.xml
.

// File: app/code/Webguru/SampleModule/registration.php <?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Webguru_SampleModule', __DIR__ );
// File: app/code/Webguru/SampleModule/etc/module.xml <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Webguru_SampleModule" setup_version="1.0.0" /> </config>
Next, for the purpose of this article, let’s create a Block/Sample.php
file inside our module. Because this is a block class to be used on a template it needs to extend Magento\Framework\View\Element\Template. Also, let’s add a constructor because we will need it to setup logging in our module.
// File: app/code/Webguru/SampleModule/Block/Sample.php <?php namespace Webguru\SampleModule\Block; use Magento\Framework\View\Element\Template; class Sample extends Template { public function __construct(Template\Context $context, array $data = []) { parent::__construct($context, $data); } }
To setup logging we will need to pass to our class the logging service using dependency injection. Dependency injection is a technique in which an object receives other objects that it depends on. This is the recommend technique by magento whenever a service is needed to be used in a specific module. This is done by adding a new argument to the class constructor and creating an instance variable to store that object for latter use on our methods. The service we want to use is define in class Psr/Log/LoggerInterface
, so we will create a new argument of this type.
// File: app/code/Webguru/SampleModule/Block/Sample.php <?php namespace Webguru\SampleModule\Block; use Magento\Framework\View\Element\Template; use Psr\Log\LoggerInterface; class Sample extends Template { private LoggerInterface $logger; public function __construct( Template\Context $context, LoggerInterface $logger, array $data = []) { parent::__construct($context, $data); $this->logger = $logger; } }
Now, let’s create a new method test
and write a debug message to the default magento debug.log
file located under var/log
.
// File: app/code/Webguru/SampleModule/Block/Sample.php ... public function test() { $this->logger->debug("This is a debug message"); } }
Custom log file
Now, what if we want to log to a custom file? There are 3 different ways to achieve this goal using dependency injection.
If you look at the global dependency configuration file in magento that is located under app/etc/di.xml (check line #10 below) you can see that class Psr\Log\LoggerInterface
is actually being implemented with class Magento\Framework\Logger\Monolog
and in line #21 the arguments for this class are being configured: argument name
will get value main
and the argument handlers
will receive an array with the class handlers for system
, debug
and syslog
logs.
<?xml version="1.0"?> <!-- /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="DateTimeInterface" type="DateTime" /> <preference for="Psr\Log\LoggerInterface" type="Magento\Framework\Logger\LoggerProxy" /> <preference for="Magento\Framework\EntityManager\EntityMetadataInterface" type="Magento\Framework\EntityManager\EntityMetadata" /> <preference for="Magento\Framework\EntityManager\HydratorInterface" type="Magento\Framework\EntityManager\Hydrator" /> <preference for="Magento\Framework\View\Template\Html\MinifierInterface" type="Magento\Framework\View\Template\Html\Minifier" /> <preference for="Magento\Framework\Model\Entity\ScopeInterface" type="Magento\Framework\Model\Entity\Scope" /> <preference for="Magento\Framework\ObjectManager\FactoryInterface" type="Magento\Framework\ObjectManager\Factory\Dynamic\Developer" /> <preference for="Magento\Framework\Search\Request\Aggregation\StatusInterface" type="Magento\Framework\Search\Request\Aggregation\Status" /> <preference for="Magento\Framework\Search\Adapter\Aggregation\AggregationResolverInterface" type="Magento\Framework\Search\Adapter\Aggregation\AggregationResolver"/> <preference for="Magento\Framework\App\RequestInterface" type="Magento\Framework\App\Request\Http" /> <preference for="Magento\Framework\App\PlainTextRequestInterface" type="Magento\Framework\App\Request\Http" /> ... <type name="Magento\Framework\Logger\Monolog"> <arguments> <argument name="name" xsi:type="string">main</argument> <argument name="handlers" xsi:type="array"> <item name="system" xsi:type="object">Magento\Framework\Logger\Handler\System</item> <item name="debug" xsi:type="object">Magento\Framework\Logger\Handler\Debug</item> <item name="syslog" xsi:type="object">Magento\Framework\Logger\Handler\Syslog</item> </argument> </arguments> </type> ... </config>
Class Magento\Framework\Logger\Handler\Debug
definition is very simple and short. It inherits all functionality from class Magento\Framework\Logger\Handler\Base
and it only overrides 2 instance variables: filename
and loggerType
. As you can see filename
is the variable where the output filename is defined, which means that if we override this value we can make the logs be written to a different file.
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Framework\Logger\Handler; use Monolog\Logger; class Debug extends Base { /** * @var string */ protected $fileName = '/var/log/debug.log'; /** * @var int */ protected $loggerType = Logger::DEBUG; }
Create a custom Log Handler
To override this value we can create a custom debug handler and tell magento to use it instead of the default debug handle defined in Magento\Framework\Logger\Handler\Debug
. Let’s call this new class DebugHandler
and place it inside the Model
folder in our module. In this class let’s initialize filename
with our custom debug filename (custom_debug.log
, for example)
// File: app/code/Webguru/SampleModule/Model/DebugHandler.php namespace Webguru\SampleModule\Model; use Magento\Framework\Logger\Handler\Base; use Monolog\Logger; class DebugHandler extends Base { /** * @var string */ protected $fileName = '/var/log/custom_debug.log'; /** * @var int */ protected $loggerType = Logger::DEBUG; }
Now, let’s tell magento to use this customer handler. This can be done in the module di.xml
file by setting a preference, just like we saw above.
# File: app/code/Webguru/SampleModule/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"> <preference for="Magento\Framework\Logger\Handler\Debug" type="Webguru\SampleModule\Model\DebugHandler" /> </config>
Alternatively we can create a more specific di
configuration. So, instead of preference
we can use a type
config in our di.xml
file to just replace the debug handler.
# File: app/code/Webguru/SampleModule/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="Magento\Framework\Logger\Monolog"> <arguments> <argument name="handlers" xsi:type="array"> <item name="debug" xsi:type="object">Webguru\SampleModule\Model\DebugHandler</item> </argument> </arguments> </type> </config>
The downside of any of these approaches is that we are replacing\reconfiguring the instance of class Magento\Framework\Logger\Monolog
for all magento framework and not just our module. This means that all debug logs will now be written into our custom log file.
To overcome this problem we should use a virtualType
config in our di.xml
instead. virtualType
allow us to create like a virtual class of a certain class (type). This newly created virtual class can then be used by magento whenever within our module a request to inject a LoggerInterface
class is made. For this we need to set a type
config to explicitly say that the argument logger (of type LoggerInterface
) should be injected with our virtualType
class and not the default class defined in general app/etc/di.xml
.
# File: app/code/Webguru/SampleModule/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="CustomLogger" type="Magento\Framework\Logger\Monolog"> <arguments> <argument name="name" xsi:type="string">main</argument> <argument name="handlers" xsi:type="array"> <item name="system" xsi:type="object">Magento\Framework\Logger\Handler\System</item> <item name="debug" xsi:type="object">Webguru\SampleModule\Model\DebugHandler</item> <item name="syslog" xsi:type="object">Magento\Framework\Logger\Handler\Syslog</item> </argument> </arguments> </virtualType> <type name="Webguru\SampleModule\Block\Sample"> <arguments> <argument name="logger" xsi:type="object">CustomLogger</argument> </arguments> </type> </config>
Now, only calls to the debug
method of the argument logger
in our Webguru\SampleModule\Block\Sample
class will log into our custom log file.