Loggingについて調べてみた(Swift)


この記事を書くに至った経緯

AppleStoreに個人開発アプリを初リリースするにあたり、printをどう処理すべきか、またリリース後のデバグ処理を効果的に行うためのログどう取るべきかわからなかったため、Loggingについて調べ、備忘録用に内容をまとめてみました。

朗報:読むのがめんどくさい人は、以下で練習すれば一発でわかります!
英語苦手な人で、Chrome使っている人はアドレスバー内の翻訳ボタンを押せば日本語で読めます。

Unified loggingの使い方チュートリアル:
https://www.raywenderlich.com/605079-migrating-to-unified-logging-console-and-instruments

この記事でわかること

  1. リリース時にPrintは消すべきかどうか 
  2. PrintをLoggingを使った方法に置き換えることのメリット
  3. WWDC2016で紹介されているLoggingについてのまとめ
  4. その他Logging関連の役立つサイトを紹介

1. リリース時にPrintは残すべきか消すべきか? 

結論:

print()は削除し、代わりに必要に応じてLoggingを使用すべきだと判断しました。

理由:

1.個人情報保護
なんらかの理由でプライバシーに関わる情報をPrint文で出力している場合、個人情報流出に繋がる可能性がある。

2.パフォーマンスに影響する
print()処理ではアウトプット時に, binary dataをStringに変換しているので、処理が重くなる。

なお、以下のようにrelease build時にprint()がアウトプットされないようにすることはできるが、1、2を考慮すると必要なければ消した方が良いと考える。


func print(_ object: Any) {
    #if DEBUG
        Swift.print(object)
    #endif
}

2. Printをloggingに置き換えることのメリット

Apple documentation(1)記載のメリット:
Logメッセージを使用することで、アプリRUNの時のログを連続データとして確認することができ、特に以下の場合に有効としている。

・アプリにデバッガーをつけることができない(例:ユーザーのマシン上で問題の診断を行う場合)
・エラーが処理の途中で起きており、デバッガーでは捉えることが難しい
・あるタスクがいつ開始し、終了したのかを知りたい場合

iOS開発者Antoine v.d. SwiftLeがavanderleeの記事(2)で説明してるメリット:
・パフォーマンコストが低い
・デバイスより後からでもLogアーカイブを回収できる

プラバシー保護の観点からもメリット:
WWDC2020でも紹介されているようにLogメッセージの形式をプライバシーとパブリックで切り替えることができ、さらにデフォルトの設定で、
・Dynamic String/Collections/ArrayはPrivateデータ
・Static String/Scalers/objects はPublicデータ
と扱っているため、デッバーガーを付けずに、デバイスで記録されたログをコンソールで確認すると、Privateデータは以下の様に、と表示される。

よって、個人情報保護の観点からも、osLogを使用するメリットがあると考える。

補足:
プライバシーレベルは、%{public}@、%{private}@ syntaxをつける必要あったため、付け忘れる恐れがあったが、iOS14からは以下の様に、以下の様にprivacyをenumで設定することができる。


Logger.viewCycle.debug("User \(username, privacy: .private) logged in")

まとめ:
1.負荷が少ない
2.バグの特定がしやすくなる
3.個人情報の保護に有効な機能を備えている
4.後からログを回収できる

参考
1. AppleDocumentation Logging
2. OSLog and Unified logging as recommended by Apple
3. WWDC2020: Explore logging in Swift

3.WWDC2016で紹介されているLoggingについてのまとめ

参考:WWDC2016: Unified Logging and Activity Tracing

背景:

・Loggingは2014年に導入された。
・その後、AppleがActivity Tracingを導入、FaultとErrorの概念が形成された。

導入目的

・UserModeとkernel modeで使用できる効率的なLogメカニズムを作ること。

  1. Compress data: データを圧縮していため、パフォーマンスの面からいってもLow コスト。
  2. Deferring work and data collection :実際にLogを表示する時まで遅らせる処理をできるだけ減らし、Observer effectを防ぐ
  3. Managing log message lifecycle:わざわざコマンドを入力しなくてもいい様に、できるだけデバグに必要な情報をLoggingで集められるようにする。

*以下の画像は、WWDC2016 Unified Logging and Activity Tracingで使用のスライドのスクショです。

Log Fileのフォーマット


・ログはバイナリーデータで保存されているため、ログを確認するにはツール使用する必要がある。
・ログアーカイブ常に、ログデータは1ファイルでまとめられるため、バグレポートとして、Emailに添付して送れる。

サブシステムとカテゴリー


