4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SwiftプロジェクトでOSLogStoreを使ってユーザーのログを回収する

Posted at

概要

ネット上にOSLogStore周りの使い方をまとめた記事があまりなかったのでそれらの記事を参考にしながら、iPhoneで実機のログを収集するサンプルを作りました。

OSLogStoreとは?

Apple側で用意してくれているSwiftプロジェクト内で使用できるLogを取るためのライブラリ。
ユーザーのシステムログを取るのに使える。
公式ドキュメントはここ

サンプル

イメージ

ボタンをタップした時に報告をするアラートが表示され、

Simulator Screenshot - iPhone 15 - 2023-10-17 at 01.59.41.png

アラートのOKをタップしたらログが送信されるようなイメージ。

Simulator Screenshot - iPhone 15 - 2023-10-17 at 01.59.07.png

今回のソースでは送信時の処理にログファイルがlog.txtとしてiPhoneのアプリ内(ドキュメントディレクトリ)に保存される仕様となっているため、一旦ログの中身を見たい場合にはprint(messages)を追加すること。

ログの出力先情報
Logs exported to: file:///Users/UserName/Library/Developer/CoreSimulator/Devices/358AD377-A8B8-4EDA-92A1-0266704E7597/data/Containers/Data/Application/67A88D6C-ADE9-4636-8DE1-FEC69A137CF7/Documents/logs.txt
log.txtの中身(print(messages)実行時)
Using ~iphone resources
main bundle CFBundle 0x600003b082a0 </Users/UserName/Library/Developer/CoreSimulator/Devices/358AD377-A8B8-4EDA-92A1-0266704E7597/data/Containers/Bundle/Application/646B339D-2D47-47E4-A692-D8CD41E9D85F/OSLogStore.app> (executable, loaded) getting handle 0xfffffffffffffffb
[0x600003b10000] activating connection: mach=true listener=false peer=false name=com.apple.cfprefsd.daemon
Initializing connection
Removing all cached process handles
Sending handshake request attempt #1 to server
Creating connection to com.apple.runningboard
[0x600003b14000] activating connection: mach=true listener=false peer=false name=com.apple.runningboard
[0x10dd04080] activating connection: mach=false listener=false peer=false name=(anonymous)
Handshake succeeded
Identity resolved as app<kuehar.OSLogStore((null))>
didChangeInheritances: <RBSInheritanceChangeSet| gained:{(
    <RBSInheritance| environment:(none) name:com.apple.frontboard.visibility origID:86005-86002-137>
)} lost:(null)>
No persisted cache on this platform.
Deactivation reason added: 10; deactivation reasons: 0 -> 1024; animating application lifecycle event: 0
assertion failed: 23A344 21A328: libxpc.dylib + 62124 [D42F682D-2192-3D1C-AA6B-B96242532F0B]: 0x7d
activating monitor for service com.apple.frontboard.open
activating monitor for service com.apple.frontboard.workspace-service
..............................(この後にもログが続く)

ソースファイル

ContentView.swift
import SwiftUI
import OSLog

struct ContentView: View {
    @State private var messages:String = ""
    @State private var isLogSendAlertDisplay = false
    
    var body: some View {
        Form {
            Section(header: Text("報告")) {
                Button(action: {
                    exportToLogFile()
                    isLogSendAlertDisplay = true
                }, label: {
                    Text("アプリがクラッシュしたことを報告する")
                })
                .alert("通知", isPresented:$isLogSendAlertDisplay) {
                    Button("OK") {
                        // ログ送付処理をここに書く
                        isLogSendAlertDisplay = false
                    }
                    Button("Cancel",role:.cancel){}
                } message: {
                    Text("クラッシュしたことを開発者に報告します。よろしいですか?")
                }
            }
        }
    }
    /// OSLogStoreを使用し、ユーザーログを抽出する。
    /// その後の処理で`logs.txt`を作成し、ドキュメントディレクトリに保存を行う。
    func exportToLogFile() {
        do {
            let store = try OSLogStore(scope: .currentProcessIdentifier)
            let position = store.position(date: Date().addingTimeInterval(-3600))
            let enumerator = try store.__entriesEnumerator(options: [.reverse], position: position, predicate: nil)
            
            var logString = ""
            messages = ""
            enumerator.forEach { element in
                if let message = (element as? OSLogEntry)?.composedMessage {
                    logString += message + "\n"
                    messages += message + "\n"
                }
            }
            
            // ドキュメントディレクトリのパスを取得
            if let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
                let logURL = documentDirectory.appendingPathComponent("logs.txt")
                try logString.write(to: logURL, atomically: true, encoding: .utf8)
                print("Logs exported to: \(logURL)")
            }
        } catch {
            print("Error exporting logs: \(error)")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

exportToLogFileの中でOSLogStoreを使用し、ユーザーログを抽出する。
その後の処理でlogs.txtを作成し、ドキュメントディレクトリに保存を行う。

プロジェクトに応じて対応すべき内容

log.txtの送付処理に関してはプロジェクトによってまちまちな部分なので、今回は記載していない。
この記事の目的はあくまでOSLogStoreを通してユーザーのiPhoneのシステムログを取得するところにあるので、その点に関しては各々で対応するものとしている。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?