こんにちは、@watilde です。Amplifyの開発者体験体験の向上をすべく、ツイートのウォッチやGitHubでの反応などしています。もう去年のことですが、最近はcliの改善としてcreate-react-app
のようにinitの実行時にREADMEの生成を行うPRなど作ったりしてます。参考: aws-amplify/amplify-cli#5808
この記事は英語で書いた Improve UX by observability in front-end with Amplify and QuickSight を自分で日本語に意訳してみたものです。Node学園 35時限目 オンライントライアル でも同様の内容を発表予定です。

JavaScriptは100%動いているのか
私達の作るWebアプリ・Webサイトが様々なデバイスで100%動作しているかは、実態を観測しない限り断言できません。ユーザーにJavaScriptのエラーが表示され、開発者が発生している問題を詳細に理解したいときに、エラーなどについて詳細を可視化できるAmazon QuickSight(以下QuickSight)の活用方法を最近学びました。また、筆者も開発に参加しているAWS Amplify(以下Amplify)はJavaScript開発者にとってAWSを使い始めるのに丁度よい入口に感じています。ここでは、2つのサービスを主に使用してObservabilityをフロントエンド開発に簡単に導入する方法を紹介します。
要旨
Webアプリ・サイトの開発におけるObservabilityは、ユーザー体験(UX)の低下がいつどこで発生するかを検出し、開発者が改善にかかる時間を短縮するのに役立つ原因を把握することです。 構築をするには下記の3つのステップに区切ることができます。
- ログを集める
- ログの詳細を可視化する
- ログの詳細から、改善に必要な意味合いだしを行う
最後の意味合だしを行うステップでは、情報を下記の3つのレベルに分類します。
- エラー:空白ページの原因
- 警告:50ミリ秒を超えるハングアウト
- その他:デバッグなどの情報
この記事では、上記のステップを元に、AmplifyとQuickSightを活用したフロントエンドにおけるObservability向上の基盤を実装する方法を紹介しています。Observabilityの獲得により、フロントエンド開発者が短い時間でUXの向上を行うことがやりやすくなる、と結論づけています。
コード例はこちら
https://github.com/watilde/amplify-observability-example
フロントエンドにおけるObservabilityとは
ソフトウェア開発におけるObservabilityは、文脈に応じて定義が変わる用語です。 John Porcaroは、2020年にhumioのブログにて「(re)refine observability」という記事を公開し、Observabilityの定義として以下のステートメントを紹介しました。
Observabilityとは、システムが提供するデータからシステムの内部状態を理解することです。データを調べ、何が起こったのか、なぜ起こったのか、の質問に答えられる状態を指します。
https://www.humio.com/whats-new/blog/observability-redefined
実際のプロダクト開発、特にフロントエンドでは、良いUXを提供することが多くの場合ミッションとして設定されています。上記のステートメントを、フロントエンド開発における「これの達成によりより」まで続けるならば、下記のようになるでしょう。
これの達成により、データに基づいて何を改善すべきかを推測し、より良いエクスペリエンスを短時間で提供できるようになります。
https://watilde.medium.com/improve-ux-by-observability-in-front-end-with-amplify-and-quicksight-e7083ec1913b
ソフトウェアの現在の状態を理解し、短時間で解決策を考え出すにはデータの意味合い出しをサポートするような情報を視覚化する必要があります。Syslogのログレベルは、ログを分類するためのベストプラクティスとしてよく引き合いに出されます。

JavaScriptの世界においては、情報を出力するためのコンソール関数があります。ログレベルは標準でerror, warn, infoの3つのレベルがあります。 それぞれ開発者の観点で、errorは発生したエラー情報を出力、warnは規約違反などの情報を出力、infoはデバッグ用の情報を表示するのに使われています。

では、開発者視点ではなく利用者視点でどのようなログレベルが必要でしょうか。SyslogログレベルとJavaScriptのログレベルを組み合わせると、下記のように分類することができます。
- error: ページが壊れるような緊急度の高い問題
- warn: UXの低下を引き起こす問題
- info: UXに影響を与えない情報
フロントエンドでこれらを観測する方法は次のとおりです。最初の項目「error」の場合、エラーはonerrorおよびonunhandledrejectionによってキャッチできます。 2番目の項目「warn」では、2021年5月からSEOを改善するメトリックとしても使われる予定であるWeb Vitalsを活用できます。3番目の項目「info」は主にデバッグ目的であり、UXにも影響しないため、今回は深堀りしません。
Observability向上を行うサービス
Observabilityが世に浸透にしてからしばらく経ちますが、QuickSightは、AWSを使用している際に情報の可視化を行うのによく使われているサービスです。 公式の説明文では、「スケーラブルでサーバーレス、組み込み可能な、機械学習を活用したクラウド向けのビジネスインテリジェンス (BI) 」として紹介されています。

