如何将Monolog日志写入文件和远程数据库

时间:2016-09-05 09:32:14

标签: mongodb symfony monolog

我有一个Symfony2项目,它将日志写入不同的文件,但我希望它也将日志写入远程数据库(mongodb)。我希望将实际日志文件保留在服务器中作为备份,以防数据库连接出现问题。

问题1: 甚至可以同时将相同的日志保存到两个不同的地方吗?

问题2: 如何将日志保存到mongodb?我不一定需要特定的mongodb指令,但有关如何使用monologger写入远程数据库的一些指导原则。如果可以的话,也欢迎特定于mongodb的说明。 ;)

问题3(可选): 我能以某种方式在日志中获得完整的错误堆栈吗?哪里可以找到Monolog可以实际写入的数据以及如何编写的完整列表?

2 个答案:

答案 0 :(得分:1)

有时候有一个非常好的Blogpost用于记录到monolog和doctrine的mysql数据库。我不能再找到它了,所以我只需要在这里添加必要的文件,你就可以调整它了。 整个逻辑在DatabaseHandler中完成,因此您只需更改即可 mysql插入到你的mongodb的处理。 如果有人知道原帖请注释,此代码不是我的。

<强> BacktraceLoggerListener.php

namespace UtilsBundle\EventListener;

use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;

class BacktraceLoggerListener{
private $_logger;

public function __construct(LoggerInterface $logger = null)
{
    $this->_logger = $logger;
}

public function onKernelException(GetResponseForExceptionEvent $event)
{
    $this->_logger->addError($event->getException());
}
}

<强> DatabaseHandler.php

namespace UtilsBundle\Logger;



use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Logger;

/**
 * Stores to database
 *
 */
class DatabaseHandler extends AbstractProcessingHandler{
protected $_container;

/**
 * @param string $stream
 * @param integer $level The minimum logging level at which this handler will be triggered
 * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
 */
public function __construct($level = Logger::DEBUG, $bubble = true)
{
    parent::__construct($level, $bubble);
}

/**
 *
 * @param type $container
 */
public function setContainer($container)
{
    $this->_container = $container;
}

/**
 * {@inheritdoc}
 */
protected function write(array $record)
{

    // Ensure the doctrine channel is ignored (unless its greater than a warning error), otherwise you will create an infinite loop, as doctrine like to log.. a lot..
    if( 'doctrine' == $record['channel'] ) {

        if( (int)$record['level'] >= Logger::WARNING ) {
            error_log($record['message']);
        }

        return;
    }
    // Only log errors greater than a warning
    // TODO - you could ideally add this into configuration variable
    if( (int)$record['level'] >= Logger::NOTICE ) {

        try
        {
            // Logs are inserted as separate SQL statements, separate to the current transactions that may exist within the entity manager.
            $em = $this->_container->get('doctrine')->getManager();
            $conn = $em->getConnection();

            $created = date('Y-m-d H:i:s');

            $serverData = ""; //$record['extra']['server_data'];
            $referer = "";
            if (isset($_SERVER['HTTP_REFERER'])){
                $referer= $_SERVER['HTTP_REFERER'];
            }

            $stmt = $em->getConnection()->prepare('INSERT INTO system_log(log, level, server_data, modified, created)
                                    VALUES(' . $conn->quote($record['message']) . ', \'' . $record['level'] . '\', ' . $conn->quote($referer) . ', \'' . $created . '\', \'' . $created . '\');');
            $stmt->execute();

        } catch( \Exception $e ) {

            // Fallback to just writing to php error logs if something really bad happens
            error_log($record['message']);
            error_log($e->getMessage());
        }
    }
}
}

我们在这里使用了xml,但这可以在 services.yml也是

<强>的services.xml

<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services     http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
    <service id="utils.database.logger" class="UtilsBundle\Logger\DatabaseHandler">
        <call method="setContainer">
            <argument type="service" id="service_container" />
        </call>
    </service>

    <service id="utils.backtrace.logger.listener" class="UtilsBundle\EventListener\BacktraceLoggerListener">
        <argument type="service" id="logger" />
        <tag name="monolog.logger" channel="backtrace" />
        <tag name="kernel.event_listener"  event="kernel.exception" method="onKernelException" />
    </service>
</services>

最后将处理程序添加到您的monolog配置中 config _ **。yml所以这里用于生产,例如

config_prod.yml

monolog:
handlers:
    main:
        type:         rotating_file
        action_level: error
        max_files: 10
        handler:      nested
    nested:
        type:  stream
        path:  "%kernel.logs_dir%/%kernel.environment%.log"
        level: debug
    console:
        type:  console
    database:
        type: service
        level: notice
        id: utils.database.logger
        channels: ["!translation"]

希望有所帮助

答案 1 :(得分:0)

希望我能帮到你:

问题1 :是的,可能。例如。你可以做smt。像:

    $this->logger->pushHandler(new StreamHandler('/path/to/logs/123_info.log',    Logger::INFO));
    $this->logger->pushHandler(new StreamHandler('/path/to/logs/456_warning.log',    Logger::INFO));

因此,如果$this->logger->addInfo("testinfo");在两个流中都记录了这一点。

问题2 :根据StreamHandler,有一个MongoDBHandler。您应该可以对其进行配置并将其传递给pushHandler方法,或者如果您想在服务中查看它,请查看MongoDBConfiguration

问题3 : 这应该有所帮助:Configure Monolog

希望有所帮助。