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段階のログレベルがある。

レベルメソッドデフォルトの永続化
Debuglogger.debug()なし(デバッグ時のみ)
Infologger.info()なし
Noticelogger.notice()あり
Errorlogger.error()あり
Faultlogger.fault()あり

debuginfoはデフォルトでディスクに保存されない。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を使うとよい。