Amazon QuickSight は、スケーラブルでサーバーレス、組み込み可能な、機械学習を活用したクラウド向けのビジネスインテリジェンス (BI) サービスです。QuickSight では、機械学習を利用したインサイトを含むインタラクティブな BI ダッシュボードを簡単に作成し、公開できます。
Amazon QuickSight
Amplifyを使ってWebアプリを構築し、QuickSightと連携
AWSには、モバイルおよびウェブアプリケーションの開発を加速するための一連のツール(OSSライブラリ、ヘッドレスCMS、管理画面)とサービス(Webアプリ・サイトのホスティング)で構成されるAmplifyと呼ばれるサービスがあります。

Amplifyはストリームデータを送ることが可能なAmazon Kinesis Data Streams(以下Kinesis Data Streams)をサポートしており、複雑な設定なしでフロントエンドからQuickSightにログを届けられます。 6つのステップで実際にログを可視化する方法を見てみましょう。
1. 前提となるソフトウェアの設定
Amplifyの公式ドキュメントには、前提条件となるソフトウェアのインストール等を紹介するチュートリアルがあります: URL。 Nader Dabitによる動画版もあるので、併せて見てみてください。
2. Reactアプリを作成し、Kinesis Data Streamsと繋げる
AmplifyはKinesis Data Streamsをサポートしています。 amplify add analysis
を実行するだけで、Kinesis Data Streamsと繋ぐことができます。
$ npx create-react-app example $ cd example $ amplify init # Put amplifyobservability as name $ amplify add analytics # Select Amazon Kinesis Stream # Stream name is "amplifyobservabilityKinesis-dev" $ amplify push
3. Kinesis Data Streamsを介して情報を送信
どのファイルがどのデバイスでエラーを起こすか、詳細を知るためにStackTrace.JSを使用してエラーメッセージを標準化し、UAParser.jsを使用してユーザーエージェント情報を標準化します。 次に、「monitorErrors.js」と「monitorWebVitals.js」というファイルを作成しましょう。
$ npm install stacktrace-js ua-parser-js $ touch src/monitorErrors.js $ touch src/monitorWebVitals.js
monitorWebVitals.jsでは、Kinesis Data Streamsとの接続情報を設定し、標準化されたエラーとユーザーエージェントを送信します。
// src/monitorErrors.js import * as StackTrace from 'stacktrace-js'; import UAParser from 'ua-parser-js'; import { Analytics, AWSKinesisProvider } from 'aws-amplify'; import awsconfig from './aws-exports'; Analytics.configure({ AWSKinesis: { region: awsconfig.aws_project_region, bufferSize: 1000, flushSize: 100, flushInterval: 5000, resendLimit: 5 } }); Analytics.addPluggable(new AWSKinesisProvider()); const parser = new UAParser(); const callback = (stackframes) => { return stackframes.map((sf) => { return sf.toString(); }).join('\n'); }; const errback = () => { return null; }; const monitorErrors = (msg, file, line, col, error) => { const stack = StackTrace.fromError(error).then(callback).catch(errback); stack.then((stringifiedStack) => { const data = { name: 'ERROR', msg: msg, url: file, line: line, column: col || null, stack: stringifiedStack, ...parser.getResult() }; Analytics.record({ data: data, streamName: 'amplifyobservabilityKinesis-dev' }, 'AWSKinesis'); }); }; export default monitorErrors;
monitorWebVitals.jsでは、Kinesis Data Streamsとの接続情報を設定し、標準化されたWebVitals情報とユーザーエージェントを送信します。
// src/monitorWebVitals.js import UAParser from 'ua-parser-js'; import { Analytics, AWSKinesisProvider } from 'aws-amplify'; import awsconfig from './aws-exports'; Analytics.configure({ AWSKinesis: { region: awsconfig.aws_project_region, bufferSize: 1000, flushSize: 100, flushInterval: 5000, resendLimit: 5 } }); Analytics.addPluggable(new AWSKinesisProvider()); const parser = new UAParser(); const monitorWebVitals = (metric) => { const data = { name: metric.name, value: metric.value, url: window.location.href, ...parser.getResult() }; Analytics.record({ data: data, streamName: 'amplifyobservabilityKinesis-dev' }, 'AWSKinesis'); }; export default monitorWebVitals;
次に、作成したmonitorモジュールをsrc/index.jsにてインポートします。
// src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import Amplify from 'aws-amplify'; import awsconfig from './aws-exports'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import monitorErrors from './monitorErrors'; import monitorWebVitals from './monitorWebVitals'; Amplify.configure(awsconfig); ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals reportWebVitals(monitorWebVitals); // To capture errors and report through Kinesis Stream // Learn more: https://docs.amplify.aws/lib/analytics/streaming/q/platform/js#installation-and-configuration window.onerror = monitorErrors; window.onunhandledrejection = monitorErrors;
4. Webアプリを公開する
下記のコマンドを実行すると、作成したReactアプリを公開することができます。
$ amplify add hosting # Select manual deployment for this time $ amplify publish
CI/CDが終了すると、アプリを表示できるようになります。

