¿Cómo crear un archivo de registro personalizado en Magento 2?

57

En Magento 1, era común segmentar los registros en diferentes archivos (para separar los registros de los métodos de pago, etc.). Eso es tan fácil como cambiar el $fileparámetro de Mage::log.

Magento 2 ha cambiado para usar Monolog.

Parece que Monolog (o la implementación de Magento2) segmenta todos los registros de todo el marco a los controladores por gravedad. Hay algunos controladores que escriben en el archivo:

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

Iniciar sesión en los archivos respectivos en var / log como en Magento 1.

Podría agregar un controlador para una gravedad particular (es decir, escribir avisos var/log/notice.log). Extienda \Magento\Framework\Logger\Handler\Basey registre el controlador en di.xml.

Este artículo describe más o menos ese proceso: http://semaphoresoftware.kinja.com/how-to-create-a-custom-log-in-magento-2-1704130912

Pero, ¿cómo hago para escribir todos los registros (no solo una gravedad) para una clase (no todos los de Magento) en mi archivo de elección?

Parece que tendré que crear mi propia versión de Magento\Framework\Logger\Monolog, pero ¿cómo encaja todo para que eso realmente funcione?

Si este es un gran no-no en Magento 2, ¿cuál es la alternativa? Quiero algo para separar los registros de esta extensión con el fin de depurarla cuando sea necesario en los sitios de los clientes. Tener esa información escrita en system.log, exception.log, etc. y mezclada con los registros de cualquier otro módulo no es práctico.

Ryan Hoerr
fuente

Respuestas:

101

No necesita personalizar ni intentar extender el registro de Magento2. Como dijiste, está usando Monolog con solo una ligera personalización. Es suficiente escribir su propio registrador extendiendo Monolog con muy poco esfuerzo.

Suponiendo que su módulo esté en YourNamespace/YourModule:

1) Escriba la clase Logger en Logger/Logger.php:

<?php
namespace YourNamespace\YourModule\Logger;

class Logger extends \Monolog\Logger
{
}

2) Escribir clase de controlador en 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';
}

Nota: este es el único paso que utiliza el código Magento. \Magento\Framework\Logger\Handler\Baseextiende Monolog's StreamHandlery, por ejemplo, antepone el atributo $ fileName con la ruta base de Magento.

3) Registrar registrador en inyección de dependencia 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>

Nota: Esto no es estrictamente necesario, pero permite que el DI pase argumentos específicos al constructor. Si no realiza este paso, debe ajustar el constructor para configurar el controlador.

4) Use el registrador en sus clases de Magento:

Esto se hace mediante inyección de dependencia. A continuación encontrará una clase ficticia que solo escribe una entrada de registro:

<?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');
    }
}
halk
fuente
2
Estaba preguntando algo similar a uno de los arquitectos el otro día, ¡así que gracias por este ejemplo! Me preguntaba acerca de agregar soporte basado en el nombre de la clase para que el marco DI pudiera inyectar el registrador "correcto" a diferentes clases, y tener interruptores en el Administrador para activar / desactivar las banderas sin cambios de código como este. ¿Qué tan útil sería este tipo de funcionalidad para las personas?
Alan Kent
1
Manoj, si la plantilla a la que te refieres tiene una clase de bloque con registrador, entonces puedes escribir un método público que luego pasa el mensaje al registrador. Su ejemplo no funcionará ya que _logger está protegido si es que existe
halk
3
En mi opinión, el enfoque actual es un paso atrás de lo que tenía M1. El registro también debe ser una herramienta de desarrollo, no solo está destinado a monitorear una aplicación en vivo. Puedo ver cómo se podría crear una biblioteca simplificada multipropósito opcional para ser utilizada en el desarrollo anulando la implementación actual y luego reemplazada para uso de producción
barbazul
2
@AlanKent Estoy de acuerdo con barbazul aquí: la capacidad de iniciar sesión fácilmente en cualquier archivo que desee, especificando el nivel rápidamente en M1 fue genial. Esto no es tan flexible (dinámicamente) lo cual es una pena. Sería bueno tener el nombre de archivo como parámetro para las llamadas predeterminadas del registrador. Gracias por la respuesta halk!
Robbie Averill
2
Para mí siempre está tomando el /var/log/system.log, ¿alguna idea de por qué?
MagePsycho
20

Podemos registrar datos en un archivo como este.

$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());
Pramod Kharade
fuente
2
esto es rápido y fácil
PMB
9

La forma más simple posible:

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

Además de las respuestas de Halk y Pradeep Kumar: si de hecho su única preocupación es iniciar sesión en un archivo diferente, hay una manera un poco más fácil. Especialmente si desea incorporar eso a múltiples módulos o si desea diferentes archivos de registro dentro de su módulo. Con este método, no tiene que crear controladores personalizados.

Suponiendo que su módulo está dentro MyNamespace/MyModuley se llama a la clase, que desea registrar en un archivo personalizado MyClass. Si el constructor de la clase ya inyecta, \Psr\Log\LoggerInterfacevaya al paso 2). De lo contrario, debe inyectarlo en el constructor:

1) Inyecte LoggerInterface en su clase 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;
    }
}

Si extiende una clase que ya incluye un registrador (como \Magento\Framework\App\Helper\AbstractHelper), también podría sobrescribir ese miembro (generalmente $_logger) en lugar de usar uno separado. Simplemente agregue $this->_logger = $logger después de la directiva del constructor padre.

<?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) Configurar el registrador mediante inyección de dependencia 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>

Esto registrará todo en /var/log/mymodule.log.

Si necesita iniciar sesión en un archivo diferente para una clase diferente, simplemente puede crear otro registrador virtual con otro controlador virtual e inyectarlo en esa clase.

T. Dreiling
fuente
5

Si lo necesita solo dentro de su clase individual:

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
fuente
pushHandler no está expuesto método en la interfaz y la implementación no parece funcionar ...
George
¿Tu versión de Magento?
mshakeel
Magento CE 2.2.0
George
Lo intentaré en CE 2.2.0 y te responderé. Lo he usado en 2.1
mshakeel
2

Pruebe el módulo " praxigento / mage2_ext_logging ". Este módulo agrega compatibilidad con "Monolog Cascade" a Magento 2. "Monolog Cascade" le permite configurar su salida de registro con un solo archivo de configuración. Puede imprimir sus registros en diferentes archivos, bases de datos, enviar alertas por correo electrónico, etc. sin modificaciones de su propio código.

Esta es una muestra del archivo de configuración ('var / log / logging.yaml' por defecto):

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]
Alex Gusev
fuente
1

Si no hay cambio lógico y solo necesita cambiar un nombre de archivo de registro personalizado, entonces no es necesario crear una clase de registrador personalizado, solo siga los pasos a continuación

1. en 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. Manejador

<?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';
}

donde necesitabas registrar los datos, debes llamar al registro PSR predeterminado
que es

<?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();
    }
}

así que el ejemplo anterior registrará todos los datos de depuración en test.log si necesita cambiar el sistema también puede agregar la siguiente línea en di.xml

Pradeep Kumar
fuente
0

Intenté esto debajo del código de objeto del registrador en un módulo de terceros donde quiero obtener información de registro allí que puse y ponerlos en el archivo custom.log, verifique este código, definitivamente obtiene los registros en su archivo de registro personalizado.

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

Si requieres más información comenta aquí, te responderé. Gracias.

Jdprasad V
fuente