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. blackfire.io @blackfireio #blackfireio Faites plaisir à vos utilisateurs : surveillez

    votre prod Symfony Live Paris 2015 1
  2. blackfire.io @blackfireio #blackfireio Qui suis-je ? • Grégoire Pineau •

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

  4. blackfire.io @blackfireio #blackfireio Erreur PHP C’est l’histoire d’une fatal erreur

    … … en prod … 4
  5. blackfire.io @blackfireio #blackfireio5

  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
  7. blackfire.io @blackfireio #blackfireio Sommaire • Pourquoi logger ? • Avec

    Monolog ? • Et dans Symfony ? • Et les erreurs PHP ? 7
  8. Pourquoi logger ? 8

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

    du code • Trouver les bugs • Résoudre les bugs ? Pour debugger 9
  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
  11. PSR/3 LoggerInterface 11

  12. blackfire.io @blackfireio #blackfireio psr/log 1.0 • https://packagist.org/packages/psr/log • Psr\Log\LoggerInterface •

    Quelques Traits / Outils • Psr\Log\LogLevel 12
  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
  14. Et avec monolog ? 14

  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
  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
  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
  18. blackfire.io @blackfireio #blackfireio Logger dans symfony Que l’on peut aussi

    injecter: services: workflow: class: AppBundle\Workflow\Workflow arguments: ["@logger"] 18
  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
  20. Les handlers (dispatcher ses logs) 20

  21. blackfire.io @blackfireio #blackfireio Les handlers Votre code LOG Monolog Slack

    app/logs/prod.log Elasticsearch Sentry 21
  22. C’est tout ? Non ! 22

  23. Les channels (grouper ses logs) 23

  24. blackfire.io @blackfireio #blackfireio Détail d’un log 24

  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
  26. blackfire.io @blackfireio #blackfireio services: workflow: class: AppBundle\Workflow\Workflow arguments: ["@logger"] tags:

    - name: monolog.logger channel: workflow 26
  27. Les processors (Enrichir ses logs) 27

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

    ◦ => extra • Permet de retravailler les logs ◦ => context 28
  29. blackfire.io @blackfireio #blackfireio Détail d’un log 29

  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
  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
  32. blackfire.io @blackfireio #blackfireio <?php namespace AppBundle\Monolog\Processor; class UserProcessor { public

    function __invoke(array $record) { return $record; } } 32
  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 !!
  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 !!
  35. blackfire.io @blackfireio #blackfireio [2015-04-08 20:11:21] app.INFO: new registration. [] {

    "user": { "username":"alice", "email":"alice@example.com" } } 35
  36. C’est tout ? Non ! 36

  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
  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
  39. blackfire.io @blackfireio #blackfireio [2015-04-08 21:27:50] app.DEBUG: add product to cart.

    { "product":"[object] (...\\Product: {})", "quantity":1 } [] 39
  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
  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
  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
  43. Et les erreurs PHP ? 43

  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
  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
  46. Nouveau channel 46

  47. blackfire.io @blackfireio #blackfireio #config.yml monolog: channels: - php_error 47

  48. Configuration de l’application (Symfony 2.6+) 48

  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
  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
  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
  52. C’est tout ? Oui ... 52

  53. blackfire.io @blackfireio #blackfireio Merci Questions ? 53

  54. blackfire.io @blackfireio #blackfireio SensioLabs Recrute Contactez moi ;) 54