Lumen is an amazingly fast micro-framework by Laravel. Even though Atatus PHP APM supports major frameworks without any extra code, Lumen applications need some extra configuration.

This integration includes:

  1. Custom Middleware to set proper transaction name.
  2. Custom Exception handler to report errors.

Include AtatusLumenMiddleware.php

Save the following contents in the file app/Http/Middleware/AtatusLumenMiddleware.php

copy
icon/buttons/copy
<?php

// app/Http/Middleware/AtatusLumenMiddleware.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

/**
 * Class AtatusLumenMiddleware
 */
class AtatusLumenMiddleware
{

    /**
     * Set Custom Transaction Name
     *
     * @param Request $request
     * @param Closure $next
     */
    public function handle(Request $request, Closure $next)
    {
        $response = $next($request);

        if (extension_loaded('atatus')) {
            atatus_set_transaction_name($this->txnNameFromRequest($request));
        }

        return $response;
    }


    /**
     * Transaction name from request.
     *
     * @param Request $request
     * @return string
     */
    public function txnNameFromRequest(Request $request)
    {
        $route = $request->route();

        if (is_array($route)) {
            if (isset($route[1]) && isset($route[1]['uses'])) {
                return $route[1]['uses'];
            }
            elseif (isset($route[1]) && isset($route[1]['as'])) {
                return $route[1]['as'];
            }
        }

        return 'index.php';
    }
}

Add the atatus middleware to the app

You have to add the Atatus middleware to the app.

copy
icon/buttons/copy
<?php
// bootstrap/app.php
//...
$app->middleware([
    //...
    \App\Http\Middleware\AtatusLumenMiddleware::class,
]);

Include ChainedExceptionHandler.php

Save the following contents in the file app/Exceptions/ChainedExceptionHandler.php

copy
icon/buttons/copy
<?php

// app/Exceptions/ChainedExceptionHandler.php
namespace App\Exceptions;

use Exception;
use Illuminate\Contracts\Debug\ExceptionHandler;

/**
 * Class ChainedExceptionHandler
 * @package App\Exceptions
 */
class ChainedExceptionHandler implements ExceptionHandler
{

    /**
     * @var ExceptionHandler
     */
    private $primaryHandler;

    /**
     * @var ExceptionHandler[]
     */
    private $secondaryHandlers;


    /**
     * ChainedExceptionHandler constructor.
     *
     * @param ExceptionHandler   $primaryHandler
     * @param ExceptionHandler[] $secondaryHandlers (optional)
     */
    public function __construct(ExceptionHandler $primaryHandler, array $secondaryHandlers = [])
    {
        $this->primaryHandler    = $primaryHandler;
        $this->secondaryHandlers = $secondaryHandlers;
    }


    /**
     * @inheritdoc
     */
    public function report(Exception $e)
    {
        $this->primaryHandler->report($e);

        foreach ($this->secondaryHandlers as $handler) {
            $handler->report($e);
        }
    }


    /**
     * @inheritdoc
     */
    public function render($request, Exception $e)
    {
        return $this->primaryHandler->render($request, $e);
    }


    /**
     * @inheritdoc
     */
    public function renderForConsole($output, Exception $e)
    {
        $this->primaryHandler->renderForConsole($output, $e);
    }
}

Include AtatusExceptionHandler.php

Save the following contents in the file app/Exceptions/AtatusExceptionHandler.php

copy
icon/buttons/copy
<?php

// app/Exceptions/AtatusExceptionHandler.php
namespace App\Exceptions;

use Exception;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Class AtatusExceptionHandler
 * @package app\Exceptions
 */
class AtatusExceptionHandler implements ExceptionHandler
{

    /**
     * @var array list of class names of exceptions that should not be reported to Atatus. Defaults to the
     *            NotFoundHttpException class used for 404 requests.
     */
    protected $ignoredExceptions = [
        NotFoundHttpException::class,
    ];


    /**
     * AtatusExceptionHandler constructor.
     *
     * @param array|false $ignoredExceptions (optional) a list of exceptions to ignore, or false to use the default
     *                                       set
     */
    public function __construct($ignoredExceptions = false)
    {
        if (is_array($ignoredExceptions)) {
            $this->ignoredExceptions = $ignoredExceptions;
        }
    }


    /**
     * @inheritdoc
     */
    public function report(Exception $e)
    {
        if (!in_array(get_class($e), $this->ignoredExceptions)) {
            $this->logException($e);
        }
    }


    /**
     * @inheritdoc
     */
    public function render($request, Exception $e)
    {
    }


    /**
     * @inheritdoc
     */
    public function renderForConsole($output, Exception $e)
    {

    }


    /**
     * Logs the exception to Atatus (if the extension is loaded)
     *
     * @param Exception $ex
     */
    protected function logException(Exception $ex)
    {
        if (extension_loaded('atatus')) {
            atatus_notify_exception($ex);
        }
    }
}


Add the exception handler to the app

Replace the $app->singleton() call which registers the concrete exception handler in bootstrap/app.php with the following:

copy
icon/buttons/copy
<?php
// bootstrap/app.php
//...
$app->instance(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    new App\Exceptions\ChainedExceptionHandler(
        new Laravel\Lumen\Exceptions\Handler(),
        [new App\Exceptions\AtatusExceptionHandler()]
    )
);