Sentry Performance, PHP a GraphQL

Jak využít Sentry Performance pro monitoring výkonu PHP aplikace včetně výkonu GraphQL API.

Sentry je vynikající nástroj pro monitoring a logování chybových stavů aplikace. Lze velmi snadno využívat jak SaaS službu, tak i na vlastní infrastruktuře díky dostupnému Docker image.

Přidání podpory do Symfony projektu je díky composeru velmi snadné

composer require sentry/sentry-symfony

V tomto zápisku popisuji jak nastavit Sentry jako monitor výkonu PHP Symfony aplikace včetně nastavení pro přehledné monitorování GraphQL API implementovaném pomocí  Overblog GraphQL Bundle. Pro úplně základní nastavení aplikace stačí jednoduchá úprava souboru config/packages/sentry.yaml a to následujícím způsobem

sentry:
    dsn: '%env(SENTRY_DSN)%'
    options:
        traces_sample_rate: '%env(float:SENTRY_PERFORMANCE_RATIO)%'
    tracing:
        enabled: '%env(bool:SENTRY_PERFORMANCE_SWITCH)%'
        dbal: # DB queries
            enabled: true
        cache: # Cache pools
            enabled: true
        twig: # templating engine
            enabled: true

Kde

  • traces_sample_rate v sekci options - poměr měřených transakcí jako číslo mezi 0 a 1 (0.2 pak znamená, že 20% transakcí je měřených). Na devel stage dáváme klidně 1.0, v produkčním prostředí pak třeba 0.02
  • sekce tracing
    • enabled - zapnutí/vypnutí měření transakcí
    • dbal - měření dotazů do databáze
    • cache - měření cache pools
    • twig - měření renderování Twig šablon

Každé z výše uvedených měření může představovat dodatečnou výkonovou zátěž, proto zapnutí/vypnutí konkrétního měření je závislé od konkrétního projektu a jeho potřeb. Nyní už jenom zbývá doplnit env proměnné do .env souborů, např. takto

SENTRY_DSN=https://sentry.io/dsn-vaseho-projektu
SENTRY_PERFORMANCE_SWITCH=true
SENTRY_PERFORMANCE_RATIO=0.05

Pokud je vše dobře nastavené, tak by se vám již s prvními requesty do aplikace měly zobrazovat výkonové charakteristiky měřených transakcí identifikované http metodou a url requestu, např. GET https://www.id-sign.com/poradna. Toto může být dostačující pro klasické stránky, kdy každý obsah má svojí vlastní URL. Pokud však chceme monitorovat výkon GraphQL API, které má pro veškerý obsah jeden endpoint, potřebujeme jednotlivá volání API pojmenovat dle volané query. A ideálně si k tomu doplnit i autentikovaného uživatele, přesný zápis query použité proměnné.

V našich projektech využíváme Overblog GraphQL Bundle, který lze pro toto doplnění velmi snadno upravit. Stačí využít jednu z jeho událostí a to graphql.pre_executor. Připravíme si tedy patřičný EventListener, pojmenovaný např. GraphQLListener:

<?php

namespace App\EventListener;

use App\Entity\User;
use Overblog\GraphQLBundle\Event\ExecutorArgumentsEvent;
use Sentry\Breadcrumb;
use Sentry\SentrySdk;
use Sentry\State\Scope;
use Symfony\Component\Security\Core\Security;
use function Sentry\addBreadcrumb;
use function Sentry\configureScope;

class GraphQLListener
{
    public function __construct(private readonly Security $security)
    {
    }

    public function onPreExecutor(ExecutorArgumentsEvent $event): void
    {
        $sentryTransaction = SentrySdk::getCurrentHub()->getTransaction();
        $sentryTransaction?->setName($event->getOperationName());

        $authUser = $this->security->getUser();
        if ($authUser instanceof User) {
            configureScope(function (Scope $scope) use ($authUser): void {
                $scope->setUser([
                    'id' => $authUser->getId(),
                    'email' => $authUser->getUserIdentifier(),
                ]);
            });
        }

        try {
            addBreadcrumb(
                new Breadcrumb(
                    Breadcrumb::LEVEL_INFO,
                    Breadcrumb::TYPE_DEFAULT,
                    'GraphQL',
                    'query',
                    [
                        'userId' => $authUser?->getId(),
                        'operationName' => $event->getOperationName(),
                        'query' => $event->getRequestString(),
                        'variables' => json_encode($event->getVariableValue(), JSON_THROW_ON_ERROR),
                    ]
                )
            );
        } catch (\JsonException) {
        }
    }
}

a v config/services.yaml si jej zaregistrujeme tak, aby byl volán pro výše uvedenou událost

App\EventListener\GraphQLListener:
    tags:
        - { name: kernel.event_listener, event: graphql.pre_executor, method: onPreExecutor }

Tímto jednoduchým rozšířením máme jednotlivé transakce

  • pojmenované dle volané query - viz $sentryTransaction?->setName(...)
  • doplněné o informaci o přihlášeném uživateli - viz $scope->setUser(...)
  • a v breadcrumbs k celé transakci vidíme i přesné znění query a použité proměnné - viz addBreadcrumb(...)

Možností konfigurací je mnohem více, pro další konkrétní informace doporučuji si projít oficiální dokumentaci

Nakonec zmíním důležitou informaci plynoucí z podstaty PHP a jeho synchronnosti. Veškeré měření a komunikace se Sentry (nejspíše ve většině případů) probíhá ve stejném procesu jako zpracování měřeného requestu. Z toho plyn ovlivnění rychlosti zpracování vlastního requestu. Sentry nabízí řešení, které tento vliv snižuje téměř k nule - vytvoření lokální proxy skrze Relay, viz. opět oficiální dokumentace.

Mohlo by vás také zajímat

PHP8.1, Enum třídy a Doctrine

Jednou z novinek PHP8.1 je i podpora Enum tříd. Tento zápisek vznikl jako stručné intro k tomu, jak lze tuto novinku používat ve spojení s Doctrine.

Číst více

Symfony, lokální server a důvěryhodný SSL certifikát v Chrome

Nastavte si důvěryhodný certifikát v Chrome i pro vývoj Symfony aplikací

Číst více

NGINX, PHP-FPM a memory_limit

Nastavení memory limitu pro PHP v konfiguraci NGINX a PHP-FPM

Číst více

nám. Republiky 28
301 00 Plzeň
Česká republika
IČ: 28006402
DIČ: CZ28006402

© 2002 - 2022 iD-SIGN BRANDS MENTIONED ABOVE ARE PROPERTY OF THEIR RESPECTIVE OWNER.