ハイライト
Apple、Xcode 12 で統合ログ システム用の Swift ネイティブを提供
Loggerアプリがタイプセーフな文字列補間を使用してイベントをログに記録し、数値以外の機密データをデフォルトでマスクし、コンソールとlog collect再現が難しい問題を追跡します。
主要内容
Fruta のギフト カード ページには典型的なオンライン バグがあります。リストが一番下までスクロールされるとカードはサーバーから読み込まれ続けますが、ユーザーがカードをクリックすると現在の読み込みが停止します。ほとんどの場合、すべてが正常に動作しますが、ロード中にカードをクリックすると失敗することがあります。この問題はめったに発生せず、開発マシンの隣に安定して表示されないため、ブレークポイント デバッグでは状況を把握できません。 (01:19)
このセッションで Apple が提供した解決策は、主要なイベントを統合ログ システム (統合ログ) に書き込むことです。 Xcode 12 の新しい Swift API は次のとおりです。Loggerタイプはエントリであり、開発者はタスクの開始、失敗、停止、再試行などで構造化ログを書き込むことができます。システムはこれらのログを圧縮してデバイスにアーカイブし、後で使用できるようにします。log collectMac に戻り、コンソールでサブシステム、カテゴリ、タスク ID でフィルタリングします。 (02:24)
この API の焦点は、単に次のことだけではありません。print()名前を変更してください。ログ メッセージは呼び出し時点で完全には文字列に変換されません。コンパイラとログ ライブラリは型情報を保持し、フォーマットのコストはログが実際に表示されるときにのみ支払われます。実行時データもデフォルトで保護されます。文字列やオブジェクトなどの非数値型はログ内で隠され、明示的にマークされるだけです。.public値が表示されます。 (03:44)
Fruta の例では、最終的にタスク UUID に依存してタイムラインを接続します。ログには次のように表示されます。タスクはネットワーク エラーにより最初に停止され、再試行を待機しました。次に、ユーザーがギフト カードを選択すると、コードは存在しないタスクを停止しようとしたため、矛盾した状態に入りました。この結論は実際のデバイスのログから得られたものであり、同じネットワーク タイミングをローカルで再作成する必要はありません。 (07:35)
詳細
1. ロガーを作成し、クリティカル パス上に配置します。
(02:44) アクセスは 3 つのステップに分かれています。osモジュール、作成するLogger、重要なイベントが発生した場合に log メソッドを呼び出します。subsystem通常はアプリのバンドル識別子を使用します。categoryプログラム内のさまざまな機能領域を区別するために使用されます。
// Add logging to your app in three simple steps
import os
let logger = Logger(subsystem: "com.example.Fruta", category: "giftcards")
func beginTask(url: URL, handler: (Data) -> Void) {
launchTask(with: url) {
handler($0)
}
logger.log("Started a task")
}
キーポイント:
import os統合ログ API の導入。 -Logger(subsystem:category:)後続の各ログにソース情報を添付すると、コンソールはそれを使用してフィルタリングできます。 -logger.logデフォルトの通知レベルを使用します。これは、問題のトラブルシューティング時に保持する必要がある重要なイベントを記録するのに適しています。- ログは次のように書き込まれます。
launchTaskその後、ダウンロードタスクが開始されたことが確認できます。
2. プライバシーを管理しながら実行時データを記録する
(03:32)LoggerSwift 文字列補間をサポートします。タスク ID、サーバー番号、消費時間などの実行時データをログに入力し、これらの値を使用してコンソールの検索範囲を絞り込むことができます。
// Add runtime data to the log messsage using string interpolation
import os
let logger = Logger(subsystem: "com.example.Fruta", category: "giftcards")
func beginTask(url: URL, handler: (Data) -> Void) {
launchTask(with: url) {
handler($0)
}
logger.log("Started a task \(taskId)")
}
キーポイント:
\(taskId)これはロギング システムによって型付きデータとして処理され、最初に通常の文字列に変換されることはありません。- コンパイラとログ ライブラリはメッセージ構造を保持し、ログを表示するときに必要に応じてフォーマットします。
- フィルタ条件として適した ID は、この Fruta の例のタスク UUID のように安定している必要があります。
(04:28) プライバシーのデフォルトは保守的です。文字列やオブジェクトなどの非数値型はプライベート データとしてマスクされます。
logger.log("Paid with bank account \(accountNumber)")
キーポイント:
accountNumberは文字列であり、ログ出力ではプライベートとして表示されます。- このデフォルトの動作は、ログによって個人情報が公開されるのを防ぐために、ユーザーのデバイスに公開されたアプリ向けです。
- 検索を容易にするために、アカウント、電子メール アドレス、名前などの個人データを公開としてマークしないでください。
(05:01) 値自体が機密でない場合は、明示的にパブリックとしてマークできます。
logger.log("Ordered smoothie \(smoothieName, privacy: .public)")
キーポイント:
privacy: .publicコンソールとログ アーカイブに実際の値を表示させます。- 公開タグを付ける前に、データがユーザーを特定する可能性があるかどうかを判断します。
- Fruta の例では、ダウンロード タスクをまとめるためにのみ使用されるため、タスク UUID をパブリックとしてマークします。
3. ログ収集とコンソールを使用してシーンを復元します。
(05:20) ログがシステムに書き込まれた後、オペレーティング システムはログを圧縮形式でデバイスに保存します。講演では完全なコマンド ライン パラメーターは示されませんでしたが、デバイスを接続して Mac で実行するというトラブルシューティング プロセスのみが明確化されました。log collect、デバイスオプション、開始時刻、出力ファイル名を指定して、コンソールで開きます。.logarchive。
log collect
device option
start time a few minutes before the bug
output log archive file
キーポイント:
- 無関係なログの収集を避けるために、バグが最初に確認される数分前に開始時間を選択する必要があります。
- 出力結果はログアーカイブであり、ダブルクリックするとコンソールアプリで確認できます。
- アーカイブには、システム上のすべてのプロセスのログが含まれます。最初のステップは、サブシステムによって独自のアプリをフィルターすることです。
(06:42) コンソールの検索とフィルタリングは、位置決めの問題の中核です。 Fruta は、最初にバンドル ID によって独自のログをフィルターで除外し、次に失敗したタスクの UUID を追加して、この失敗に関連する少数のレコードのみを保持します。
logger.log("Starting a new task for loading cards \(currentTaskID, privacy: .public)")
logger.fault("Task \(currentTaskID, privacy: .public) is not runinng, cannot be stopped!")
logger.log("Task \(currentTaskID, privacy: .public) interrupted")
キーポイント:
- すべてのログには同じ内容が記録されます
currentTaskID, コンソールはこれを使用して、ダウンロードタスクをつなぎ合わせることができます。 -logger.faultプログラムが破損していると想定されるシナリオで使用されます。この場合、コードは実行されていないタスクを停止しようとします。 -privacy: .publicUUID をログに表示できるようにすることで、開発者は同じタスクの複数のレコードをフィルターで除外できます。
4. 適切なログ レベルを選択します。
(09:27)Loggerデバッグ、情報、通知、エラー、障害の 5 つのレベルが提供されます。レベルは、コンソール内の視覚的なキュー、永続化するかどうか、保持時間、パフォーマンス コストに影響します。エラーと障害はコンソールで強調表示されます。デバッグは最も高速ですが、永続的ではありません。
logger.debug("\(slowFunction(data))")
キーポイント:
- デバッグ レベルのメッセージは、ストリーミングされない場合は破棄されます。
- Swift コンパイラーは、デバッグ メッセージの構築に必要なコードの実行を回避するため、
slowFunction(data)ユーザーに費用はかかりません。 - その後使用する必要があります
log collect取得した主要なイベントはデバッグ レベルに配置しないでください。
(10:20) レベルを選択するときは、目的ごとにレベルを階層化できます。デバッグは開発段階の詳細用に予約されており、情報は役立つが必須ではない情報を記録し、通知は問題のトラブルシューティング時に知っておく必要があるイベントを記録し、エラーは実行中のエラーを記録し、障害は潜在的なプログラムのバグによって引き起こされる重大な状態を記録します。
logger.log("Task \(currentTaskID, privacy: .public) interrupted")
logger.fault("Task \(currentTaskID, privacy: .public) is not runinng, cannot be stopped!")
キーポイント:
logger.logデフォルトの通知レベルに対応しており、タスクの中断などの重要なプロセス イベントを記録するのに適しています。 -logger.fault最も厳しいレベルに相当し、プログラムの期待に反する状態を記録するのに適しています。- エラーと障害は一般に保持時間が長くなりますが、デバイスのストレージ容量は依然として保持期間に影響します。
5. データを読み取り、コピーできるようにログをフォーマットします。
(12:58) ログはエラーのトラブルシューティングに役立つだけでなく、軽量の統計データを収集することもできます。 Fruta は、タスク ID、ギフトカード ID、サーバー ID、経過時間を記録します。生の出力は位置がずれていると読みにくくなります。alignそしてformatログ呼び出しのコストを増加させることなく、可読性を向上させることができます。
import SwiftUI
import os
let statisticsLogger = Logger(subsystem: "com.example.Fruta", category: "statistics")
// Log statistics about communication with a server.
func logStatistics(taskID: UUID, giftCardID: String, serverID: Int, seconds: Double) {
statisticsLogger.log("\(taskID) \(giftCardID, align: .left(columns: GiftCard.maxIDLength)) \(serverID) \(seconds, format: .fixed(precision: 2))")
}
キーポイント:
align: .left(columns:)列内で読みやすくするために、ギフト カード ID が同じ幅になるように修正されました。 -format: .fixed(precision: 2)無駄な精度を減らすために、所要時間は小数点以下 2 桁までにしてください。- 整列されたログは、Option キーを使用して列を選択し、分析のために Numbers に貼り付けることができます。
- この例では、グラフから、遅いタスクはすべてサーバー 3 からのものであることがわかり、サーバーはメンテナンスのためにオフラインになります。
(15:00) 書式設定オプションは固定小数点を超えています。 Xcode コード補完では、16 進数、8 進数、指数形式などのオプションが表示され、列幅に応じて右揃えにすることもできます。
logger.log("\(data, format: .hex, align: .right(columns: width))")
キーポイント:
format: .hexバイナリまたは数値データを 16 進数で表示するのに適しています。 -align: .right(columns:)異なる長さのデータを同じ列に配置します。- フォーマットはログ システムによって処理され、通話のログ記録のコストが増加しないことが講演で明確に述べられています。
6. ハッシュを使用して機密値をマスクしますが、マッチング機能は保持します
(15:25) ログはアプリのリリース後も継続され、デバイスとパスワードがあれば誰でも収集できます。個人情報は公開マークを付けてはいけません。 2 つのログが同じ値を指しているかどうかを判断する必要があるシナリオが発生した場合は、等価性保持ハッシュを使用できます。
logger.log("Paid with bank account: \(accountNumber, privacy: .private(mask: .hash))")
キーポイント:
.private(mask: .hash)銀行口座番号の原文は表示されません。- 同じアカウントは同じハッシュを取得するため、開発者は 2 つのログが関連しているかどうかを判断できます。
- この方法は、同じ機密値をフィルタリングおよび集約するのには適していますが、人間が閲覧できるように機密コンテンツを復元する場合には適していません。
重要ポイント
非同期タスクのログ タイムラインを作成します
作業内容: ネットワーク リクエスト、バックグラウンド ダウンロード、同期タスクのタスク ID を生成し、開始、再試行、キャンセル、完了、失敗時にログを書き込みます。
実行する価値がある理由: Fruta のバグは、ネットワーク エラーとユーザーのクリックのタイミングに依存しています。タスク UUID を使用すると、コンソールは同じタスク内のイベントのシーケンスを復元できます。
開始方法: 作成するLogger(subsystem:category:)、タスクのエントリごとに生成されますUUID、使用privacy: .publicこの ID を機密性のない意味として記録します。
プロジェクト契約書にログレベルを書き込みます
やるべきこと: チームのデバッグ、情報、通知、エラー、および障害の使用境界を定義します。
実行する価値がある理由: レベルによって、後で収集できるかどうかが決まり、また、コンソールのハイライト表示と保持期間も決まります。重大なエラーをデバッグとして書き込みます。問題発生後にログが残っていない可能性があります。
開始方法: ユーザーが認識できるエラーを次のように記述します。logger.error、プログラムの仮定に反する状態を次のように書きます。logger.fault、高周波開発期間の詳細をlogger.debug。
プライバシーとセキュリティのログ監査を行う
対処方法: 名前、アカウント、注文、電子メール、位置情報、デバイス識別子のログを調べて、デフォルトでどの値をマスク、公開、またはハッシュする必要があるかを判断します。
実行する価値がある理由: セッションでは、文字列とオブジェクトがデフォルトでプライベートであることが明確に示されています。ログは、デバイスにアクセスできるユーザーが収集できます。間違っている.public個人情報が暴露されてしまいます。
開始方法: まず機密性の高い値を削除します.public;同じユーザーまたはアカウントを関連付ける必要がある場合は、代わりに を使用します。.private(mask: .hash)。
パフォーマンスのトラブルシューティング ログを直接分析可能にする
何をすべきか: サーバー番号、消費時間、リソース ID およびその他のフィールドを記録し、次を使用します。align、format出力を列に並べ替えます。
実行する価値がある理由: Fruta の例では、アライメント ログを Numbers にコピーしたところ、遅いタスクがサーバー 3 に集中しており、ログが直接診断データになっていることがわかりました。
開始方法: 固定幅フィールドで使用するalign: .left(columns:)、時間のかかる使用のためformat: .fixed(precision: 2)、コンソールで列選択を使用して表ツールにコピーします。
関連セッション
- MetricKit の新機能 - MetricKit を使用して、実際のユーザー デバイス上のパフォーマンス インジケーターと診断ペイロードを収集し、ログ以外のオンライン トラブルシューティングの手がかりを補足します。
- Xcode Organizer を使用してパフォーマンスの問題を診断する — Xcode Organizer から集約されたパフォーマンス データ、バージョン回帰、ディスク書き込み診断、スクロール スタッター メトリクスを表示します。
- 一般的な Siri エラーを解読して対処する — SiriKit マルチプロセス デバッグ タイムラインを Xcode、デバイス ログ、および os_log と結び付けます。
- プライバシーの向上による信頼の構築 — iOS 14 のプライバシー原則を理解して、どのランタイム データをログに公開できないかを判断するのに役立ちます。
- Endpoint Security アプリを構築する - macOS セキュリティ製品でシステム イベントを処理し、プライバシーに配慮したログで検出とデバッグ情報を記録します。
コメント
GitHub Issues · utterances