iOS開発でログを利用する場合、自前のロガーを利用したり、OSSを利用する選択肢が多いですが、標準のOSLog
のAPIが新しくなり、iOS14以降で使いやすくなりそうなので、紹介します。
printと比べて何が良いか
Xcodeの使い勝手が原因でもあるのですが、print
で出力された情報は他のログと混ざりやすく、検索もまともにできないので、ログを出力するのには向いていません。
OSLog
は今まで通りXcode上のconsoleに出力をしつつ、Console.appで視覚的にログを追いやすくすることができます。
OSLogを利用した出力
iOS14以上では、新しいAPIを使って直感的な記法で記述することができますが、それ以下のiOSをサポートしている場合は古い方のAPIを利用する必要があります。(機能は同等です)
Loggerを用意する
iOS14からはLogger
インスタンスを用意します。
import os let logger = Logger(subsystem: "com.flyingalpaca.sample", category: "Network")
subsystem
を利用してどのアプリケーションから出力されたログ化を識別します。
他のアプリケーションと区別しやすくするために、上のような逆DNS記法で書くことが推奨されています。
category
はアプリ内のどこから出力されたログかを識別するのに利用します。
こちらは、Network
やUI
など適当につけても大丈夫です。
iOS14以前の古いAPIでの記法はこちらです。
import os.log let osLog = OSLog(subsystem: "com.flyingalpaca.sample", category: "Network")
共通で利用するものは、static
でどこかにインスタンスを作っておくと便利です。
import os extension OSLog { static let network = Logger(subsystem: "com.flyingalpaca.sample", category: "Network") }
ログを出力する
新しいAPIでは、出力のインターフェースが他の言語でもあるような形式に変更されました。
以下のような書き方で出力することができます。
logger.trace("trace") logger.debug("debug") logger.info("info") logger.notice("notice") logger.error("error") logger.fault("fault") logger.critical("critical")
詳しい解説がないのも含めれば、7種類ログレベルが存在します。 下に行くほど深刻度が高くなります。(詳しくは下のドキュメントや、docコメントを参照してください)
https://github.com/apple/swift-log/blob/1.2.0/Sources/Logging/Logging.swift#L323-L352
trace
やdebug
はストレージへの書き込みをデフォルトでは行わないので、オーバーヘッドが少なくなっています。
info
はツールを使ってログを収集しているときのみ、ストレージにログを保持します。
critical
はデベロッパードキュメントには記載されていないですが、上のソースコードのdocコメントを参照すると、このログを送った場合、ロガーがスタックトレースのキャプチャなどの重い処理を行えるようになると書いてあります。(逆に言えばオーバーヘッドがあるので、名前の通り致命的な場合以外は使わないほうが良さそうです)
古いAPIでは、os_log
というメソッドを使います。
os_log(.info, log: osLog, "info") os_log(.error, log: osLog, "error")
ログの記法
新しいAPIではOSLogMessage
を引数にとっていますが、このインスタンスを直接生成してはいけません。
OSLogInterpolation
を介して独自のフォーマットで記述することで、より柔軟な出力を行うことができます。
logger.info("started: \(1000000, format: .secondsSince1970)") let value: UInt32 = 3735928559 logger.info("address: \(value, format: .hex)")
2021-03-07 17:26:44.471594+0900 LoggerTest2[7455:334390] [view] started: 1970-01-12 22:46:40+0900 2021-03-07 17:26:44.471784+0900 LoggerTest2[7455:334390] [view] address: deadbeef
詳しくは、以下の"Format Custom Values in Message Strings"の部分や、各formatの実装を参照してください。
ここで出力される情報は、誰でもアクセスできるので、第三者に知られたくない重要な情報はマスクする必要があります。
デフォルトでは、数値はマスクされず、それ以外のオブジェクトはマスクされます。
let value: UInt32 = 3735928559 logger.info("address: \(value, format: .hex, privacy: .private(mask: .hash))")
古いAPIでは、以下のような記法になります。(情報が少なく、printf likeな書き方でここが使いづらかった部分の一つです。)
let value: UInt32 = 3735928559 os_log(.info, log: osLog, "address: %{private}x", value)
ログの確認
Xcodeのコンソールにも出力されますが、OSLog
を経由するとログを確認する方法があります。
Console.appを利用した確認
Console.appを利用すると、GUIを通じてログを確認することができます。
また、先程設定したsubsystem
やcategory
を利用して、ログをフィルタリングすることができます。
上の画像のように、右上にフィルターの条件を記述します。
info
やdebug
のログは、初期設定では表示されないので、以下の部分にチェックを入れます。
複数行のログもまとめて吐かれるので、print
よりも検索しやすくなっています。
logコマンドを使った確認
log collect
やlog show
を使ってターミナル上でも確認することができます。
log collect --device 00008101-001E69993EC2001E --last 1h log show --predicate '(subsystem == "com.flyingalpaca.sample")' --info --last 1h --archive ./system_logs.logarchive
2021-03-07 18:26:22.876033+0900 0x7a513 Info 0x25c42e 2997 0 LoggerTest2: [com.flyingalpaca.sample:view] info 2021-03-07 18:26:22.876050+0900 0x7a513 Default 0x25c42e 2997 0 LoggerTest2: [com.flyingalpaca.sample:view] notice 2021-03-07 18:26:22.876177+0900 0x7a513 Error 0x25c42e 2997 0 LoggerTest2: [com.flyingalpaca.sample:view] error 2021-03-07 18:26:22.876522+0900 0x7a513 Fault 0x25c42f 2997 14 LoggerTest2: [com.flyingalpaca.sample:view] fault 2021-03-07 18:26:22.876718+0900 0x7a513 Fault 0x25e510 2997 14 LoggerTest2: [com.flyingalpaca.sample:view] critical 2021-03-07 18:26:22.876840+0900 0x7a513 Info 0x25c42e 2997 0 LoggerTest2: [com.flyingalpaca.sample:view] started: 1970-01-12 22:46:40+0900 2021-03-07 18:26:22.876899+0900 0x7a513 Info 0x25c42e 2997 0 LoggerTest2: [com.flyingalpaca.sample:view] address: deadbeef
テキスト処理をしたい場合は、こちらも活用できます。
(あまりWWDCの動画などでは情報がないので、man log
を参照すると良さそうです)
OSLogに関するドキュメントやWWDCのセッション
ぜひみてください