OSLogとは
OSLogはAppleが提供する統合ログシステムのSwift APIで、macOS 11/iOS 14以降で利用できる。
print()と異なり、ログレベル・カテゴリ・プライバシー保護を備えており、本番環境でも安全に使える。
import OSLog
let logger = Logger(subsystem: "com.example.MyApp", category: "network")
logger.info("リクエスト送信: \(url)")
logger.error("エラーが発生: \(error.localizedDescription)")
subsystemにはBundle Identifier、categoryには機能名や画面名を指定する。
プライバシー保護
デフォルトでは、文字列の補間(String Interpolation)に渡した動的な値はプライバシー保護によりマスクされる。
let password = "secret123"
let username = "alice"
logger.debug("ログイン試行: \(username), パスワード: \(password)")
// ログ出力例:
// ログイン試行: <private>, パスワード: <private>
動的な値を出力したい場合は、.publicを指定する。
logger.debug("ユーザー名: \(username, privacy: .public), パスワード: \(password, privacy: .private)")
// ユーザー名: alice, パスワード: <private>
| プライバシー設定 | 動作 |
|---|---|
.auto | 数値は公開、文字列はマスク(デフォルト) |
.public | 常に出力 |
.private | 常にマスク |
.sensitive | .privateと同等だが将来的な拡張を想定 |
フォーマット指定
数値には表示フォーマットを指定できる。
let bytes = 1048576
logger.info("データサイズ: \(bytes, format: .byteCount(style: .file))")
// データサイズ: 1 MB
let value = 0xFF
logger.debug("値: \(value, format: .hex)")
// 値: 0xff
ログの確認方法
Console.appで確認する
macOSのConsole.app(/Applications/Utilities/Console.app)でリアルタイムにログを確認できる。
検索バーでsubsystem:com.example.MyAppのように絞り込める。
logコマンドで確認する
ターミナルからlogコマンドで確認する。
# リアルタイムにストリーミング
$ log stream --predicate 'subsystem == "com.example.MyApp"' --level debug
# 過去のログを検索
$ log show --predicate 'subsystem == "com.example.MyApp"' --info --debug --last 1h
ログレベルと永続化
OSLogには5段階のログレベルがある。
| レベル | メソッド | デフォルトの永続化 |
|---|---|---|
| Debug | logger.debug() | なし(デバッグ時のみ) |
| Info | logger.info() | なし |
| Notice | logger.notice() | あり |
| Error | logger.error() | あり |
| Fault | logger.fault() | あり |
debugとinfoはデフォルトでディスクに保存されない。log streamでリアルタイム確認するか、--debug/--infoオプションを付けてlog showで参照する。
OSLogStore(プログラムからログを読み取る)
OSLogStoreを使うとアプリ内からログを取得できる。クラッシュレポートや診断機能の実装に役立つ。
import OSLog
func fetchRecentLogs() throws -> [OSLogEntry] {
let store = try OSLogStore(scope: .currentProcessIdentifier)
let position = store.position(timeIntervalSinceLatestBoot: -3600) // 直近1時間
let entries = try store.getEntries(at: position)
return entries
.compactMap { $0 as? OSLogEntryLog }
.filter { $0.subsystem == "com.example.MyApp" }
}
scopeの選択肢:
| スコープ | 対象 |
|---|---|
.currentProcessIdentifier | 自プロセスのログのみ(entitlementなしで使用可能) |
.system | システム全体(com.apple.loggingentitlementが必要) |
print()との比較
| 項目 | print() | OSLog |
|---|---|---|
| リリースビルドでの出力 | あり | あり(レベルに依存) |
| ログレベル | なし | あり |
| プライバシー保護 | なし | あり |
| パフォーマンス | 低い | 高い(遅延評価) |
| Console.appでの確認 | 不可 | 可 |
| ログの永続化 | なし | あり(レベルに依存) |
| カテゴリ分類 | なし | あり |
print()はデバッグには手軽だが、本番環境では個人情報がそのまま出力される。OSLogはプライバシー保護と高パフォーマンスを両立しており、本番コードではOSLogを使うとよい。
\第一線のプログラマーの行動原理を学べる!/
