Como criar um arquivo de log personalizado no Magento 2?

57

No Magento 1, era comum segmentar logs em arquivos diferentes (para separar logs para métodos de pagamento, etc.). É tão fácil quanto mudar o $fileparâmetro de Mage::log.

Magento 2 mudou para usar o Monolog.

Parece que o Monolog (ou a implementação do Magento2) segmenta todos os logs de todo o framework para manipuladores por gravidade. Existem alguns manipuladores que gravam no arquivo:

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

Registrando nos arquivos respectivos em var / log como no Magento 1.

Eu poderia adicionar um manipulador para uma determinada gravidade (IE, escrever avisos para var/log/notice.log). Estenda \Magento\Framework\Logger\Handler\Basee registre o manipulador di.xml.

Este artigo descreve aproximadamente esse processo: http://semaphoresoftware.kinja.com/how-to-create-a-custom-log-in-magento-2-1704130912

Mas como eu escrevo todos os logs (não apenas uma gravidade) para uma classe (nem todo o Magento) no meu arquivo de escolha?

Parece que terei que criar minha própria versão Magento\Framework\Logger\Monolog, mas como tudo se encaixa para que realmente funcione?

Se este é um grande não-não no Magento 2, então qual é a alternativa? Eu quero algo para separar os logs dessa extensão com o objetivo de depurá-la quando necessário nos sites do cliente. Ter essas informações gravadas em system.log, exception.log etc. e misturadas com os logs de todos os outros módulos não é prático.

Ryan Hoerr
fonte

Respostas:

99

Você não precisa personalizar ou tentar estender o registro do Magento2. Como você disse, está usando o Monolog apenas com uma leve personalização. É suficiente escrever seu próprio logger estendendo o Monolog com muito pouco esforço.

Supondo que seu módulo esteja em YourNamespace/YourModule:

1) Escreva a classe Logger em Logger/Logger.php:

<?php
namespace YourNamespace\YourModule\Logger;

class Logger extends \Monolog\Logger
{
}

2) Escreva a classe Handler em 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 é o único passo que usa o código Magento. \Magento\Framework\Logger\Handler\Baseestende o Monolog StreamHandlere, por exemplo, acrescenta o atributo $ fileName ao caminho base do Magento.

3) Registre o Agente de Log na injeção de dependência 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: Isso não é estritamente necessário, mas permite que o DI passe argumentos específicos para o construtor. Se você não executar esta etapa, precisará ajustar o construtor para definir o manipulador.

4) Use o logger em suas classes Magento:

Isso é feito por injeção de dependência. Abaixo, você encontrará uma classe fictícia que grava apenas uma entrada de log:

<?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
fonte
2
Eu estava perguntando algo semelhante a um dos arquitetos no outro dia, então obrigado por este exemplo! Eu estava pensando em adicionar suporte com base no nome da classe para que o framework DI pudesse injetar o criador de logs "certo" em diferentes classes e ter opções no Admin para ativar / desativar os sinalizadores sem alterações de código como esta. Quão útil seria esse tipo de funcionalidade para as pessoas?
Alan Kent
11
Manoj, se o modelo ao qual você está se referindo tiver uma classe de bloco com o logger, você poderá escrever um método público que passará a mensagem para o logger. O seu exemplo vai funcionar desde _logger está protegido se é que existe
halk
3
Na minha opinião, a abordagem atual está um passo atrás do que o M1 tinha. O registro também deve ser uma ferramenta de desenvolvedor, não apenas para monitorar um aplicativo ativo. Eu posso ver como um multi-purpose biblioteca simplificado opcional poderia ser criado para ser usado no desenvolvimento substituindo a implementação atual e, em seguida, substituído para uso em produção
Barbazul
2
@ AlanKent Eu concordo com o barbazul aqui - a capacidade de acessar facilmente o arquivo que você queria, especificando o nível rapidamente no M1 foi ótimo. Isso não é tão flexível (dinamicamente), o que é uma pena. Seria bom ter o nome do arquivo como parâmetro para as chamadas do criador de logs padrão. Obrigado pela resposta halk!
Robbie Averill
2
Para mim, está sempre usando o /var/log/system.log, alguma ideia do porquê?
MagePsycho
20

Podemos registrar dados em um arquivo 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
fonte
2
isso é rápido e fácil
PMB
9

A maneira mais simples possível:

$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
fonte
6

Além das respostas de Halk e Pradeep Kumar: se de fato sua única preocupação é registrar-se em um arquivo diferente, existe uma maneira um pouco mais fácil. Especialmente se você deseja incorporar isso a vários módulos ou se deseja arquivos de log diferentes dentro do seu módulo. Com esse método, você não precisa criar manipuladores personalizados.

Supondo que seu módulo esteja MyNamespace/MyModulee que a classe, que você deseja registrar em um arquivo personalizado, seja chamada MyClass. Se o construtor da classe já injetar, \Psr\Log\LoggerInterfacepule para a etapa 2). Caso contrário, você precisará injetá-lo no construtor:

1) Injete o LoggerInterface em sua classe 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;
    }
}

Se você estender uma classe que já inclua um criador de logs (como \Magento\Framework\App\Helper\AbstractHelper), poderá substituir esse membro (geralmente $_logger) em vez de usar outro. Basta adicionar $this->_logger = $logger após a diretiva do construtor pai.

<?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 o logger via injeção de dependência 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>

Isso registrará tudo no /var/log/mymodule.log.

Se você precisar fazer logon em um arquivo diferente para uma classe diferente, basta criar outro criador de logs virtual com outro manipulador virtual e injetá-lo nessa classe.

T. Dreiling
fonte
5

Se você precisar apenas da sua classe:

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
fonte
pushHandler não é método exposto na interface e a implementação parece não funcionar ...
George
Sua versão do Magento?
mshakeel 28/01
Magento CE 2.2.0
George
Vou experimentá-lo no CE 2.2.0 e voltar para você. Eu usei-o em 2.1
mshakeel 29/01
2

Experimente o módulo " praxigento / mage2_ext_logging ". Este módulo adiciona o suporte "Monolog Cascade" ao Magento 2. "Monolog Cascade" permite configurar a saída de log com um único arquivo de configuração. Você pode imprimir seus registros em diferentes arquivos, bancos de dados, enviar alertas por e-mail e etc. sem modificar seu próprio código.

Esta é uma amostra do arquivo de configuração ('var / log / logging.yaml' por padrão):

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
fonte
1

Se não houver alteração lógica e apenas for necessário alterar um nome de arquivo de log personalizado, não será necessário criar uma classe de logger personalizada, apenas siga as etapas abaixo

1. em 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. Manipulador

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

onde quer que você precise registrar os dados, é necessário chamar o registro PSR padrão
que é

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

Então, o exemplo acima registrará todos os dados de depuração no test.log, se você precisar alterar o sistema, também poderá adicionar a linha abaixo em di.xml

Pradeep Kumar
fonte
0

Eu tentei isso abaixo do código do objeto do registrador em um módulo de terceiros onde desejo obter as informações de log que coloquei e colocá-las no arquivo custom.log, verifique esse código, você definitivamente obtém os logs no seu arquivo de log 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);

Se você precisar de mais informações, comentar aqui, eu responderei. Obrigado.

Jdprasad V
fonte