NewRelic: monitoring aplikace na Nette Frameworku

Má Vaše aplikace víc než pět návštěv denně? Pak není od věci nějakým způsobem monitorovat, co se děje. Za tímhle účelem vznikají nejrůznější placené i opensource řešení. Některé lepší, některé horší. Několik měsíců zpátky jsem řešil, jaký monitoring nasadím na svoje sexy VPS od Wedosu (na kterém běží i tento blog).

Nebudu to protahovat, zvolil jsem nakonec NewRelic, který mi poradil Honza Doleček. Jeho jediné mínus je, že je docela drahý. Ale po měsíci trial verze a tričku zdarma už se mi nechtělo nikam migrovat.

Pak jsem dlouho monitoring neřešil a teď máme NewRelic i v Damejidlo.cz. Na své VPS mám pár malých webíků, ale tady už začíná být kritické, mít vše pod dohledem.

Před pár dny jsem objevil killer feature NewRelicu a o tu bych se s Vámi chtěl zde podělit. Je to jeho PHP API. Abych to trošku rozvedl, NewRelic se instaluje tak, že do phpčka zavedete modul a nastavíte IDčko aplikace. V ten moment začne rozsíření odesílat data na jejich servery a já se můžu kochat krásnými grafy :)

newrelic-overview

A to prý je Doctrine2 pomalá ;)

Stejným způsobem, prostou instalací balíku, jde rozchodit i monitoring systému.

newrelic-pizza-overview

Náš dev server se moc nenadře :)

S chytrými frameworky je ale problém..

a určitě Vám hned dojde jaký, při pohledu na tento screenshot.

newrelic-index

NewRelic nerozlišuje adresy, protože všechno jde na index.php

Další “problém” je, že Nette Framework sám řeši všechny chyby a výjimky a NewRelic se tak vůbec v tomto nedostane ke slovu. Když tedy server začne spamovat logy laděnkama, dozvím se to až když mi přijde email, který ani navíc přijít nemusí.

A tady přichází k řeči PHP API, na které mě též upozornil Honza. Chtěl jsem to mít cool, tak jsem řešení založil “na svém Event systému”:/blog/eventy-a-nette-framework.

Tohle API pokrývá rozlišení jednotlivých requestů, logování errorů a výjimek a umí taky označit požadavek jako “proces na pozadí”((background job)). Což není špatné rozlišit, protože vydatně používáme CLI scripty přes Symfony Consoli, pouštené cronem a nechceme, aby se nám pletly do frontend requestů. Byť snad všechny zatím roztřídil správně, jistota je jistota :)

Kompletní řešení tedy vypadá takto

namespace NewRelic;

use Kdyby;
use Nette;
use Nette\Application\Application;
use Nette\Application\Request;
use Nette\Diagnostics\Debugger;

class NewRelicProfilingListener extends Nette\Object implements Kdyby\Events\Subscriber
{
    public function getSubscribedEvents()
    {
        return array(
            'Nette\\Application\\Application::onStartup',
            'Nette\\Application\\Application::onRequest',
            'Nette\\Application\\Application::onError'
        );
    }

    public function onStartup(Application $app)
    {
        if (!extension_loaded('newrelic')) {
            return;
        }

        // registrace vlastního loggeru na errory
        Debugger::$logger = new Logger;
        Debugger::$logger->directory =& Debugger::$logDirectory;
        Debugger::$logger->email =& Debugger::$email;
    }

    public function onRequest(Application $app, Request $request)
    {
        if (!extension_loaded('newrelic')) {
            return;
        }

        if (PHP_SAPI === 'cli') {
            // uložit v čitelném formátu
            newrelic_name_transaction('$ ' . basename($_SERVER['argv'][0]) . ' ' . implode(' ', array_slice($_SERVER['argv'], 1)));

            // označit jako proces na pozadí
            newrelic_background_job(TRUE);

            return;
        }

        // pojmenování požadavku podle presenteru a akce
        $params = $request->getParameters();
        newrelic_name_transaction($request->getPresenterName() . (isset($params['action']) ? ':' . $params['action'] : ''));
    }

    public function onError(Application $app, \Exception $e)
    {
        if (!extension_loaded('newrelic')) {
            return;
        }

        if ($e instanceof Nette\Application\BadRequestException) {
            return; // skip
        }

        // logovat pouze výjimky, které se dostanou až k uživateli jako chyba 500
        newrelic_notice_error($e->getMessage(), $e);
    }
}

A ještě Logger

namespace NewRelic;

use Nette;

class Logger extends Nette\Diagnostics\Logger
{
    public function log($message, $priority = self::INFO)
    {
        $res = parent::log($message, $priority);

        // pouze zprávy, které jsou označené jako chyby
        if ($priority === self::ERROR || $priority === self::CRITICAL) {
            if (is_array($message)) {
                $message = implode(' ', $message);
            }
            newrelic_notice_error($message);
        }

        return $res;
    }
}

Pokud máte v aplikaci Kdyby/Events, tak je rozběhání listeneru otázkou tří řádků konfigurace

services:
    newRelicListener:
        class: NewRelic\NewRelicProfilingListener
        tag: [kdyby.subscriber]

Co se týče logování chyb, má NewRelic do laděnky ještě světelné míle daleko. To co tam je teď, připomíná spíše brášku log/error.log. Pro laděnky si tedy stále musím dojít do logu na server. Je ale super vidět prolnutí chybovosti vzhledem k počtu požadavků. Určitě tedy stojí za to, posílat chyby do NewRelicu.

Na druhou stranu, chudý rozbor chyb je vynahrazený luxusním profilerem, který automaticky loguje requesty, které trvají déle než by měly a velice přesně, až možná doterně, upozorňuje na úzká hrdla aplikace.

Výsledek

Nyní už uvidím jednotlivé requesty podle presenteru a akce

newrelic-presenters

Requesty seskupené podle presenterů

stejně tak procesy, které probíhají na pozadí

newrelic-background-jobs

Procesy na pozadí

a také všechny chyby, které se v aplikaci vyskytnou.

newrelic-errors

Procento chyb vzhledem k requestům

Naštěstí jich tam moc není :)

Za mě můžu NewRelic jedině doporučit. U větších aplikací je to must have. Jak monitorujete svoje aplikace vy?

Have you found a typo? Fix me

Autor:

comments powered by Disqus