WWDC Quick Look 💓 By SwiftGGTeam
Debug with structured logging

Debug with structured logging

元の動画を見る

ハイライト

Xcode 15 の新しいデバッグ コンソールはログ メッセージをコアとして採用し、オンデマンドでメタデータを表示します。トークン フィルタリングと LLDB の DWIM Print を併用すると、開発者は大量のログから問題を特定する効率を大幅に向上できます。

主要内容

デバッグするときに最も恐れていることは何ですか?コンソールはprint出力が大量に発生し、重要なログを見つけるのに時間がかかります。さらに悪いことに、本番環境に問題が発生し、ユーザーのデバイス上のログがまったく取得できなくなりました。

Xcode 15 は、再設計されたデバッグ コンソール、よりスマートな LLDB 式評価、および統合ログのベスト プラクティスの 3 つのレベルでこの問題を解決します。

デバッグ コンソールの新しいバージョン

古いバージョンのコンソールでは、各ログの前に大きな文字列のメタデータ (タイムスタンプ、プロセス ID、スレッド ID など) があり、本当に重要なメッセージの内容は後ろに押し込まれています。 (01:02)

新しいバージョンでは、デフォルトでメタデータが非表示になり、開発者が作成したメッセージ自体のみが表示されます。メタデータを表示する必要がある場合は、必要に応じて左下隅にあるオプション ボタンをクリックして開きます。単一のログを選択し、Space キーを押してクイックルックを押すと、ログを呼び出す関数名やソース コードの場所などの完全な情報がポップアップ ウィンドウに表示されます。

エラーおよび障害レベルのログは黄色と赤色の背景で強調表示されるため、重大な問題を一目で見つけることができます。

トークンのフィルタリング

フィルタリングは、新しいデバッグ コンソールの中核機能です。 (02:16)

フィルター フィールドに入力すると、オートコンプリートにより、利用可能なフィルターのディメンション (サブシステム、カテゴリ、タイプ、ライブラリなど) が表示されます。選択した後、フィルター値を入力します。複数の条件を組み合わせて使用​​できます。

手動入力に加えて、次の 3 つのショートカットがあります。

  • フィルター メニュー: 表示するログの種類 (デバッグ、情報、エラー、障害など) を直接選択します。
  • 右クリックメニュー: 特定のログに対して「類似したものを非表示」または「類似したもののみ表示」を選択すると、フィルター条件が自動的に生成されます。
  • カテゴリ フィルタリング: キーワードを入力した後、カテゴリ ディメンションを選択して、特定のモジュールに素早く焦点を当てます。

実践的な戦闘: ログを使用してバグを見つける

Session は、実際のケースを使用してこのワークフローを実証しました。ユーザーが個人情報を更新した後、変更が保存されませんでした。 (03:35)

手順は次のとおりです。

  1. 再発する問題: 表示名を変更し、ページを終了して再入力すると、変更が失われていることがわかります。
  2. デバッグ コンソールに「アカウント」と入力し、カテゴリ フィルター ディメンションを選択し、アカウント関連のログのみを保持します。
  3. 「DisplayName 変更の要求」ログを見つけてマウスを移動し、右下隅にあるソース コードの場所をクリックして、ログを発行したコードに直接ジャンプします。 4.入力してくださいsetDisplayName関数を実行すると、コードはデータベースを更新するだけで、ローカル キャッシュは更新しないことがわかりました。
  4. 修正: データベース更新後に追加account.displayName = newDisplayName

位置決めプロセス全体には 2 分もかかりません。重要なのは、フィルターが正確にヒットできるように、ログが正しい位置に書き込まれることです。

LLDB 用の DWIM 印刷

デバッグ時poそしてpどちらを使用しますか?多くの人は自分の感覚で選んでしまい、何の情報もないままメモリアドレスを取得してしまうことがよくあります。 (06:35)