・ログメッセージ:サブシステムとカテゴリーに分類される。
・ツールでログメッセージを見る時、フィルターや検索をかけることができる。
・サブシステム、カテゴリーの数に限りはないため、好きなだけ作成できる。

ログ種類と保存先


種類:
ベーシックレベル:
1.Default
2.Info
3.Debug
スペシャルレベル:
1.Fault
2.Error

保存先:
・Defaultは常にEnable
・Logging systemには、ログメッセージ用のin-memory circular buffersがあり、そこにinfoデータは一時保存される。
・FaultとErrorのみDiskに保存される。
・メモリーにFault/Errorの場合のみ残して、残りはメッセージについては、最後のVersionのみDiskの保存される。

プライバシー


・Dynamic String/Collections/ArrayはPrivateデータとされている。
・Static String/ScalersはPublicデータととされている。
これにより、個人が特定できる情報のログ化を防いでいる。

FaultとErrorについて


Error:
特定のアプリケーション・ライブラリーで検知された問題を示す。なにかエラーが見つかれば、メモリーバッファーから、その処理に関連するすべてのログメッセージをDiskに書き込む。

Fault:
Errorより広い範囲(システムレベル)で発生する問題を示す。なにかエラーが見つかれば、メモリーバッファーから、その処理とそのactivityに関連するすべての処理に関するすべてのログメッセージをDiskに書き込む。

ログ保存の仕組み


・FaultとErrorはBasuc log dataとは別のログファイルにキャプチャーされ優先的にメモリが確保される。
・通常、non observer effectによる影響を受けないよう設計されているが、live log streamの場合、IPCをすべてのCallに対して行うため、影響をもろに受けるようになる。

コンソールを使ったログアーカイブの確認操作デモ

参考:WWDC2016: Unified Logging and Activity Tracingの16:00-25:00をご確認ください。

osLog APIsまとめ

OS_log_createの使用例

os_log_t log = os_log_create("サブシステム名", "カテゴリー名")

シングルトンを作成し、サブシステム、カテゴリーをつけることで、コンソールでログを確認時にカテゴリー分けやフォルターをかけられるようになる。

OS_LOG_DEFAULT:
ログのカテゴリー分けやフィルター性能が落ちるが、カテゴリーやフィルターを気にしない場合は、このコマンドでOK

Build-in Type Formatters


各パラメータのプライバシー設定


・パラメータごとにプライバシー設定できる。
・Dynamic String/Collections/ArrayはPrivateデータとされている。
・Static String/ScalersはPublicデータととされている。
これにより、個人が特定できる情報のログ化を防いでいる。

WW2016以前のログの取り方との比較


Activity API


コンソールでできること


・ライブてログを確認できる。
・ログアーカイブを開いて、アクティビティーをメインにログを追える。
・フィルター、検索できる。
・デバイスからのログを見れる。

コマンドラインツールでできること


・コンソールと同じ機能
・Stream live log messageを見れる
・付与したメッセージ付きの Stream live logをみることができる。
・ログファイル、アーカイブを表示できる。

ベストプラクティス


・デバグに必要な情報だけメッセージに含むようにする。
・Stringへの変換作業はツールで行う。変換すればするほど遅くなる。
・関数の中にos_log* APIを関数に含めないこと。
・APIをラップする必要がある場合、関数ではなく、マクロ内ですること。
・コレクションの必要なものだけのログをとること。(dictionaries, array)
・ループの中でコードを使わないこと。

いつos_log APisを使うべきか?


os_log : 問題をデバグするのにクリティカルな情報のログ取得したい時に使用する。例:数時間前の情報を必要とする時。
os_log_info: errorやfault中にキャプチャーされた追加情報を取得するとき。
os_log_debug: 開発など、多くのdebugをする必要があるとき。
os_log_error: app: でキャプチャーした追加情報
os_log_falult: systemに関する追加情報が必要な時。

4. その他Logging理解に役立つ情報

Loggingを使用してパフォーマンスを測る方法:
https://developer.apple.com/videos/play/wwdc2018/405/

Unified loggingをコンソールで使用する練習用のチュートリアル:
https://www.raywenderlich.com/605079-migrating-to-unified-logging-console-and-instruments

osLogをprintやNSlogの代替として使用することへの考察:
https://www.avanderlee.com/debugging/oslog-unified-logging/

iOS14から導入される最新のunified logging APIs:
https://developer.apple.com/videos/play/wwdc2020/10168/

iOS 14's New Logger API vs. OSLog
https://medium.com/better-programming/ios-14s-new-logger-api-vs-oslog-ef88bb2ec237