Explore logging in Swiftの紹介 #WWDC20

WWDC20の「Explore logging in Swift」を視聴したので、そのまとめです。
2020.06.29

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

大阪オフィスの山田です。チワワに吠えられて、腰をやりました。

WWDC20の Explore logging in Swift を視聴したので、ご紹介です。

このセッション動画は一般に公開されています。Explore logging in Swift WWDC 2020

また、記事中の画像は動画の引用です。

本記事は Apple からベータ版として公開されているドキュメントを情報源としています。 そのため、正式版と異なる情報になる可能性があります。ご留意の上、お読みください。

トークの内容

ロギングAPI(os_log)を使って、アプリのデバッグログを収集し、アプリをデバッグする実演です。 実際に、話者のRavi Kandhadai Madhavanさんが開発しているアプリ「Fruta」にて、最近追加されたギフトカード機能で時々発生するバグの調査が実演されています。

新しいLogger APIについて

Xcode12, iOS14でLogger APIが新しくなりました。このAPIを使用して、実行中のアプリで発生したイベントを記録することができます。ログはOSによってアーカイブされるため、後からデバイスを接続して取得することができます。アプリにロガーを追加するには3つのステップを実行します。

  1. osモジュールをimport
  2. Logger型のインスタンスを作成する
  3. Loggerインスタンスでメソッドをコールする

Logger型のインスタンスを生成する際には、subsystemcategoryを渡します。これらはログに付与されます。subsystemは通常であれば、bundleIdを指定すると良いです。さらにcategoryを指定することで、アプリのどの部分のログか識別するのに役に立ちます。

上記の例ではタスクのIdをメッセージに追加しています。printメソッドをコールするのと似ていますが、重要な部分で異なります。文字列への変換は重い処理なので、代わりにコンパイラとロギングライブラリが連携して、ログのメッセージに最適化された表現を生成します。そして、ログメッセージが実際に表示された時にのみ、文字列変換のコストがかかることになります。CustomStringConvertibleプロトコルに準拠した任意の型をログとして記録することができます。ただ、ログメッセージにデータを追加する場合、文字列やオブジェクトのような非数値型は、デフォルトでは改訂されます。これはログに個人情報が表示されないようにするために行われます。

例えば以下の例では、ユーザーの銀行口座番号をログに記録していますが、出力されたログでは非公開情報として編集されています。

しかし、機密情報を含まないデータはログに表示することができます。以下の下の例に示すように、オプションのパラメータprivacy.publicを渡します。

ログを取得する

OSは圧縮された形式でデイバスにログを保存します。ログを取得するにはMacのlog collectコマンドを使用します。まず、デバイスをMacに接続し、ターミナルからデバイスオプションを指定してログ収集コマンドを実行します。

収集したログはConsole.appで開くことができます。Console.appで開くと多数のメッセージが存在します。これは、システム内の全てのプロセスが記録したメッセージが含まれているためです。なので、右上のフィルタリング機能でログをフィルタリングすることで、関連するログだけを見ることができます。

log collectコマンドを使用することで、アプリの実行後にログを取得することができることがわかりました。

アプリがXcodeから起動されている場合はXcodeのコンソールにも表示されます。

ログレベルについて

ロギングAPIには5つのレベルが用意されています。レベルに対応するメソッドが用意されています。

ErrorとFaultはConsole.appで見た時に、黄色と赤色のマークで強調表示されます。ログレベルを選択する際に考慮すべきことは、永続性です。ログが永続化されるかどうかは、ログレベルに依存します。重要度の高いログほど、永続性が高くなります。Debugレベルは永続化されないため、アプリが終了した後では取得できません。Infoレベルのログは、数分前に生成された場合を除いて、永続的ではありません。他のレベルで記録されたログは永続的ですが、保存されるログの数にはストレージの制限があり、この制限を超えると古いログは削除されます。ErrorとFaultレベルのログはNoticeレベルのログよりも長く保持されます。また、ログレベルはパフォーマンスにも影響し、重要度の低いログほど高速です。

フォーマットについて

ログを読みやすいようにフォーマットする方法が用意されています。 以下の例で、タスクの識別し、取得されたギフトカードの識別し、リクエストを提供したサーバー、タスクが完了するまでにかかった時間を記録します。今回はiPhoneをMacに接続し、Xcodeからアプリを実行し、Xcodeのコンソールでログを表示します。以下の例では綺麗に並んでいないのでわかりにくいです。

ギフトカードの識別子の最大文字数を表示することで、識別子を固定幅にし、実行時間を小数点2桁に丸めます。

これらをNumbersに貼り付けて、データを可視化します。サーバー3で処理されているタスクが遅いように見えますね。

ロギングAPIは多くのフォーマットオプションを提供していてます。オプションのformatとalignパラメータを使ってデータをフォーマットすることができます。ランタイムのコストがかからないため、簡単にデータをわかりやすくすることができます。

Xcodeのコード補完を使うことで、選べるオプションを見ることができます。

プライバシーについて

先ほど述べたようにprivacyオプションを使うことでログデータの可視性を制御できます。ログに記録されたデータのプライバシーを考えることはとても重要です。アプリがユーザーの手に渡った後、デバイスへの物理的なアクセス権を持つ人がログを収集することができるためです。したがって、個人情報に.publicを設定しないことが重要です。

そこで、平等性を保持するハッシュを使用することで.publicを使用するのと同じ利点を得ることができます。以下の様に.privacyオプションのprivateにmaskするパラメータを渡して、銀行口座番号をハッシュでログに記録しています。これでデータの内容は明らかになっていませんが、ログに記録された2つの値が同じであることを知ることができ、ログのフィルタリングに役立ちます。

API availability

iOS14から使うことができます。他に、os_log()がstring interpolationsを受け付けることができます。

さいごに

渋くてかっこいいアップデートだなって思ったので、記事にしました。 ログの設計ってそもそも難しいよね。