Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

Grégoire Pineau

April 09, 2015
Tweet

More Decks by Grégoire Pineau

Other Decks in Technology

Transcript

  1. blackfire.io @blackfireio
    #blackfireio
    Faites plaisir à vos
    utilisateurs :
    surveillez votre prod
    Symfony Live Paris 2015
    1

    View Slide

  2. blackfire.io @blackfireio
    #blackfireio
    Qui suis-je ?
    ● Grégoire Pineau
    ● Devops pour Blackfire // SensioLabs
    ● twitter / Github : @lyrixx
    2

    View Slide

  3. Log Log Log
    3

    View Slide

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

    View Slide

  5. blackfire.io @blackfireio
    #blackfireio5

    View Slide

  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

    View Slide

  7. blackfire.io @blackfireio
    #blackfireio
    Sommaire
    ● Pourquoi logger ?
    ● Avec Monolog ?
    ● Et dans Symfony ?
    ● Et les erreurs PHP ?
    7

    View Slide

  8. Pourquoi logger ?
    8

    View Slide

  9. blackfire.io @blackfireio
    #blackfireio
    ● Suivre un flow d'exécution
    ● Comprendre du code
    ● Trouver les bugs
    ● Résoudre les bugs ?
    Pour debugger
    9

    View Slide

  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

    View Slide

  11. PSR/3
    LoggerInterface
    11

    View Slide

  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

    View Slide

  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

    View Slide

  14. Et avec monolog ?
    14

    View Slide

  15. blackfire.io @blackfireio
    #blackfireio
    #!/usr/bin/env 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

    View Slide

  16. blackfire.io @blackfireio
    #blackfireio
    #!/usr/bin/env php
    require __DIR__.'/../vendor/autoload.php';
    $logger = new Monolog\Logger('app');
    $context = [
    'username' => 'alice',
    'email' => '[email protected]',
    ];
    $logger->info('new registration.', $context);
    16

    View Slide

  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

    View Slide

  18. blackfire.io @blackfireio
    #blackfireio
    Logger dans symfony
    Que l’on peut aussi injecter:
    services:
    workflow:
    class: AppBundle\Workflow\Workflow
    arguments: ["@logger"]
    18

    View Slide

  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

    View Slide

  20. Les handlers
    (dispatcher ses logs)
    20

    View Slide

  21. blackfire.io @blackfireio
    #blackfireio
    Les handlers
    Votre code LOG Monolog
    Slack
    app/logs/prod.log
    Elasticsearch
    Sentry
    21

    View Slide

  22. C’est tout ?
    Non !
    22

    View Slide

  23. Les channels
    (grouper ses logs)
    23

    View Slide

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

    View Slide

  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

    View Slide

  26. blackfire.io @blackfireio
    #blackfireio
    services:
    workflow:
    class: AppBundle\Workflow\Workflow
    arguments: ["@logger"]
    tags:
    -
    name: monolog.logger
    channel: workflow
    26

    View Slide

  27. Les processors
    (Enrichir ses logs)
    27

    View Slide

  28. blackfire.io @blackfireio
    #blackfireio
    Les Processors
    ● Permet d’ajouter plus d’informations
    ○ => extra
    ● Permet de retravailler les logs
    ○ => context
    28

    View Slide

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

    View Slide

  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":"[email protected]"
    }
    }
    30

    View Slide

  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

    View Slide

  32. blackfire.io @blackfireio
    #blackfireio
    namespace AppBundle\Monolog\Processor;
    class UserProcessor
    {
    public function __invoke(array $record)
    {
    return $record;
    }
    }
    32

    View Slide

  33. blackfire.io @blackfireio
    #blackfireio
    use Symfony\Component\DependencyInjection\Container;
    class UserProcessor
    {
    protected $container;
    public function __construct(Container $container)
    {
    $this->container = $container;
    }
    }
    33
    WHATTT !!

    View Slide

  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 !!

    View Slide

  35. blackfire.io @blackfireio
    #blackfireio
    [2015-04-08 20:11:21]
    app.INFO:
    new registration.
    []
    {
    "user": {
    "username":"alice",
    "email":"[email protected]"
    }
    }
    35

    View Slide

  36. C’est tout ?
    Non !
    36

    View Slide

  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

    View Slide

  38. blackfire.io @blackfireio
    #blackfireio
    $product = new Product('sf live paris 2015', 10);
    $this->get('logger')->debug('add product to cart.', [
    'product' => $product,
    'quantity' => 1,
    ]);
    38

    View Slide

  39. blackfire.io @blackfireio
    #blackfireio
    [2015-04-08 21:27:50]
    app.DEBUG:
    add product to cart.
    {
    "product":"[object] (...\\Product: {})",
    "quantity":1
    }
    []
    39

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  43. Et les erreurs PHP ?
    43

    View Slide

  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

    View Slide

  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

    View Slide

  46. Nouveau channel
    46

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  52. C’est tout ?
    Oui ...
    52

    View Slide

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

    View Slide

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

    View Slide