Xcode 15 では、Do What I Mean Print (DWIM) が導入されています。pコマンドは式のタイプをインテリジェントに判断し、最も速い評価方法を自動的に選択するようになりました。

  • 基本型 (Int、String など) は値を直接出力します。
  • 構造体とクラスは属性リストを最初に出力します。
  • 実現しましたCustomStringConvertibleオブジェクトはカスタムの説明を出力します

poと同等ですpさらに、オブジェクト記述モードを強制しました。ほとんどの場合、使用しますpそれで十分です。

過去を思い出す必要があるppovvoexpressionframe variable複数のコマンドを待機するシナリオが 2 つに簡素化されました。

印刷から OSLog への移行

printいくつかの欠点があります。ログ レベルがない、構造化されたメタデータがない、運用環境では収集できない、パフォーマンスのオーバーヘッドが制御できないなどです。 (09:18)

OSLog はこれらの問題をすべて解決します。移行プロセスは簡単です。

  1. import OSLog2. ロガーを作成します。let logger = Logger(subsystem: "BundleID", category: "ComponentName")3.使用するlogger.info()logger.error()交換を待ちますprint

通常、サブシステムはバンドル識別子を使用し、カテゴリはクラス名またはモジュール名を使用します。このようにして、フィルタリング時にモジュールごとに直接見つけることができます。

詳細

ログレベルの選択

OSLog には、重大度の増加に応じて 5 つのレベルが用意されています。

  • debug: 開発およびデバッグ情報。デフォルトではディスクに保存されません。 -info:運転開始/完了などの一般的な情報 -notice:注意が必要なイベント、デフォルトレベル -error: エラー、調査が必要です -fault:重大な障害、システムレベルの問題
import OSLog

let logger = Logger(subsystem: "com.example.MyApp", category: "Account")

func login(password: String) -> Error? {
    var error: Error? = nil
    logger.info("Logging in user '\(username)'...")

    // ... 验证逻辑 ...

    if let error {
        logger.error("User '\(username)' failed to log in. Error: \(error)")
    } else {
        logger.notice("User '\(username)' logged in successfully.")
    }
    return error
}

キーポイント:

  • Loggerこれは値タイプであり、グローバルまたはオンデマンドで作成できます。
  • サブシステムとカテゴリは、デバッグ コンソールでのフィルタリングに使用される文字列です。
  • ログ メッセージは文字列補間をサポートしていますが、複雑なオブジェクトでは次のことが必要です。\(object, privacy: .public)プライバシーレベルの制御
  • デフォルトでは、ユーザーデータを保護するために動的文字列はプライベートとしてマークされます

バグ修正前後のコード比較

修復前setDisplayNameデータベースのみが更新され、ローカル モデルは更新されません。

public func setDisplayName(_ newDisplayName: String) {
    logger.info("Sending Request to update DisplayName")
    Database.setValueForKey(Database.Key.displayName, value: newDisplayName, forAccount: account.id)
    logger.info("Updated DisplayName to '\(newDisplayName)'")
}

修正後にローカル キャッシュの更新を追加しました:

public func setDisplayName(_ newDisplayName: String) {
    logger.info("Sending Request to update DisplayName")
    Database.setValueForKey(Database.Key.displayName, value: newDisplayName, forAccount: account.id)
    account.displayName = newDisplayName  // 新增
    logger.info("Updated DisplayName to '\(newDisplayName)'")
}

キーポイント:

  • 完全な通話リンクを形成するための操作の前後にログが書き込まれます。
  • パスlogger.info「リクエスト送信中」と「更新済み」の 2 つのステータス ポイントを記録します。
  • 修復後にLLDBを使用するp accountローカル プロパティが更新されたことを確認する

LLDB デバッグの検証

修正されたコードにブレークポイントを設定し、LLDB で検証します。

(lldb) p account
(BackyardBirdsData.Account) =0x000060000223b2a0 {
    id = 3A9FC684-8DFC-4D7D-B645-E393AEBA14EE
    joinDate = 2023-06-05 16:41:00 UTC
    displayName = "Sample Account"          // 修复前:旧值
    emailAddress = "[email protected]"
    isPremiumMember = true
}

