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

エラー時にログに出力する情報と画面に表示する情報を分ける #LaravelTokyo

エラー時にログに出力する情報と画面に表示する情報を分ける #LaravelTokyo

2019-05-22 開催の「Laravel Meetup Tokyo Vol.12」におけるLT資料です

https://laravel-meetup-tokyo.connpass.com/event/124314

Shohei Okada

May 22, 2019
Tweet

More Decks by Shohei Okada

Other Decks in Programming

Transcript

  1. Τϥʔ࣌ʹϩάʹग़ྗ͢Δ৘ใ
    ͱը໘ʹදࣔ͢Δ৘ใΛ෼͚Δ
    okashoi@Laravel Meetup Tokyo Vol.12

    View Slide

  2. ͓·͑ͩΕʁ
    • Ԭా ਖ਼ฏʗ͓͔͠ΐ͍
    • גࣜձࣾ΢Οϧήʔτ
    • ΞʔΩςΫτ ݉ ٕज़޿ใ ← New!!
    • ٕज़ॻయ 6 Ͱٕज़ಉਓࢽσϏϡʔͨ͠
    • ͜ͷ LT ͸ͦͷ 1 খઅΑΓ
    • ൓Ԡ͋Δͱخ͍͠Ͱ͢🙇 #LaravelTokyo
    !2

    View Slide

  3. ΍Γ͍ͨ͜ͱ
    !3

    View Slide

  4. ΍Γ͍ͨ͜ͱ
    !4
    Τϥʔʢ˞ʣൃੜ࣌
    ˞ΞϓϦέʔγϣϯίʔυͰϋϯυϦϯά͞Εͳ͔ͬͨྫ֎

    View Slide

  5. ΍Γ͍ͨ͜ͱ
    !5
    Τϥʔʢ˞ʣൃੜ࣌ ϨεϙϯεͱϩάͷϝοηʔδΛ

    ͦΕͧΕҟͳΔ΋ͷʹ͍ͨ͠
    ˞ΞϓϦέʔγϣϯίʔυͰϋϯυϦϯά͞Εͳ͔ͬͨྫ֎

    View Slide

  6. ϕʔεͱͳͬͨΞΠσΞ
    https://speakerdeck.com/suzuken/phpcon2017
    !6

    View Slide

  7. ͳΜͰ΍Γ͍ͨͷʁ
    • Ϣʔβ޲͚ը໘ʹγεςϜʹؔ͢ΔϝοηʔδΛදࣔͯ͠͠·͏
    • Ϣʔβͷؔ৺͝ͱͰ͸ͳ͍ʗແ༻ͳࠞཚΛੜΉ
    • ηΩϡϦςΟతͳϦεΫ͕͋Δ
    • ϩάʹϢʔβ޲͚ϝοηʔδ͔͠ग़ྗ͞Ε͍ͯͳ͍
    • ໰୊Λղফ͢Δख͕͔ΓʹͳΒͳ͍ʢʮΤϥʔ͕ൃੜ͠·ͨ͠ʯʣ
    !7

    View Slide

  8. ࣮ݱํ๏Λͻͱ͜ͱͰ·ͱΊΔͱ
    ಠࣗྫ֎ʹɺࣗ਎Λ HttpException ΁ม׵͢ΔϝιουΛͭ͘Δͱָ😄
    !8

    View Slide

  9. ΞδΣϯμ
    • ΍Γ͍ͨ͜ͱ ←DONE
    • σϑΥϧτͷΤϥʔϋϯυϦϯάͷڍಈ
    • ࣮ݱख๏
    !9

    View Slide

  10. ΞδΣϯμ
    • ΍Γ͍ͨ͜ͱ
    • σϑΥϧτͷΤϥʔϋϯυϦϯάͷڍಈ
    • ࣮ݱख๏
    !10

    View Slide

  11. • App\Exceptions\Handler ʹྫ֎ϋϯυϦϯάॲཧ͕ॻ͚Δ
    • ϩΪϯάʹؔ͢Δڍಈ report()
    • HTTP Ϩεϙϯεʹؔ͢Δڍಈ render()
    • جఈΫϥε͸ Illuminate\Foundation\Exceptions\Handler

    View Slide

  12. • App\Exceptions\Handler ʹྫ֎ϋϯυϦϯάॲཧ͕ॻ͚Δ
    • ϩΪϯάʹؔ͢Δڍಈ report()
    • HTTP Ϩεϙϯεʹؔ͢Δڍಈ render()
    • جఈΫϥε͸ Illuminate\Foundation\Exceptions\Handler

    View Slide

  13. Illuminate\Foundation\Exceptions\Handler::render()
    !13
    /**
    * Render an exception into a response.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Exception $e
    * @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
    */
    public function render($request, Exception $e)
    {
    if (method_exists($e, 'render') && $response = $e->render($request)) {
    return Router::toResponse($request, $response);
    } elseif ($e instanceof Responsable) {
    return $e->toResponse($request);
    }
    $e = $this->prepareException($e);
    if ($e instanceof HttpResponseException) {
    return $e->getResponse();
    } elseif ($e instanceof AuthenticationException) {
    return $this->unauthenticated($request, $e);
    } elseif ($e instanceof ValidationException) {
    return $this->convertValidationExceptionToResponse($e, $request);
    }
    return $request->expectsJson()
    ? $this->prepareJsonResponse($request, $e)
    : $this->prepareResponse($request, $e);
    }

    View Slide

  14. Illuminate\Foundation\Exceptions\Handler::render()
    !14
    /**
    * Render an exception into a response.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Exception $e
    * @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
    */
    public function render($request, Exception $e)
    {
    if (method_exists($e, 'render') && $response = $e->render($request)) {
    return Router::toResponse($request, $response);
    } elseif ($e instanceof Responsable) {
    return $e->toResponse($request);
    }
    $e = $this->prepareException($e);
    if ($e instanceof HttpResponseException) {
    return $e->getResponse();
    } elseif ($e instanceof AuthenticationException) {
    return $this->unauthenticated($request, $e);
    } elseif ($e instanceof ValidationException) {
    return $this->convertValidationExceptionToResponse($e, $request);
    }
    return $request->expectsJson()
    ? $this->prepareJsonResponse($request, $e)
    : $this->prepareResponse($request, $e);
    }
    ྫ֎ࣗ৴͕ render ՄೳͳΒ༏ઌతʹͦΕΛར༻

    View Slide

  15. Illuminate\Foundation\Exceptions\Handler::render()
    !15
    /**
    * Render an exception into a response.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Exception $e
    * @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
    */
    public function render($request, Exception $e)
    {
    if (method_exists($e, 'render') && $response = $e->render($request)) {
    return Router::toResponse($request, $response);
    } elseif ($e instanceof Responsable) {
    return $e->toResponse($request);
    }
    $e = $this->prepareException($e);
    if ($e instanceof HttpResponseException) {
    return $e->getResponse();
    } elseif ($e instanceof AuthenticationException) {
    return $this->unauthenticated($request, $e);
    } elseif ($e instanceof ValidationException) {
    return $this->convertValidationExceptionToResponse($e, $request);
    }
    return $request->expectsJson()
    ? $this->prepareJsonResponse($request, $e)
    : $this->prepareResponse($request, $e);
    }
    ಛఆͷྫ֎Λผͷྫ֎ʹม׵

    View Slide

  16. Illuminate\Foundation\Exceptions\Handler::render()
    !16
    /**
    * Render an exception into a response.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Exception $e
    * @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
    */
    public function render($request, Exception $e)
    {
    if (method_exists($e, 'render') && $response = $e->render($request)) {
    return Router::toResponse($request, $response);
    } elseif ($e instanceof Responsable) {
    return $e->toResponse($request);
    }
    $e = $this->prepareException($e);
    if ($e instanceof HttpResponseException) {
    return $e->getResponse();
    } elseif ($e instanceof AuthenticationException) {
    return $this->unauthenticated($request, $e);
    } elseif ($e instanceof ValidationException) {
    return $this->convertValidationExceptionToResponse($e, $request);
    }
    return $request->expectsJson()
    ? $this->prepareJsonResponse($request, $e)
    : $this->prepareResponse($request, $e);
    }
    ಛఆͷྫ֎ʹ͍ͭͯઐ༻ͷॲཧΛͯ͠ऴྃ

    View Slide

  17. Illuminate\Foundation\Exceptions\Handler::render()
    !17
    /**
    * Render an exception into a response.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Exception $e
    * @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
    */
    public function render($request, Exception $e)
    {
    if (method_exists($e, 'render') && $response = $e->render($request)) {
    return Router::toResponse($request, $response);
    } elseif ($e instanceof Responsable) {
    return $e->toResponse($request);
    }
    $e = $this->prepareException($e);
    if ($e instanceof HttpResponseException) {
    return $e->getResponse();
    } elseif ($e instanceof AuthenticationException) {
    return $this->unauthenticated($request, $e);
    } elseif ($e instanceof ValidationException) {
    return $this->convertValidationExceptionToResponse($e, $request);
    }
    return $request->expectsJson()
    ? $this->prepareJsonResponse($request, $e)
    : $this->prepareResponse($request, $e);
    }
    ͦΕҎ֎͸ྫ֎ΛΤϥʔϨεϙϯεʹม׵
    ʢContet-Type ΍σόοάϞʔυͷ ON/OFF ౳ʹΑΔ੍ޚʣ

    View Slide

  18. ΞδΣϯμ
    • ΍Γ͍ͨ͜ͱ
    • σϑΥϧτͷΤϥʔϋϯυϦϯάͷڍಈ
    • ࣮ݱख๏
    !18

    View Slide

  19. ಠࣗྫ֎ΛఆٛʢҰྫʣ
    !19
    namespace App\Exceptions;
    /**
    * Class MyAppException
    * @package App\Exceptions
    */
    abstract class MyAppException extends \Exception
    {
    /**
    * @return int HTTP ステータスコード
    */
    abstract public function getStatusCode(): int;
    /**
    * @return string ユーザ向けのメッセージ
    */
    abstract public function getUserMessage(): string;
    }

    View Slide

  20. ಠࣗྫ֎ΛఆٛʢҰྫʣ
    !20
    namespace App\Exceptions;
    /**
    * Class MyAppException
    * @package App\Exceptions
    */
    abstract class MyAppException extends \Exception
    {
    /**
    * @return int HTTP ステータスコード
    */
    abstract public function getStatusCode(): int;
    /**
    * @return string ユーザ向けのメッセージ
    */
    abstract public function getUserMessage(): string;
    }
    HTTP Ϩεϙϯεੜ੒ʹ༻͍Δ

    View Slide

  21. ಠࣗྫ֎ΛఆٛʢҰྫʣ
    !21
    namespace App\Exceptions;
    /**
    * Class MyAppException
    * @package App\Exceptions
    */
    abstract class MyAppException extends \Exception
    {
    /**
    * @return int HTTP ステータスコード
    */
    abstract public function getStatusCode(): int;
    /**
    * @return string ユーザ向けのメッセージ
    */
    abstract public function getUserMessage(): string;
    }
    ͜ΕΛܧঝ͠ɺ
    ఆٛͨ͠ΤϥʔʹରԠ͢Δྫ֎Λ࣮૷͢Δ

    View Slide

  22. App\Exceptions\Handler ΛΧελϚΠζ
    • ࠓճ͸ report() ͸ͦͷ··Ͱ 🙆
    • Τϥʔ࣌ͷ HTTP ϨεϙϯεΛ

    ಠࣗྫ֎ͷ getStatusCode() ͱ getUserMessage() Λ࢖ͬͯੜ੒͍ͨ͠
    • → ͪΐͬͱָ͢Δํ๏Λ͝঺հ
    !22

    View Slide

  23. Illuminate\Foundation\Exceptions\Handler::render()
    !23
    /**
    * Render an exception into a response.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Exception $e
    * @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
    */
    public function render($request, Exception $e)
    {
    if (method_exists($e, 'render') && $response = $e->render($request)) {
    return Router::toResponse($request, $response);
    } elseif ($e instanceof Responsable) {
    return $e->toResponse($request);
    }
    $e = $this->prepareException($e);
    if ($e instanceof HttpResponseException) {
    return $e->getResponse();
    } elseif ($e instanceof AuthenticationException) {
    return $this->unauthenticated($request, $e);
    } elseif ($e instanceof ValidationException) {
    return $this->convertValidationExceptionToResponse($e, $request);
    }
    return $request->expectsJson()
    ? $this->prepareJsonResponse($request, $e)
    : $this->prepareResponse($request, $e);
    }
    ࠶ܝ

    View Slide

  24. Illuminate\Foundation\Exceptions\Handler::render()
    !24
    /**
    * Render an exception into a response.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Exception $e
    * @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
    */
    public function render($request, Exception $e)
    {
    if (method_exists($e, 'render') && $response = $e->render($request)) {
    return Router::toResponse($request, $response);
    } elseif ($e instanceof Responsable) {
    return $e->toResponse($request);
    }
    $e = $this->prepareException($e);
    if ($e instanceof HttpResponseException) {
    return $e->getResponse();
    } elseif ($e instanceof AuthenticationException) {
    return $this->unauthenticated($request, $e);
    } elseif ($e instanceof ValidationException) {
    return $this->convertValidationExceptionToResponse($e, $request);
    }
    return $request->expectsJson()
    ? $this->prepareJsonResponse($request, $e)
    : $this->prepareResponse($request, $e);
    }
    ࠶ܝ

    View Slide

  25. Illuminate\Foundation\Exceptions\Handler::prepareResponse()
    !25
    /**
    * Prepare a response for the given exception.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Exception $e
    * @return \Symfony\Component\HttpFoundation\Response
    */
    protected function prepareResponse($request, Exception $e)
    {
    if (! $this->isHttpException($e) && config('app.debug')) {
    return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e);
    }
    if (! $this->isHttpException($e)) {
    $e = new HttpException(500, $e->getMessage());
    }
    return $this->toIlluminateResponse(
    $this->renderHttpException($e), $e
    );
    }

    View Slide

  26. Illuminate\Foundation\Exceptions\Handler::prepareResponse()
    !26
    /**
    * Prepare a response for the given exception.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Exception $e
    * @return \Symfony\Component\HttpFoundation\Response
    */
    protected function prepareResponse($request, Exception $e)
    {
    if (! $this->isHttpException($e) && config('app.debug')) {
    return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e);
    }
    if (! $this->isHttpException($e)) {
    $e = new HttpException(500, $e->getMessage());
    }
    return $this->toIlluminateResponse(
    $this->renderHttpException($e), $e
    );
    }
    Symfony\Component\HttpKernel\Exception\HttpException Λ

    Α͠ͳʹ HTTP Ϩεϙϯεʹม׵ͯ͘͠ΕΔ

    View Slide

  27. ಠࣗྫ֎Λ HttpException ʹ

    ม׵Ͱ͖Δͱָ😄
    !27

    View Slide

  28. ಠࣗྫ֎Λ HttpException ʹม׵Ͱ͖Δͱָ😄
    !28
    namespace App\Exceptions;
    use Symfony\Component\HttpKernel\Exception\HttpException;
    /**
    * Class MyAppException
    * @package App\Exceptions
    */
    abstract class MyAppException extends \Exception
    {
    /**
    * @return int HTTP ステータスコード
    */
    abstract public function getStatusCode(): int;
    /**
    * @return string ユーザ向けのメッセージ
    */
    abstract public function getUserMessage(): string;
    /**
    * @return HttpException
    */
    public function toHttpException(): HttpException
    {
    return new HttpException(
    $this->getStatusCode(),
    $this->getUserMessage(),
    $this->getPrevious(),
    [],
    $this->code
    );
    }
    }
    ௥Ճ
    ※આ໌͸ল͖·͕͢ JSON Ϩεϙϯεͷํ΋͏·͍͖͘·͢

    View Slide

  29. App\Exceptions\Handler ΛΧελϚΠζ
    !29
    /**
    * Render an exception into an HTTP response.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Exception $exception
    * @return \Illuminate\Http\Response
    */
    public function render($request, Exception $exception)
    {
    // 既存の render の仕組みを活用するため、HttpException に変換する
    if ($exception instanceof MyAppException) {
    $exception = $exception->toHttpException();
    }
    return parent::render($request, $exception);
    }
    ௥Ճ

    View Slide

  30. ࢖͍ํ
    !30
    namespace App\Exceptions;
    use Throwable;
    /**
    * Class HogeException
    * @package App\Exceptions
    */
    abstract class HogeException extends MyAppException
    {
    /**
    * @return int HTTP ステータスコード
    */
    public function getStatusCode(): int
    {
    return 500;
    }
    /**
    * @return string ユーザ向けのメッセージ
    */
    public function getUserMessage(): string
    {
    return 'ごめん';
    }
    }
    • ಠࣗྫ֎ͷ۩৅ΫϥεΛఆٛ
    • throw new HogeException('エラーです');

    ͷΑ͏ʹͯ͠ྫ֎Λૹग़
    • ϩάʹ͸ϩά༻ͷϝοηʔδ͕ه࿥͞ΕΔ
    • Blade ςϯϓϨʔτ಺Ͱ͸

    $exception->getMessage()

    ͰHTTP Ϩεϙϯε༻ͷϝοηʔδʹ

    ΞΫηεՄೳ

    View Slide

  31. ·ͱΊ
    ಠࣗྫ֎ʹɺࣗ਎Λ HttpException ΁ม׵͢ΔϝιουΛͭ͘Δͱָ😄
    !31

    View Slide

  32. ⾠஫ҙ
    • ʮྫ֎ΛͲ͏࢖͏͔ʯʹ͍ͭͯ͸͍Ζ͍Ζͳݟղ͕͋Δͱࢥ͍·͢
    • Laravel Ͱ͸ 404 ౳ͷΫϥΠΞϯτΤϥʔʹ΋ྫ֎Λ࢖͍ͬͯΔͷͰ

    ͦΕʹͷͬͱͬͯྫ֎Λ࢖͏ͱ͍͏ํ๏Λ࠾༻͠·ͨ͠
    !32

    View Slide

  33. આ໌ͨ͠ιʔείʔυ +α ͸Լه URL ʹ΋ࡌ͍ͤͯ·͢
    https://github.com/okashoi/colab-techbook6-example
    !33

    View Slide

  34. !34

    View Slide

  35. ʢ෇࿥ʣฤूͷաఔͰল͍ͨ

    εϥΠυ܊
    !35

    View Slide

  36. Illuminate\Foundation\Exceptions\Handler::report()
    !36
    /**
    * Report or log an exception.
    *
    * @param \Exception $e
    * @return mixed
    *
    * @throws \Exception
    */
    public function report(Exception $e)
    {
    if ($this->shouldntReport($e)) {
    return;
    }
    if (is_callable($reportCallable = [$e, 'report'])) {
    return $this->container->call($reportCallable);
    }
    try {
    $logger = $this->container->make(LoggerInterface::class);
    } catch (Exception $ex) {
    throw $e;
    }
    $logger->error(
    $e->getMessage(),
    array_merge($this->context(), ['exception' => $e]
    ));
    }

    View Slide

  37. Illuminate\Foundation\Exceptions\Handler::report()
    !37
    /**
    * Report or log an exception.
    *
    * @param \Exception $e
    * @return mixed
    *
    * @throws \Exception
    */
    public function report(Exception $e)
    {
    if ($this->shouldntReport($e)) {
    return;
    }
    if (is_callable($reportCallable = [$e, 'report'])) {
    return $this->container->call($reportCallable);
    }
    try {
    $logger = $this->container->make(LoggerInterface::class);
    } catch (Exception $ex) {
    throw $e;
    }
    $logger->error(
    $e->getMessage(),
    array_merge($this->context(), ['exception' => $e]
    ));
    }
    ࢦఆ͞Εͨྫ֎ʹ͍ͭͯ͸Կ΋ͤͣʹऴྃ

    View Slide

  38. Illuminate\Foundation\Exceptions\Handler::report()
    !38
    /**
    * Report or log an exception.
    *
    * @param \Exception $e
    * @return mixed
    *
    * @throws \Exception
    */
    public function report(Exception $e)
    {
    if ($this->shouldntReport($e)) {
    return;
    }
    if (is_callable($reportCallable = [$e, 'report'])) {
    return $this->container->call($reportCallable);
    }
    try {
    $logger = $this->container->make(LoggerInterface::class);
    } catch (Exception $ex) {
    throw $e;
    }
    $logger->error(
    $e->getMessage(),
    array_merge($this->context(), ['exception' => $e]
    ));
    }
    ྫ֎ࣗ৴͕ report() ϝιουΛ͍࣋ͬͯͨΒ
    ༏ઌతʹͦΕΛར༻

    View Slide

  39. Illuminate\Foundation\Exceptions\Handler::report()
    !39
    /**
    * Report or log an exception.
    *
    * @param \Exception $e
    * @return mixed
    *
    * @throws \Exception
    */
    public function report(Exception $e)
    {
    if ($this->shouldntReport($e)) {
    return;
    }
    if (is_callable($reportCallable = [$e, 'report'])) {
    return $this->container->call($reportCallable);
    }
    try {
    $logger = $this->container->make(LoggerInterface::class);
    } catch (Exception $ex) {
    throw $e;
    }
    $logger->error(
    $e->getMessage(),
    array_merge($this->context(), ['exception' => $e]
    ));
    }
    ͦΕҎ֎͸ྫ֎ͷϝοηʔδͱ
    context Λϩάग़ྗ

    View Slide