Symfony Live 2015 Paris - Monitorer sa prod

Symfony Live 2015 Paris - Monitorer sa prod

Développer une application Symfony est maintenant chose commune, mais en connaissez-vous vraiment le comportement en production ? Combien de “fatal error” avez-vous générées aujourd'hui ? Quel est le temps de réponse moyen ? Quelle est la charge absorbée ? Au programme : des éléments sur la gestion des erreurs en prod, la configuration et la customisation de Monolog, le stockage des logs avec elastic search et la visualisation kibana, l’alerting avec sentry et enfin la télémétrie avec graphite, pour vous donner des pistes et savoir où intervenir avant que trop d'utilisateurs ne se plaignent, ou pire, vous abandonnent.

7602f2751868682b296171f58589c851?s=128

Grégoire Pineau

April 09, 2015
Tweet

Transcript

  1. 2.

    blackfire.io @blackfireio #blackfireio Qui suis-je ? • Grégoire Pineau •

    Devops pour Blackfire // SensioLabs • twitter / Github : @lyrixx 2
  2. 6.

    blackfire.io @blackfireio #blackfireio … Oups … pas d’histoire • page

    blanche • pas de logs • pas d’alerte => ça c’était avant (Symfony <= 2.3) 6
  3. 7.

    blackfire.io @blackfireio #blackfireio Sommaire • Pourquoi logger ? • Avec

    Monolog ? • Et dans Symfony ? • Et les erreurs PHP ? 7
  4. 9.

    blackfire.io @blackfireio #blackfireio • Suivre un flow d'exécution • Comprendre

    du code • Trouver les bugs • Résoudre les bugs ? Pour debugger 9
  5. 10.

    blackfire.io @blackfireio #blackfireio Pour monitorer • Recevoir un mail à

    chaque erreur • Recevoir une notification slack • Envoyer ses logs dans: ◦ Sentry ◦ Elasticsearch (coucou @odolbeau) ◦ … ◦ => Pour agrégation • ... 10
  6. 13.

    blackfire.io @blackfireio #blackfireio Les niveaux de logs DEBUG Information pour

    le debug INFO Évènement intéressant NOTICE Évènement intéressant, mais occasionnel WARNING Évènement occasionnel, qui n'impacte pas l’utilisateur ERROR Erreur lors de l’execution CRITICAL Erreur grave, qui doit etre résolu le plus rapidement ALERT Erreur grave, qui doit etre résolu encore + rapidement EMERGENCY WTF OMG BBQ !!! Plus rien de marche 13
  7. 15.

    blackfire.io @blackfireio #blackfireio #!/usr/bin/env php <?php require __DIR__.'/../vendor/autoload.php'; $logger =

    new Monolog\Logger('app'); $logger->debug('the user wants to logout.'); $logger->info('the user is logged out.'); 15
  8. 16.

    blackfire.io @blackfireio #blackfireio #!/usr/bin/env php <?php require __DIR__.'/../vendor/autoload.php'; $logger =

    new Monolog\Logger('app'); $context = [ 'username' => 'alice', 'email' => 'alice@example.com', ]; $logger->info('new registration.', $context); 16
  9. 17.

    blackfire.io @blackfireio #blackfireio Logger dans symfony Il y a un

    service “logger”: /** @Route("/log", name="log") */ public function logAction() { $logger = $this->get('logger'); $logger->debug('MY DEBUG LOG'); $logger->info('MY INFO LOG'); } 17
  10. 18.

    blackfire.io @blackfireio #blackfireio Logger dans symfony Que l’on peut aussi

    injecter: services: workflow: class: AppBundle\Workflow\Workflow arguments: ["@logger"] 18
  11. 19.

    blackfire.io @blackfireio #blackfireio use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; class Workflow {

    private $logger; function __construct(LoggerInterface $logger = null) { $this->logger = $logger ?: new NullLogger(); } } 19
  12. 25.

    blackfire.io @blackfireio #blackfireio Les channels • Permet de grouper les

    logs • Permet de filtrer facilement les logs • Ne pas hésiter à en “créer” (5 .. 20) • Il en existe déjà dans symfony ◦ assetic ◦ doctrine ◦ event ◦ php ◦ profiler ◦ request ◦ ... 25
  13. 28.

    blackfire.io @blackfireio #blackfireio Les Processors • Permet d’ajouter plus d’informations

    ◦ => extra • Permet de retravailler les logs ◦ => context 28
  14. 30.

    blackfire.io @blackfireio #blackfireio [2015-04-08 21:14:50] app.INFO: add product to cart.

    { "product":"sf live paris 2015","quantity": 1 } { "user": { "username":"alice", "email":"alice@example.com" } } 30
  15. 31.

    blackfire.io @blackfireio #blackfireio Ajouter plus d’information • Processor natif et

    à mettre en place: ◦ WebProcessor: URI + Method + IP du client ◦ UidProcessor: Ajoute un unique id par ▪ Requête HTTP ▪ Command • Custom (par exemple): ◦ UserProcessor: ajoute des informations sur l’ utilisateur connecté ◦ RequestContextProcessor: ajoute un potentiel unique id dans les headers de la requête (SOA) 31 Attention aux consumers
  16. 33.

    blackfire.io @blackfireio #blackfireio <?php use Symfony\Component\DependencyInjection\Container; class UserProcessor { protected

    $container; public function __construct(Container $container) { $this->container = $container; } } 33 WHATTT !!
  17. 34.

    blackfire.io @blackfireio #blackfireio public function __invoke(array $record) { $user =

    $this->container ->get('security.token_storage') ->getToken() ->getUser(); $record['extra']['user'] = [ 'username' => $user->getUsername(), 'uuid' => $user->getUuid(), ]; return $record; } 34 Attention !!
  18. 35.

    blackfire.io @blackfireio #blackfireio [2015-04-08 20:11:21] app.INFO: new registration. [] {

    "user": { "username":"alice", "email":"alice@example.com" } } 35
  19. 37.

    blackfire.io @blackfireio #blackfireio Mettre en forme le context • Permet

    de retravailler le tableau “context” • Par exemple: ◦ extraire le nom d’un produit ◦ extraire le titre d’un article ◦ ... 37
  20. 38.

    blackfire.io @blackfireio #blackfireio <?php $product = new Product('sf live paris

    2015', 10); $this->get('logger')->debug('add product to cart.', [ 'product' => $product, 'quantity' => 1, ]); 38
  21. 39.

    blackfire.io @blackfireio #blackfireio [2015-04-08 21:27:50] app.DEBUG: add product to cart.

    { "product":"[object] (...\\Product: {})", "quantity":1 } [] 39
  22. 40.

    blackfire.io @blackfireio #blackfireio [2015-04-08 21:34:12] app.DEBUG: add product to cart.

    { "product":{ "name":"sf live paris 2015", "price":10 }, "quantity":1 } [] 40
  23. 41.

    blackfire.io @blackfireio #blackfireio class ProductProcessor { public function __invoke(array $record)

    { if (!isset($record['context']['product'])) { return $record; } $product = $record['context']['product']; if (!$product instanceof Product) { return $record; } $record['context']['product'] = [ 'name' => $product->getName(), 'price' => $product->getPrice(), ]; return $record; } } 41
  24. 42.

    blackfire.io @blackfireio #blackfireio Résumé • On injecte le logger partout

    ! • On log, on log, on log et on log • On utilise des channels • On utilise des processors pour enrichir ses logs: ◦ ip ◦ url ◦ user connecté • On utilise des processors pour mettre en forme le context 42
  25. 44.

    blackfire.io @blackfireio #blackfireio Le saviez vous ? • Pour Symfony

    <= 2.5 ◦ Les fatal errors PHP ne sont pas logguées • Pour Symfony >= 2.6 ◦ Les fatal errors PHP sont logguées ◦ Les notices PHP ne sont pas logguées • Pour Symfony >= 2.[7|8] ◦ Les notices PHP sont logguées 44
  26. 45.

    blackfire.io @blackfireio #blackfireio Tirer parti de symfony • pour Symfony

    <= 2.5 ◦ utiliser le Monolog\ErrorHandler • pour Symfony >= 2.6 ◦ utiliser Symfony\Component\Debug\ErrorHandler Dans tous les cas, on branche le logger dans l’error handler. 45
  27. 49.

    blackfire.io @blackfireio #blackfireio class AppKernel extends Kernel { public function

    boot() { parent::boot(); if (false === $this->debug) { $handler = ErrorHandler::register(); $handler->throwAt(0, true); $handler->scopeAt(0, true); $handler->traceAt(E_ALL, true); $logger = $this->container->get('monolog.logger.php_error'); $handler->setDefaultLogger($logger); } } } 49
  28. 50.

    blackfire.io @blackfireio #blackfireio ErrorHandler Pour quel niveau d’erreur PHP: en

    prod ? throwAt On lance une exception 0 => rien scopeAt On log les variables de la backtrace 0 => rien traceAt On ajoute la stracktrace dans le log E_ALL => tout 50
  29. 51.

    blackfire.io @blackfireio #blackfireio Attention au mapping Attention au mapping des

    erreurs PHP sur les niveaux d’erreurs Monolog / PSR. Dans Symfony 2.5 .. 2.6, ils ne sont pas parfaits. Pour moi, une notice PHP est du niveau d’une error Monolog / PSR 51