第4回定期勉強会「Flutter勉強会」

こんにちは。 エキサイトの三浦です。

毎月恒例の定期勉強会が今月も開催されました。 今回は「Flutter勉強会」ということで、エキサイトでも使われ始めたFlutterの話になります。

なお過去開催分は以下になりますので、よければ御覧ください。

tech.excite.co.jp

tech.excite.co.jp

tech.excite.co.jp

Flutter勉強会

Flutterは、公式サイトで以下のように紹介されています。

Flutter is Google's UI toolkit for building beautiful, natively compiled applications for mobile, web, desktop, and embedded devices from a single codebase.

端的に言えば、「単一コードからモバイルアプリやwebなど様々なデバイス用のアプリケーションを生成できるフレームワーク」です。 その利便性から徐々にいろいろなサービスに取り入れられていっており、エキサイトでも導入され始めています。

今回は実際にエキサイト内でFlutterに触っているエンジニア3名に、

  • Flutterとは何か
  • どんな特徴があるのか
  • 触ってみた所感

を発表してもらいました。

Flutter自体の説明は以上の通りで、その利便性は言うまでもないですが、実際に使っているからこそわかる

  • 具体的なディレクトリ構造やコードはどうなっているのか
  • Swiftなどのネイティブコードの開発と比べた時のメリット・デメリット
  • 広告など、実際にアプリケーションコードを書く上で必要になってくる要素をどのように実装すべきなのか

などを教えてもらうことができました。

単に「Flutterっていいよね」だけではなく、こんな面倒な点がある、デメリットがあるというところも発表されていて、Flutterを技術選定の選択肢に入れる上で有意義な勉強会になったと感じています。 こうした生の声は、導入しはじめの技術では特に共有のメリットが大きいので、Flutter以外の技術でもぜひ勉強会をしていきたいところです。

最後に

定期勉強会は第4回目ということで、ついに四半期分をやったということになりました。 今後も半年、1年、そしてそれ以上と続いていったらと思います。

Spring Bootでコンポーネント名が重複してエラーになったときの解決策

はじめに

エキサイト株式会社 21卒 バックエンドエンジニアの山縣です。 Spring Bootでコンポーネント名が重複したときに、ConflictingBeanDefinitionExceptionが発生してエラーとなってしまいました。 その原因と解決策についてまとめました。

別のパッケージの同一のコンポーネント

例としてItemに関するItemServiceについて考えてみます。 ItemServiceはWebとアプリで処理が少し異なるため、それぞれで実装します。

  • WebのService
package com.sample.service.item;

import org.springframework.stereotype.Service;

@Service
public class ItemServiceImpl implements ItemService {
    @Override
    public Item getItemByID(Long id) {
      /* 処理 */
    }
}
  • アプリのService
package com.sample.app.service.item;

import org.springframework.stereotype.Service;

@Service
public class ItemServiceImpl implements ItemService {
    @Override
    public Item getItemByID(Long id) {
      /* アプリ固有の処理 */
    }
}

上記2つは似ていますが、パッケージが異なることに注意が必要です。 通常、同一のクラス名であったとしても、パッケージが異なれば問題なく利用できます。 しかし、ConflictingBeanDefinitionExceptionが発生しコンパイルできずにエラーとなってしまいました。