5. データストリームの構築
次のステップは、Kinesis Data StreamsからQuickSightへのデータの流れを構築することです。 Amazon S3(以下S3)にデータを保存し、S3からQuickSightにデータを届けるには少し手間がかかります。 ゴールイメージは以下のアーキテクチャです。

5.1 Kinesis Data Streamの名前を確認
AWSの管理コンソールでAmazon Kinesisを開き、サイドバーから「Data streams」を選択します。 次に、右側のパネルのData streamの名前が、実装したコードであるamplifyobservabilityKinesis-dev
と一致するかどうかを確認します。

5.2 Kinesis Data Firehoseの作成
次に、サイドバーから「Data Firehose」をクリックして、新しいKinesis Data Firehoseの配信用ストリームを作成します。 これにより、Kinesis Data Streamを介してS3にデータが保存されるようになります。

5.3 S3のバケットを確認
公開したWebアプリを少し操作して、エラーデータとWeb Vitalsデータを送信してみましょう。送信が完了したら、S3で配信されたデータを確認できます。

5.4 Glue crawlerの設定
次のステップは、S3バケットからデータを収集するクローラーを構築することです。 AWS Glue(以下Glue)を開き、前のセクションで確認したS3のバケットからデータをクローリングするために「Add crawler」を押します。

5.5 クロールされたデータを分析するためのAthenaの構築
前ステップにより、Amazon Athena(以下Athena)によるGlueを介したS3に保存されたデータを分析する準備が整いました。 新しいデータベースを作成し、それをGlueのデータに接続します。

6. QuickSightでデータを可視化
最後にQuickSightでデータを可視化。 新しいボードを作成し、以下の画像のようにAthenaとQuickSightを接続します。 WebVitalsとErrorを別々のダッシュボードとして作成してみます。


おまけ: Amplify hostingの最新機能
Amplify Hostingは最近、Amazon CloudWatchと連携したモニタリング機能を追加しました。 この機能を利用すると、サービスが機能しているかHTTPステータスを元に確認できます。併せて活用できます。

詳細については、公式ドキュメントを参照してください。
ユーザーはUX向上を加速させる貢献者
OSSの世界では、「十分な目ん玉があれば、全てのバグは洗い出される」という言葉があり、著名なOSSがなぜ高い品質を保つことが出来ているかの説明によく引用されます。この言葉と同様に、モニタリングはユーザーと一体となってプロダクション環境でのスモークテストを可能とし、品質の向上を実現するでしょう。
言い換えると、リアルタイムにJavaScriptのエラーとWeb Vitalsの観察を行えるソフトウェアは、より多くのユーザーをUX向上を行う改善プロセスに招待することが可能となるのでは、と思っています。
謝辞
- Nader Dabit for review and feedback
- Hiromi Motodera for PR on GitHub
- Koya Kimura for review and feedback
参考文献
- Exposing Private Information by Timing Web Applications: http://crypto.stanford.edu/~dabo/papers/webtiming.pdf
- User-centric performance metrics: https://web.dev/user-centric-performance-metrics/
- Free e-book: Distributed Systems Observability: https://www.humio.com/free-ebook-distributed-systems-observability
- Observability (re)defined: https://www.humio.com/whats-new/blog/observability-redefined
- RFC 5424 — The Syslog Protocol: https://tools.ietf.org/html/rfc5424
- Web Vitals: https://web.dev/vitals/
- Timing for bringing page experience to Google Search: https://developers.google.com/search/blog/2020/11/timing-for-page-experience
- Observability for Frontend Developers: https://www.swyx.io/frontend-observability/
- Conflict of Interest: author @watilde works at AWS