Monitoring

Edit on GitHub

First things first. Effective monitoring is required for a production e-commerce application to stay healthy and react on potential issues fast. The Spryker Cloud team does environment monitoring and adjustments as necessary (for PROD environments). But application monitoring is the responsibility of a partner or customer.

Ensure the APM tool is active

It can be NewRelic or OpenTelemetry with any supported backend (for example Grafana, DataDog, etc).

OpenTelemetry

For OpenTelemetry integration, see OpenTelemetry - Spryker Monitoring Integration.

NewRelic

For NewRelic configuration:

NewRelic instrumentation tips for Backend-Gateway

The default OOTB setup of NewRelic integration registers all Backend-Gateway transactions under a single transaction name, namely index.php. This leads to limited visibility as NewRelic aggregates and filters out slow traces only for this one transaction type, showing detailed traces only for the slowest transactions (often in the 30-60s range).

Faster transactions (10-30s range) are rarely visible in such a setup. Splitting one single transaction type (index.php) into multiple based on URL increases visibility, as NewRelic will show the slowest transactions per transaction type, same as it is for Yves/Zed/Glue apps.

Implementation steps:

  1. Create src/Pyz/Zed/Monitoring/Communication/Plugin/EventDispatcher/BackendGatewayMonitoringRequestTransactionEventDispatcherPlugin.php:
<?php

namespace Pyz\Zed\Monitoring\Communication\Plugin\EventDispatcher;

use Spryker\Zed\Monitoring\Communication\Plugin\EventDispatcher\MonitoringRequestTransactionEventDispatcherPlugin as SprykerMonitoringRequestTransactionEventDispatcherPlugin;
use Spryker\Service\Container\ContainerInterface;
use Spryker\Shared\EventDispatcher\EventDispatcherInterface;

/**
 * @method \Spryker\Zed\Monitoring\Communication\MonitoringCommunicationFactory getFactory()
 */
class BackendGatewayMonitoringRequestTransactionEventDispatcherPlugin extends SprykerMonitoringRequestTransactionEventDispatcherPlugin
{
    public function extend(EventDispatcherInterface $eventDispatcher, ContainerInterface $container): EventDispatcherInterface
    {
        // Don't call parent - we need custom behavior
        $eventDispatcher->addSubscriber(
            $this->getFactory()->createBackendGatewayControllerListener()
        );

        return $eventDispatcher;
    }
}
  1. Create src/Pyz/Zed/Monitoring/Communication/MonitoringCommunicationFactory.php:
<?php
namespace Pyz\Zed\Monitoring\Communication;

use Pyz\Zed\Monitoring\Communication\Plugin\BackendGatewayControllerListener;
use Spryker\Zed\Monitoring\Communication\MonitoringCommunicationFactory as SprykerMonitoringCommunicationFactory;

class MonitoringCommunicationFactory extends SprykerMonitoringCommunicationFactory
{
    public function createBackendGatewayControllerListener(): BackendGatewayControllerListener
    {
        return new BackendGatewayControllerListener(
            $this->getMonitoringService()
        );
    }
}
  1. Create src/Pyz/Zed/Monitoring/Communication/Plugin/BackendGatewayControllerListener.php:
<?php
namespace Pyz\Zed\Monitoring\Communication\Plugin;

use Spryker\Service\Monitoring\MonitoringServiceInterface;
use Spryker\Zed\Kernel\Communication\AbstractPlugin;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class BackendGatewayControllerListener extends AbstractPlugin implements EventSubscriberInterface
{
    public const PRIORITY = -255;

    protected $monitoringService;

    public function __construct(MonitoringServiceInterface $monitoringService)
    {
        $this->monitoringService = $monitoringService;
    }

    public function onKernelController(ControllerEvent $event): void
    {
        if (!$event->isMasterRequest()) {
            return;
        }

        $request = $event->getRequest();
        $transactionName = $this->getTransactionName($request);

        $this->monitoringService->setTransactionName($transactionName);
        $this->monitoringService->addCustomParameter('request_uri', $request->server->get('REQUEST_URI', 'n/a'));
    }

    protected function getTransactionName(Request $request): string
    {
        // Backend-gateway uses _route like "module:gateway:action"
        $route = $request->attributes->get('_route', 'n/a');
        return str_replace(':', '/', $route);
    }

    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::CONTROLLER => ['onKernelController', static::PRIORITY],
        ];
    }
}
  1. Update src/Pyz/Zed/EventDispatcher/EventDispatcherDependencyProvider.php:

Add the custom BackendGatewayMonitoringRequestTransactionEventDispatcherPlugin after MonitoringRequestTransactionEventDispatcherPlugin in \Pyz\Zed\EventDispatcher\EventDispatcherDependencyProvider::getBackendGatewayEventDispatcherPlugins().

APM is properly configured

Traces/transactions are grouped by application (Yves, Zed, Glue, etc) and by URL or command name (for example /place-order or oms:check-conditions, etc). Spryker OpenTelemetry APM has it OOTB, NewRelic has to be configured on a project level.

For NewRelic transactions grouping, see New Relic transactions grouping by queue names.

APM access

The development team has a NewRelic “Full User” account and is able to access APM metrics for a project.