ConflictingBeanDefinitionException: 
Annotation-specified bean name 'itemServiceImpl' for bean class [com.sample.app.service.item.ItemServiceImpl] conflicts with existing,
non-compatible bean definition of same name and class [com.sample.service.item.ItemServiceImpl

原因

コンポーネントの場合、同一のクラス名が2つ以上存在するときにコンパイルできずにエラーとなってしまいます。 これは、Spring BootがDIするときに、どちらのBean名もItemServiceImplとなってしまうからです。 そのため、どちらのSpring BootがどちらのItemServiceImplをDIしてよいのか判別できなかったため、ConflictingBeanDefinitionExceptionが発生してしまいました。

解決策1:コンポーネントプレフィックスに文字列を付与する

一番単純でわかりやすいのはクラス名の前にAppAdminなどの文字列を付与することです。 これで問題なくコンパイルおよび実行することができます。

package com.sample.app.service.item;

import org.springframework.stereotype.Service;

@Service
public class AppItemServiceImpl implements AppItemService {
    @Override
    public void getItemByID(Long id) {
      /* アプリ固有の処理 */
    }
}

解決策2:アノテーションの引数に文字列を指定する

@Controller@Service@Repository@Componentなどのアノテーションの引数に独自のBean名を定義します。 これにより、同じクラス名のコンポーネントが複数存在しても問題なく実行することができるようになります。

package com.sample.service.item;

import org.springframework.stereotype.Service;

@Service("AppItemServiceImpl")
public class ItemServiceImpl implements ItemService {
    @Override
    public void getItemByID(Long id) {
      /* 処理 */
    }
}

このとき、アノテーションの引数には変数を渡すことができます。 これで管理するのもよいかもしれません(ただし面倒だとは思います)。

@Service(ComponentName.APP_SERVICE_IMPL)

おわりに

Spring Bootでコンポーネント名が重複してエラーになったときの原因と2つの解決策についてまとめました。 個人的には、命名規則を決めて、管理者用ならAdminを、アプリ用ならAppプレフィックスに付与したほうがよいのかなと考えています。 例えば、解決策2で運用したときにItemServiceIDEでファイル検索したときに、ファイルパスまで含めて見ないとたどり着けないため、少し不便に感じるからです。 ただ、どちらの実装方法も大きく異なることはないため、最終的には好みになりそうです。

どこまでアプリケーションを「完璧」にすべきか

こんにちは。 エキサイト株式会社の三浦です。

アプリケーションを作るときや改修するとき、すべてのエンジニアが「完璧なアプリケーションを作ろう」と一度は思うはずです。

しかし、シンプルなコードで済むアプリケーションならともかく、複雑になればなるほど「完璧」を達成するのは難しくなっていきます。

今回は、どこまで「完璧」を目指すべきか、持論を語っていきたいと思います。 なおこちら、あくまで持論であり、必ずしも会社の方針と100%一致するとは限らないので、あらかじめご了承下さい。

完璧なアプリケーションとは

まず最初に、「完璧なアプリケーション」とは何でしょうか? それはもちろん、「いついかなる時も問題が起きない」アプリケーションです。

例えば、「2つの値を渡すとその合計値を返す」というアプリケーションの場合、正の数・負の数・非整数でも計算が可能であるとか、(仕様次第ですが)IntegerやLong値の限界を超えても問題なく動作する、などが「完璧」の条件として挙げられます。

この程度であれば問題ありませんが、ではWebサイトであればどうでしょうか? 考えられる条件としては、

  1. サイトにアクセスすれば、必ず想定通りの情報がサイトに載っている
  2. いつサイトにアクセスしても、一定以下の時間でページが表示される

でしょうか。 一見条件も少なく、簡単に達成できるような気もしてきますが、Webサイトというのは様々な状況下に陥ることが多々あります。 例えば、

  • 急に大量のアクセスが来る
  • 外部からの攻撃を受ける
  • ごく限られた条件下でしか起きない、コード内の潜在的なバグが発火する
  • 運用上のミス等で、DBなどのデータソース内のデータに不整合が生じる
  • インフラ周りに物理的な障害が発生する
  • クラウドを使用している場合、クラウド側に問題が発生する

などが挙げられるでしょう。 これら(もちろんここで挙げていないその他様々な原因も)をすべて解決し、「完璧なアプリケーション」を作り上げることは可能なのでしょうか?

「完璧」でなく「ベストエフォート」なアプリケーションを作る

個人的には、これら全てを完全に解決することはかなり難しいと考えています。

もちろん、例えば「急に大量のアクセスが来る」の対処法として「サーバ/コンテナが自動スケールするようにする」ですとか、「外部からの攻撃を受ける」の対処法として「セキュリティ対策を万全にする」など様々な解決策はありますが、それでも自動スケールが完了するまでの数分間はページ速度の急激な低下、もしくはタイムアウトはどうしても発生してしまうでしょうし、外部からの攻撃も新しい種類のものが出てきてしまえばまた新しく対処法を考える必要が出てきます。

さらに言えば、例えばごく限られた条件でしか起きない問題に対してどこまで対応するべきでしょうか? 1年に1~2回、しかも1回あたり数秒程度しか起きない問題に対して、何日も何週間もかけて原因調査・対応をするべきなのでしょうか?

私もしばらくこういった問題に頭を悩ませてきましたが、他のエンジニアと相談したり、色々と考えた結果、「完璧」ではなく「ベストエフォート」こそ目指すべきアプリケーションなのではないか、と考えるようになりました。

アプリケーション、及びそれらの上で展開されるサービスには、もちろん譲れない点があります。 例えば、「1日6時間はエラーでサイトが見られない」などといったサービスは、いくらなんでも受け入れられないでしょう。 可能な限り、いつでも見られるようにする必要はあります。

しかし、例えば「年間で99.9%の時間のサイトの動作は保証する(0.1%の時間はエラーでサイトが見られない可能性がある)」だとしたらどうでしょう? この程度であれば許容範囲、というサービスならあるのではないでしょうか? なおこれは、1年 = 365日 = 8760時間の0.1%なので、8.7時間ほどエラーが許容される、ということになります。

考えてみると、そもそもインターネットという仕組みそのものがベストエフォートという考え方で作られています。 今回の例であるWebサイトでいえば、その上で作られているアプリケーションがベストエフォートを目指すのは、ある意味自然なのかもしれません。

また有名なAWSも、サービスレベルアグリーメントという形で、「完璧」ではなく「ベストエフォート」でサービスを作っていることを宣言しています。

もちろん、サービスの内容によってどこまで許容されるかは変わってきます。 ニュースメディアサイトと医療用のオンライン手術サービスのようなものであれば、許容されるエラーのレベルは全く違うことでしょう。

ただしなんにせよ、やみくもに「完璧」なアプリケーションを作ることを考えるのではなく、まずはそのサービスでどこまでエラーが許容されるかを考えるべき、というのはすべてのアプリケーションに共通しているのではないでしょうか。 そして、許容されるエラーレベルに対して、適切に「ベストエフォート」なアプリケーションを作っていくことが、開発速度と開発精度の両方を兼ね備えた開発方法なのかもしれません。

最後に

AWSのように非常に大きく、インフラそのものをサービスとして提供するサービスであれば、最初から「~~%までの正常動作は保証する」というように宣言をしています。 一方で、いわゆる一般的なサービス・アプリケーションであれば、暗黙的に「まれに障害が発生することもある」という認識はありつつも、どこまでエラーが許容されるかという話し合いをすることは少ないのではないでしょうか? 特に自社開発であれば、その傾向は大きなものとなるでしょう。

ですがエラーや障害というのは、そのサービスが大きくなればなるほどいつか必ず起きるものとなっていきます。 例え自社開発のサービスであっても、一度はエラー許容レベルに関して話し合って決めることで、開発速度と精度のバランスを取ることができ、長期的に見てサービスの開発に良い影響を与えるのではないでしょうか。

はじめてのFlutter

iXIT株式会社の堀です。

新しい事をはじめたいと思い、グループ会社でも採用が決まったFlutterの勉強をはじめました。
2018年末にGoogleからFlutterがリリースされ、2年以上も経つのでネット上にも情報がたくさん載っています。 同じようにこれから始める人の参考になればと思い記事書かせて頂きます。(技術的要素はほぼありません)

何本か入門記事を読んだあと、環境構築。
Android StudioXcodeもすでにインストール済みだったので、あっという間に環境が出来上がりました。
最近のフレームワークは手とり足取り感満載ですね!
その後は、毎度おなじみ「Hello World」かと思いきや、用意されているサンプルアプリはカウンターアプリでした。
ソース内にコメントもいっぱい!(英語だけど)

カウンターアプリ
カウンターアプリ

Dartの入門書を見ながら、軽くソースを解析。 これだけのステップ数でスタイルもまとめて出来ちゃうなんてFlutterすごいかも。 (ただし、規定外のデザインにする時大変そうな気が・・・)

次の1歩として新しいアプリを作ろうと思い、見つけたのがこちら。

eh-career.com

解説も細かく書かれており、入門アプリにはちょうど良さそう!
早速、記事を見ながらプログラミング・・・
あれ?記事通りに書いてるのにエラーが・・・
スペルミスかなっと思いコピペ。それでもエラーが解消されないので、エラーメッセージで検索。 いっぱい出てきました。「その書き方もう使えないよ〜」ってコメントでした。 最近よく聞く「破壊的変更」ってやつですね。
下位互換はせず、新しく作り直してシンプルにする!って方針は賛成ですが、古い記事に惑わされて情報の海で溺れないように要注意です。

上記の解説ページとバージョン違いで出たエラーを潰しながら、どうにか完成。
Githubリポジトリ見てもつまらないので、Excite Tech Blogを表示できるように改良。

記事一覧アプリ
記事一覧アプリ

もうちょっとステップアップしてFirebaseと絡めたアプリを作りたいなと思っていたところ、程よいサンプルアプリ見つけました!

github.com

Github上にソースだけでなくFlowchartや利用するAPIリンクなどもキレイに纏められています。
Youtube3本立てのTutorialもあるのでこちらを見ながら実装。
(解説は英語ですが聞き取りやすい話し方ですし、コード見ながらなので分かりやすいです)
バージョン違いによるエラーを解消しつつ、Tutorial#2まで完了。

投票アプリ
投票アプリ

引き続き、完成を目指して進めて行きたいと思います。

YoutubeにはTutorialがたくさん載っています。
英語だからと諦めてしまうには勿体ないくらい素敵な動画も多いので、みなさんもぜひ挑戦してみてください!

エキサイトは「iOSDC Japan 2021」に協賛します!

今週金曜日から開催される「iOSDC Japan 2021」に、エキサイトはシルバースポンサー、Tシャツスポンサーとして協賛します。 イベント概要については、以下をご覧ください。

iOSDC Japan 2021

開催日時 : 2021年9月17日(金)〜 9月19日(日)

開催場所 : オンライン開催

公式サイト :

iosdc.jp

公式Twitter :

twitter.com

最後に

エキサイトでは一緒に働いてくれる仲間を絶賛募集しております!

また、長期インターンも歓迎していますので、ご連絡お待ちしております!!🙇‍♀️

www.wantedly.com

そして、iOSDCトークンはこちらになります!

#エキサイト

それでは皆さま、 iOSDC Japan 2021を一緒に楽しんでいきましょう!

DefaultErrorAttributesについて

エキサイト株式会社の中尾です。

RestControllerAdviceで拾えない例外処理を拾う方法を記載します。

よくある方法は、ExceptionHandlerで全てcatchする方法だと思います。

    @ExceptionHandler
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public String handleNotFoundException(Exception exception) {
        this.writeLog(exception.getStackTrace());
        return "all Exception catch";
    }

しかし、これだとResponseStatusが全てINTERNAL_SERVER_ERRORになります。 ResponseStatusを指定しない場合、ステータスOKになってしまいます。

なぜこのようなことしないといけないのでしょうか?

そもそもデフォルトのエラーレスポンスはいかになっています。

{
  "timestamp": "2021-09-04T20:25:31.581+09:00",
  "status": 404,
  "error": "Not Found",
  "trace": "org.springframework.web.servlet.NoHandlerFoundException: No handler found for GET /aaa\n\tat org.springframework.web.servlet.DispatcherServlet.noHandlerFound(DispatcherServlet.java:1278)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1041)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:626)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.servlet.resource.ResourceUrlEncodingFilter.doFilter(ResourceUrlEncodingFilter.java:67)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:94)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142)\n\tat org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.base/java.lang.Thread.run(Thread.java:834)\n",
  "message": "No handler found for GET /aaa",
  "path": "/aaa"
}

エラー情報ですぎですよね。 本番環境でこれだと困ると思います。 以下の設定を追加することで、traceログを非表示にできます。

server.error.include-message: never
server.error.include-binding-errors: never
server.error.include-stacktrace: never
server.error.include-exception: false

traceとmessageが消えました。

shogo.nakao@localhost: $ curl http://localhost:8080/aaa | jq                                                                                                    [/Users/shogo.nakao]
{
  "timestamp": "2021-09-04T20:34:27.516+09:00",
  "status": 404,
  "error": "Not Found",
  "path": "/aaa"
}

しかし、アプリケーションによって、エラーレスポンスの形は変わると思います。 アプリケーションに合わせてエラーレスポンスを変えてあげましょう。

@Component
@Profile("!local")
public class CustomErrorAttributes extends DefaultErrorAttributes {

    /**
     * error response default setting 
     *
     * @param webRequest
     * @param options
     * @return
     */
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
        final String message = this.getMessage(webRequest, this.getError(webRequest));
        Map<String, Object> customErrorAttribute = new HashMap<>();
        customErrorAttribute.put("error",
                new ErrorResponse.ErrorData()
                        .setMessage(message));
        return customErrorAttribute;
    }
}
{
  "error": {
    "message": 404
   }
}

