【iOS】アプリ内でシステムログを取得したい

この記事は[NTTコムウェア Advent Calendar 2021](https://qiita.com/advent-calendar/2021/nttcomware) 1日目の記事です。

初日からかなりニッチな内容ですが、iOSでシステムログを取得する方法について考えます。

# ベータテストあるある
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2318187/2efdcafe-4a23-33c1-0ac3-cc4992fbdcb6.png)

そんなこと言われても解析のしようがない
せめてログをください...

という光景を社内で見たことはないでしょうか。

実際アプリをリリースする前にシステムログを取得してもらうのは容易ではないため、
ログをもらうのを諦めているケースは多いと思います。

# 今までのログ取得方法

iOSでシステムログを確認する方法としては

- Macにつないで、XCode経由で取得
- Macにつないで、Console.app経由で取得
- 端末の電源ボタンと音量ボタンを長押ししてiPhone内にsysdiagnoseログを出力して、Macに繋いでファイルを取得して何らかの方法で送信してもらう

等の方法がありますが、
開発者ならまだしもテストに協力してもらっている方々にMac用意してXcodeインストールして...はハードルが高すぎます。


# iOS15以降のログ取得方法
実は最近(2021年8月)にリリースされたiOS15Beta版からアプリ内でシステムログが取得できるようになりました。

参考) Apple Developer Documentation OSLogStore Class
https://developer.apple.com/documentation/oslog/oslogstore

この機能を利用してアプリ内にバグ報告機能を実装しておけば、簡単にテスターからログを送信してもらうことができそうです。

## サンプルコード
```swift
if #available(iOS 15.0, *) {
    do {
        let store = try OSLogStore.init(scope: .currentProcessIdentifier)
        // 1時間前までのログを取得
        let position = store.position(date: Date().addingTimeInterval(-3600))
        // 時間の降順で取得
        let enumerator = try store.__entriesEnumerator(options: [.reverse], position: position, predicate: nil)
        enumerator.forEach { element in
            let message = element as? OSLogEntry
            dump(message.debugDescription)
            dump(message?.composedMessage)
        }
    } catch {
        dump(error)
    }
} else {
    // iOS14以下の処理
}
```

## 実際に取得できたログの一部
```
2021-11-19 11:39:01:Using ~iphone resources
2021-11-19 11:39:01:Initializing connection
2021-11-19 11:39:01:Removing all cached process handles
2021-11-19 11:39:01:Sending handshake request attempt #1 to server
2021-11-19 11:39:01:Creating connection to com.apple.runningboard
2021-11-19 11:39:01:Received message from server: async_didChangeInheritances:completion:
2021-11-19 11:39:01:didChangeInheritances: <RBSInheritanceChangeSet| gained:{(
<RBSInheritance| environment:(none) name:com.apple.frontboard.visibility origID:6452-6464-44>
)} lost:(null)>
2021-11-19 11:39:01:Handshake succeeded
2021-11-19 11:39:01:Identity resolved as application<com.xxxx.yyyy>
2021-11-19 11:39:02:No persisted cache on this platform.
2021-11-19 11:39:02:Deactivation reason added: 10; deactivation reasons: 0 -> 1024; animating application lifecycle event: 0
2021-11-19 11:39:02:assertion failed: 20G165 19A339: libxpc.dylib + 55577 [68F6BE69-6DB2-3A18-A053-2C03A9D2A10A]: 0x7d
2021-11-19 11:39:02:activating monitor for service com.apple.frontboard.open
2021-11-19 11:39:02:activating monitor for service com.apple.frontboard.workspace-service
2021-11-19 11:39:02:FBSWorkspace connecting to endpoint : <BSServiceConnectionEndpoint: 0x600000a84210; target: com.apple.frontboard.systemappservices; service: com.apple.frontboard.workspace-service>
2021-11-19 11:39:02:FBSWorkspace registering source: <FBSWorkspaceScenesClient: 0x600002ff8070>
2021-11-19 11:39:02:FBSWorkspace connected to endpoint : <BSServiceConnectionEndpoint: 0x600000a84210; target: com.apple.frontboard.systemappservices; service: com.apple.frontboard.workspace-service>
2021-11-19 11:39:02:Added observer for process assertions expiration warning: <_RBSExpirationWarningClient: 0x6000004d1040>
2021-11-19 11:39:02:Lookup of 'AppleLocale' from current preferences failed (app preferences do not contain the key; falling back to generation via preferred languages)
2021-11-19 11:39:02:CFLocaleCopyCurrent() constructing a locale identifier from preferred languages without a set country code
2021-11-19 11:39:02:[FBSScene] [sceneID:com.xxxx.yyyy-default] Created client agent: <UIApplicationSceneClientAgent: 0x6000006ec3c0>
2021-11-19 11:39:02:Language lookup at CFBundle 0x7fcee7a04cf0 </Users/xxxx/Library/Developer/CoreSimulator/Devices/xxxxxx/data/Containers/Bundle/Application/yyyyyy/dummy.app> (executable, loaded)
Localizations : [Base, en]
Dev language : en
User prefs : [en, ja-US]
Main bundle : [<null>]
Allow mixed : 0
Result : [en]
2021-11-19 11:39:02:Resource lookup at CFBundle 0x7fcee7a04cf0 </Users/xxxx/Library/Developer/CoreSimulator/Devices/xxxxxx/data/Containers/Bundle/Application/yyyyyy/dummy.app> (executable, loaded)
Request : InfoPlist type: strings
Result : None
```


## 動作的に不完全な部分がある

- アプリ起動後からのログしか実際は取得できていない
- ログ取得期間、例えば60分前〜のログを取得、を指定できるパラメータが存在しているが、実際はパラメータを指定しても効果が無い

まだできたてほやほやの機能ということもあって今後の改善に期待です。

# おわりに
アプリ内でシステムログが取得できる、という事実だけでも非常に有用ですのでお困りの方は試してみてはいかがでしょうか。

# 参考
1. https://developer.apple.com/documentation/oslog/oslogstore
1. https://developer.apple.com/forums/thread/650843
1. https://developer.apple.com/forums/thread/677068