os_logは、NSLogとos_traceを置き換えたAppleのiOSおよびmacOS向け統合ロギングAPIです。古いメカニズムとは異なり、os_logはカーネルレベルで動作します。メッセージはリングバッファにバッファリングされ、アクティビティしきい値に達した場合にのみディスクに書き込まれます。Apple WWDC 2016によると、os_logはNSLogと比較してディスク負荷を10分の1に削減し、カテゴリとタイプを通じて詳細レベルの制御を提供します。これはiOS開発者にとって主要な診断ツールです。Console.appを通じて、リアルタイムでプロセス、カテゴリ、重大度レベルでメッセージをフィルタリングできます。
主要ポイント
os_logは、AppleがiOS 10およびmacOS Sierraで導入した統合ロギングAPIです。NSLog、os_trace、syslogという散在したロギングメカニズムを、XNUカーネルレベルでのバッファリングを備えた単一のシステムに統合しました。
各メッセージを同期的にディスクに書き込みスレッドをブロックするNSLogとは異なり、os_logはメモリ内の非同期リングバッファを使用します。メッセージは、アクティビティが設定されたしきい値を超えた場合、またはlog collectコマンドによってのみディスクにフラッシュされます。これにより、アプリケーションパフォーマンスへのロギングの影響を根本的に削減します。
os_logは6つの重大度レベル、subsystemとcategoryによる区別、および組み込みのプライバシーメカニズムをサポートしています。privateとしてマークされたデータはプロダクションログで自動的にマスクされ、Xcodeを介して接続した場合にのみ開発者が利用できます。
iOS 10以前は、開発者はデバッグにNSLogを、システムメッセージにsyslogを使用していました。NSLogはstderrとコンソールに書き込みましたが、非常に非効率的でした。各メッセージは同期的にディスクに書き込まれ、頻繁なロギングでUIの遅延を引き起こしていました。os_logは、バッファリングをXNUカーネルのBSD部分に移し、ディスク書き込みを非同期にすることでこの問題を解決しました。
os_logはすべてのAppleアプリケーションで使用され、AppleがiOS、macOS、tvOS、watchOS向けの唯一のロギングAPIとして推奨しています。システムおよびサードパーティのアプリケーションは、これを使用して統合データベースにメッセージを書き込みます。データベースはメモリに保存され、定期的にディスクにフラッシュされます。これらのログは、MacのConsole.appまたはターミナルのlogコマンドで分析できます。
os_logのアーキテクチャは3つの層で構成されています。ユーザー空間のクライアントサイドAPI(libsystem_trace.dylib)、XNUカーネルのリングバッファ、およびバッファを非同期にディスクにフラッシュするlogdデーモンです。
アプリケーションがos_logを呼び出すと、メッセージは数メガバイトのカーネルリングバッファにコピーされます。バッファはFIFO方式で動作し、満杯になると古いメッセージは新しいメッセージで上書きされます。logdデーモンは定期的にバッファをチェックし、ファイルシステムの保護された領域の.tracev3ファイルにメッセージを保存します。
Apple Engineeringによると、os_logの呼び出しからConsole.appにメッセージが表示されるまでの一般的な遅延は、デバイス上で1〜5秒、バッチモードでディスクにフラッシュする場合は最大60秒です。これは意図的なトレードオフです。ロギングによってアプリケーションのパフォーマンスは低下しませんが、開発者はわずかな遅延でメッセージを確認します。
// OSLogによるos_logの宣言
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
os_logのリングバッファは固定サイズで、ユーザー空間から変更できません。バッファサイズはApple Watchの256 KBからMacの4 MBまでさまざまです。アプリケーションがバッファ容量を超えるメッセージを生成すると、古いメッセージは失われます。これは大量ロギングでは想定された動作です。
すべてのメッセージを長期間収集するには、log collectコマンドを使用します。デバイス上で収集デーモンを起動し、.logarchiveを開発者のコンピュータにエクスポートします。このモードでは、バッファは上書きされず、メッセージはアーカイブに直接書き込まれます。
os_logは5つの重大度レベルをサポートしており、各レベルは異なるタイプのメッセージを担当し、システムによって異なる方法で処理されます。Defaultは常にバッファに入るメッセージのベースラインレベルです。InfoとDebugは収集プロファイルなしのプロダクションビルドでは無効になります。ErrorとFaultは常にアクティブで、データベース内で特別なフラグが付けられます。
| レベル | 意味 | デフォルトのバッファ入力 |
|---|---|---|
| Default | 診断に重要な通常メッセージ | はい |
| Info | 詳細分析のための情報メッセージ | いいえ(プロファイルのみ) |
| Debug | 開発用のデバッグメッセージ | いいえ(プロファイルのみ) |
| Error | 注意が必要なエラー | はい |
| Fault | クラッシュにつながる重大な障害 | はい |
正しい重大度レベルを選択することはパフォーマンスにとって重要です。InfoとDebugは通常モードではディスクに書き込まれないため、アプリケーションを遅くするリスクなしに豊富に使用できます。ErrorとFaultは常に保存されますが、その量は最小限に抑える必要があります。これらのメッセージはそれぞれ、追加のメタデータのため書き込み時間が増加します。
Subsystemは、reverse-DNS形式(com.example.app)のアプリケーションまたはモジュール識別子です。Categoryはsubsystem内の文字列ラベルで、network、ui、database、authなどの機能領域ごとにログをグループ化します。この階層により、各メッセージを読まずにログをフィルタリングし、各モジュールの統計情報を個別に収集できます。
Appleは、モジュールごとに1つのOSLogを定義し、そのモジュールのすべてのファイルで使用することを推奨しています。アプリケーションの異なる層(networking、UI、persistence)には、個別のカテゴリを作成する必要があります。そうすればConsole.appで、アプリケーションを再コンパイルせずにnetworkのみのログを有効にし、その他を無効にできます。
import OSLog
extension Logger {
static let network = Logger(
subsystem: "com.example.app",
category: "network"
)
static let ui = Logger(
subsystem: "com.example.app",
category: "ui"
)
}
os_logは組み込みのプライバシー制御メカニズムを提供します。フォーマット文字列内の各値は、public、private、またはauto(デフォルトの動作)としてマークできます。デフォルトでは、os_logはすべての動的文字列とオブジェクトを潜在的に機密と見なし、プロダクションログで<private>マスクに置き換えます。
これはGDPRおよびHIPAAへの準拠にとって重要です。アプリケーションがos_logを介してオートモードでユーザーのメールやカード番号をログに記録しても、実際のデータがディスクに到達することはありません。開発者は、Xcodeを介して接続している場合、または同じMacに接続されたデバイスから収集プロファイルを使用している場合にのみ、完全なメッセージを確認できます。
let email = "user@example.com"
logger.log("User login: \(email, privacy: .public)")
// プロダクションログ: "User login: "
// Xcodeデバッグ: "User login: user@example.com"
logger.log("Payment token: \(token)")
数値(Int、Double、Float)はデフォルトで公開と見なされ、マークなしで安全にログに記録できます。文字列(String、NSString、StaticString)およびオブジェクト(NSObject、CFType)はデフォルトでprivateとなり、プロダクションではマスクされます。静的文字列(フォーマット文字列内の引用符で囲まれた文字列リテラル)は常に表示されます。これらはメッセージ自体の一部であり、データではありません。
この動作は、すべてのデータが平文でログに記録されていたNSLogとは異なります。os_logへの移行により、ログを介した機密ユーザーデータの漏洩リスクが大幅に削減されます。
os_logは、高頻度ロギングにおいてNSLogより90〜95%高速です。10,000回の呼び出しをループで行うテストでは、NSLogは約2.8秒の遅延を生み出すのに対し、os_logは同じ呼び出しを0.3秒で実行します。この差は、os_logの非同期バッファリングに対するNSLogの同期ディスク書き込みによって説明されます。
Apple Performance Lab(2016)によると、NSLogを介して1秒間に20回のロギング呼び出しを行うiOSアプリケーションは、メインスレッドのブロッキングにより1秒間に5〜8のアニメーションフレームを失います。os_logでは、バッファリングが別のカーネルスレッドで行われるため、フレーム損失は発生しません。
| パラメータ | NSLog | os_log |
|---|---|---|
| 書き込みメカニズム | 同期ディスク書き込み | カーネルでの非同期バッファリング |
| 10,000回の呼び出し時間 | ~2.8秒 | ~0.3秒 |
| FPSへの影響 | 5〜8フレームの損失 | 0フレーム |
| 重大度レベル | なし | 5レベル |
| プライバシー | すべてのデータが表示 | 自動マスキング |
| フィルタリング | サポートされていません | subsystem / category / levelによる |
os_logには2つのAPIがあります。従来のCスタイルのos_log_createと、iOS 14で導入されたモダンなSwiftラッパーLoggerです。Swift LoggerはフォーマットにResultBuilderシステムを使用し、引数は明示的なプライバシーマーク付きの文字列リテラルを通じて補間されます。
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
func handleResponse(statusCode: Int) {
if statusCode > 399 {
logger.error("HTTP error: \(statusCode, privacy: .public)")
} else {
logger.info("Response OK: \(statusCode)")
}
}
log collectは、デバイスから収集したログをエクスポートするコマンドラインユーティリティです。USB経由でデバイスをMacに接続した後、ターミナルから実行します。
// .logarchiveへのログ収集
// ターミナル: log collect --device --output ./app_logs.logarchive
// subsystemログの表示: log show --subsystem com.example.app
// 動的な値でのロギング
logger.log("User \(userId) opened screen \(screenName)")
Loggerを使用する場合、引数はos_logのCバージョンのようなフォーマット文字列ではなく、String Interpolationを介して補間されることに注意することが重要です。これはより安全ですが、デフォルトの動作が開発者にとって適切でない場合は、各引数に明示的なプライバシーマークが必要です。
よくある質問
os_logはカーネル内でメッセージを非同期にバッファリングし、メインスレッドをブロックしませんが、NSLogは同期的にディスクに書き込みます。os_logは10倍高速で、5つの重大度レベルを提供し、プライベートデータを自動的にマスクします。NSLogにはこれらの機能はありません。
一時的なデバッグメッセージには.debugを使用してください。プロダクションビルドでは無効になり、ユーザーのパフォーマンスに影響しません。常に保存すべき重要なメッセージには、.defaultまたは.infoを使用してください。
XcodeのConfigure Profileから:Devices → デバイスを選択 → Open Console → Actions → Configure Profile。目的のsubsystemの収集レベルをIncludeに設定します。これにより、デバイスの最初の再起動までアクティブなプロファイルが作成されます。
はい、os_logは追加設定なしですべてのSwiftUIアプリケーションで動作します。モデルまたはView拡張機能に静的なLoggerを作成し、onChange、task、ジェスチャハンドラで使用して画面のライフサイクルを追跡します。
デフォルトでは、os_logは文字列とオブジェクトをprivateとしてマスクします。値を確認するには、補間で明示的にprivacy: .publicを指定してください。このマークがないと、プロダクションビルドでは値がマスクに置き換えられますが、Xcodeデバッグでは正常に表示されます。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。