解説します。

DefaultErrorAttributesを継承することで、デフォルトエラーレスポンスの型を変えられます。 @Profile("!local")を設定することでローカル環境以外の場合に適応させます。 ローカル環境では詳細なトレースログみたいですからね。 どっちにしてもコンソールログに詳細なログは出力されますが。

final String message = this.getMessage(webRequest, this.getError(webRequest));

こちらで、this.getMessageとthis.getErrorはDefaultErrorAttributesの実装を見てください。

RestControllerAdviceについて

エキサイト株式会社の中尾です。

SpringBootでexceptionが発生したとき、特定のエラーレスポンスで返す方法を記載します。

以下、コードになります。

@RestControllerAdvice
@Slf4j
public class ExceptionController {
    @ExceptionHandler({BadRequestException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String handleBindException(Exception exception) {
        this.writeLog(exception.getStackTrace());
        return exception.getMessage();
    }

    @ExceptionHandler({NoHandlerFoundException.class})
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public String handleNotFoundException(Exception exception) {
        this.writeLog(exception.getStackTrace());
        return "404 Not Found";
    }

    private void writeLog(StackTraceElement[] stackTraceElements){
        Arrays.stream(stackTraceElements)
                .limit(10L)
                .forEach(e -> log.error(e.toString()));
    }
}
public class BadRequestException extends RuntimeException {
    public BadRequestException(String s) {
        super(s);
    }
}
spring:
  mvc:
    throw-exception-if-no-handler-found: true
  resources:
    add-mappings: false

解説します。

RestControllerAdviceはRestControllerと違ってGetMapping/PostMappingとかは記載せず、ExceptionHandlerで該当のExceptionが発生したときResponseStatusを返します、

spring.pleiades.io

動作検証

shogo.nakao@localhost: $ curl http://localhost:8080                                                                                     [/Users/shogo.nakao]
XXXX must not be empty

404の場合、springの設定ファイルにthrow-exception-if-no-handler-found: trueとadd-mappings: falseをプロパティファイルに設定した上でRestControllerAdviceに追加してください。

shogo.nakao@localhost: $ curl http://localhost:8080/aaa                                                                                                         [/Users/shogo.nakao]
404 Not Found

うまくいきました。

なお、以下のようにExceptionHandlerになにも指定しない場合、全てのエラーをキャッチすることができます。

    @ExceptionHandler
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public String handleNotFoundException(Exception exception) {
        this.writeLog(exception.getStackTrace());
        return "all Exception catch";
    }

RestControllerAdviceには関係ありませんが、writeLogにstackTraceのエラーを10行ぐらいまで出力する設定になります。

        Arrays.stream(stackTraceElements)
                .limit(10L)
                .forEach(e -> log.error(e.toString()));