Usage
Une fois le bundle installé et sa clé (key) renseignée, la remontée des exceptions fonctionne sans aucun code supplémentaire ; url ne se renseigne que pour une instance auto-hébergée. La transmission des logs et la remontée manuelle demandent chacune une étape de plus.
Capture automatique des exceptions
Le bundle enregistre monitor.exception_subscriber, un event subscriber qui écoute l'événement kernel.exception de Symfony avec une priorité de -64 (basse, pour que les écouteurs du framework s'exécutent d'abord, le bundle ne fait qu'observer). Pour chaque throwable non géré, il :
- compare les
environmentsconfigurés àkernel.environmentet s'abstient si l'environnement courant n'est pas autorisé ; - construit une charge utile à partir du throwable (classe, message, fichier, ligne et la pile d'appels, complète par défaut) ;
- attache le contexte de la requête, la méthode et l'URL HTTP, ainsi que l'environnement et la release ;
- masque les clés sensibles et envoie le résultat en POST sur
/api/v1/ingest.
Les exceptions HTTP dont le code de statut est inférieur à 500 (NotFoundHttpException et les autres erreurs client attendues, comme les sondes de bots qui tombent sur des 404) sont ignorées : elles ne sont jamais remontées et ne comptent jamais dans votre quota d'événements. Les exceptions HTTP de code 500 et plus sont remontées comme n'importe quel autre throwable.
C'est additif : il ne modifie jamais la réponse, n'interrompt jamais la propagation et ne lève jamais d'erreur. Un envoi qui échoue n'atteint jamais vos utilisateurs.
Il n'est plus silencieux non plus. Le bundle passe le logger de votre application au Reporter lorsque le service existe : un serveur injoignable, et un refus que le serveur explique (un 401 sur une clé mal collée, un 402 pendant une relance de paiement, un 403 sur une limite de forfait), sont écrits dans votre propre log en avertissement, nommant le statut et l'adresse. Aucun logger n'était passé auparavant, et ce client n'a pas de console à lui : un client qui n'avait jamais fonctionné ne le disait donc nulle part.
Le subscriber remonte les throwables qui atteignent le kernel. Les exceptions que vous attrapez et gérez vous-même ne sont, par définition, pas « non gérées », remontez-les manuellement (ci-dessous).
Transmettre les logs
Mettez logs.enabled: true pour activer la transmission des logs. Le service handler Monolog monitor.log_handler est toujours enregistré tant que le bundle est activé ; avec logs.enabled: false, il ignore simplement tous les enregistrements, et un monolog.yaml qui le référence continue donc de compiler. Le handler :
- met en tampon les enregistrements au niveau
logs.levelou au-dessus ; - respecte la liste
environmentsexactement comme le subscriber d'exceptions : hors des environnements autorisés, aucun enregistrement ne quitte votre application ; - ignore les enregistrements qui portent une
exceptiondans leur contexte: ceux-là sont déjà couverts par le pipeline d'exceptions ; - vide le tampon vers
/api/v1/logspar lots delogs.max_batch, et à nouveau lorsque le handler se ferme à la fin de la requête.
Enregistrer le service ne suffit pas à lui seul, vous devez l'attacher à Monolog comme service handler :
Les enregistrements qui transitent par Monolog (niveau warning et au-dessus par défaut) sont désormais regroupés et expédiés vers votre dashboard. Cela nécessite monolog/monolog ^3.0 dans votre application.
Remonter manuellement
Le Reporter partagé est enregistré sous l'identifiant de service monitor.reporter. Comme c'est un service privé non aliassé sur son nom de classe, l'autowiring par type (Reporter $reporter) ne le résout pas, câblez-le explicitement.
Avec l'attribut #[Autowire] sur un argument de constructeur :
Ou en le liant dans services.yaml :
Le Reporter expose :
reportException(Throwable $e, array $context = []): bool: POST/api/v1/ingest;sendLogs(array $logs): bool: POST/api/v1/logs;sendDependencies(array $packages): bool: POST/api/v1/dependencies.
Chacune renvoie false (et ne lève jamais d'erreur) lorsque le client n'est pas configuré ou que l'envoi échoue.
Masquer les données sensibles
Avant que quoi que ce soit ne quitte votre application, les clés scrub configurées sont masquées récursivement dans le tableau de contexte par [scrubbed]. La correspondance est une recherche de sous-chaîne dans le nom de la clé, sans tenir compte de la casse : une clé password configurée masque aussi user_password. Les valeurs par défaut couvrent password, password_confirmation, passphrase, token, signature, secret, authorization, cookie, referer, referrer et api_key. Ajoutez vos propres clés via l'option scrub, voir la Configuration.
À partir de la version 0.3.1, un tiret et un souligné comptent pour le même caractère (api_key masque aussi une clé x-api-key), et le masquage entre dans les chaînes qui portent des valeurs nommées. L'URL de la requête (request.url) perd la valeur de ses paramètres nommés dans la liste (?token=abc en ressort ?token=%5Bscrubbed%5D) et tout segment de chemin fait de quarante lettres ou chiffres d'affilée ; une valeur JSON écrite en chaîne est ouverte et masquée par clé ; et le message de chaque log transmis passe par la même règle : quand il commence par une URL ou qu'il est du JSON, les valeurs qu'il porte sont masquées comme ci-dessus, et le reste de son texte est gardé. À partir de la version 0.3.1 aussi, quatre paramètres d'URL sont masqués quelle que soit la liste scrub, parce qu'une adresse qui les porte ouvre une porte jusqu'à son expiration : hash (les liens de connexion, login_link), _hash (l'UriSigner), sig et signature ; le nom doit être exact, si bien qu'un paramètre content_hash ou une clé hash restent. Voir la page de masquage du cœur.
Le cœur ajoute une seconde passe : les valeurs ayant la forme d'une adresse e-mail, d'un IBAN, d'un numéro de carte, d'un numéro de sécurité sociale ou d'un téléphone français sont masquées où qu'elles se trouvent, texte du message et segments d'URL compris, et le masque nomme ce qu'il cache ([redacted:email]). Elle est active par défaut et se règle par les clés redact et redact_custom de l'arbre monitor, une liste redact vide la désactivant. Voir Configuration et la page de masquage du cœur.
Analyse des dépendances
Le bundle ne fournit pas de commande console pour capturer votre composer.lock (contrairement au SDK Laravel). La méthode sous-jacente Reporter::sendDependencies() existe, et l'endpoint serveur /api/v1/dependencies est agnostique de plateforme : vous pouvez donc câbler votre propre commande console ou étape CI qui lit composer.lock et envoie la liste des packages avec votre clé de projet. Une commande dédiée pourra arriver dans une future version du bundle.
Vous lisez la documentation Symfony Bundle v1.0.