// 执行 account.displayName = newDisplayName 后

(lldb) p account
(BackyardBirdsData.Account) =0x000060000223b2a0 {
    ...
    displayName = "Johnny Appleseed"        // 修复后:新值
    emailAddress = "[email protected]"
    ...
}

キーポイント:

  • p構造体とクラスのプロパティが自動的に展開されるようになりました
  • カスタマイズされていない場合CustomStringConvertiblepoオブジェクトアドレスのみが出力されます
  • 使用するpデフォルトのデバッグコマンドとしては、poカスタムの説明が必要な場合にのみ使用してください

本番環境のログ収集

OSLogStore API は、アプリの実行中にシステム ログを読み取ることができ、ユーザーから報告された問題の診断に使用できます。

import OSLog

do {
    let store = try OSLogStore(scope: .currentProcessIdentifier)
    let position = store.position(timeIntervalSinceLatestBoot: 0)
    let entries = try store.getEntries(at: position)

    for entry in entries {
        if let logEntry = entry as? OSLogEntryLog {
            print("[\(logEntry.subsystem)] \(logEntry.composedMessage)")
        }
    }
} catch {
    logger.error("Failed to read log store: \(error)")
}

キーポイント:

  • OSLogStoreiOS 15以降/macOS 12以降が必要です -.currentProcessIdentifier現在のプロセスのログのみを読み取ります -composedMessageすべての補間結果を含む完全なログ メッセージです。
  • 組み合わせ可能predicate特定のサブシステムまたはカテゴリでフィルタリングする

重要ポイント

1.診断性を重視したログ仕様 対策: チーム内でロギング仕様を確立し、各モジュールが独立したロガー カテゴリを使用することを要求し、主要な操作の前後にロギングを実行する必要があります。 実行する価値がある理由: 適切なログ仕様により、オンラインの問題を特定する時間を数時間から数分に短縮できます。 開始方法: アプリの起動時に、モジュール (ネットワーク、データベース、UI、アカウント) ごとに分類されたグローバル ロガーのセットを作成します。 SwiftLint ルールまたはコード レビュー チェックリストを使用して、コンプライアンスを確保します。

2.内蔵の診断レポート機能 対処法: アプリの設定に「診断ログのエクスポート」ボタンを追加します。ユーザーは問題が発生した場合、最近の操作ログを収集し、ワンクリックで開発者に送信できます。 実行する価値がある理由: OSLogStore を使用すると、実稼働環境でのログ収集が可能になり、再現が難しい口頭による説明に依存する必要がなくなります。 開始方法: 使用するOSLogStore過去 10 分間のログを読み取り、このアプリのサブシステムをフィルタリングしてテキスト ファイルにパッケージ化し、電子メールまたはアップロード インターフェイスを通じて送信します。

3.自動化されたログ アサーション テスト 対処方法: 単体テストで、クリティカル パスが予期したログを生成することを確認します。 実行する価値がある理由: ログはコードと同じように劣化する可能性があり、テストにより、リファクタリング中にログが失われないことが保証されます。 開始方法: 使用するOSLogStoreテスト実行の前後にログをキャプチャして、特定のメッセージが存在するかどうかを確認します。または、ログ ミドルウェアをカプセル化し、アサーションで使用するためにテスト モードでログをメモリ アレイに書き込みます。

4.パフォーマンス重視のサインポスト追跡 やるべきこと: を使用するos.signpost主要な操作の開始点と終了点をマークして、計測器のパフォーマンスのボトルネックを視覚的に分析します。 実行する価値がある理由: OSLog と Signpost は同じインフラストラクチャを共有しているため、ロギングとパフォーマンス分析をシームレスに接続できます。 開始方法: 使用するOSSignposter手動でタイミングを計る代わりに、ネットワークリクエスト、データベースクエリ、画像デコードなどの操作の前後に開始/終了タグを挿入します。 Instruments で Logging テンプレートを選択して、タイムラインを表示します。

関連セッション

コメント

GitHub Issues · utterances