0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TestFlightでNSLogが使いものにならなくなった件

0
Posted at

NSLogの出力が全て<private>になってしまう

iOSアプリやMacアプリの開発で、TestFlight経由のアプリのログを取得したい時、printではうまく出力できないので、今まではNSLogを使用してコンソールにログを出力し、コンソールアプリでNSLogの出力内容を確認していました。

ところが、久しぶりにコンソールアプリでNSLogログの出力内容を確認したところ、なんと次のようにログ内容が全て<private>と表示されるようになっていました。

image1.png

ちなみにXcodeからアプリを起動した時はNSLogの出力が正常に表示されます。しかし、Xcodeを使用せず直接アプリを起動すると<private>と表示されます。

なぜ??と思って調べたところ、iOS26/macOS26以降、NSLogで出力する文字列データは<private>に置き換えられると、リリースノートに書かれていました。

(iOS & iPadOS 26 Release Notes)
https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-26-release-notes

(macOS Tahoe 26 Release Notes)
https://developer.apple.com/documentation/macos-release-notes/macos-26-release-notes

NSLog

New Features

・Dynamic string data in format arguments for NSLog will be redacted to \<private\> in the Unified Logging System. This specifically targets data that enters the Unified Logging System via NSLog, and will not impact the Xcode console or NSLog’s stdout output. If you wish to log un-redacted data to the Unified Logging System please use the “os_log” or “Logger” interfaces. (137129180)

おそらく、NSLogでプライバシー情報を出力してしまうアプリがあることを懸念してAppleが処置したのだと思われます。

NSLogの代替となるログ取得方法

上記のRelease Notesの中ではNSLogの代替となるログ取得方法としてos_logかLoggerを使うように書かれています。

ただos_logは古いC API形式のメソッドであるため、Swiftから使用する場合は既にdeprecatedになっているようです。

それと、os_logやLoggerを使用する場合、次のようにして文字列変数の値を埋め込み、

let name = "sample name"
Logger().log("name = \(name)")

この出力をコンソールアプリで表示すると、結局埋め込んだ変数の部分は次のように<private>になります。(これはiOS26より前のOSでもこのような動作になります。)

image2.png

これについては、次のように埋め込み情報に privacy: .public を指定してやると、変数の内容もうまく表示されるようになります。

let name = "sample name"
Logger().log("name = \(name, privacy: .public)")

image3.png

しかし、変数を埋め込む箇所全てにいちいち privacy: .public を指定するのは面倒です。そこで、次のようにしてログ採取用のメソッドを作成し、それを呼び出すようにしました。

(このメソッドでは #if DEBUG でログを採取するか否かを判定していますが、Test FlightはReleaseモードでビルドされるため、この通りだとTest Flightでログは採取されません。Test Flightでログを採取したい場合は、一時的に #if DEBUG の判定を削除してテストします。#if DEBUG の判定を削除したものをそのままリリースしないように注意しましょう。)

Log.swift
import OSLog

let logger = Logger()

func log(_ message: String) {
    #if DEBUG
        logger.log("\(message, privacy: .public)")
    #endif
}

そして、ログを採取したい箇所で以下のように記述します。これで、NSLogと同様の手軽さでログを採取できるようになります。

let name = "sample name"
log("name = \(name)")

ただ、この場合1つ残念なことがありまして、最近のXcodeのコンソールは次の図のようにMetadataを表示する指定ができ、それを指定すると、ログの右側にそのログを採取したソースコード位置が表示され、クリックするとそのソースコードが表示できるのですが、上記のようなログ採取メソッドを作成すると、ログを採取したソースコード位置は常にLog.swift内の1箇所になってしまいます。

image4.png

ログ採取メソッドを作成を使用した上で、元のログ採取位置を表示するには、次のようにして呼び出し元のファイル名、行位置、桁位置をメッセージに埋め込めば、一応表示されます。しかし、クリックでそのソースコード位置にジャンプできるわけではないので、最新のXcodeのコンソール機能をうまく使えていません。

Log.swift
import OSLog

let logger = Logger()

func log(_ message: String, filePath: String = #file, line: Int = #line, column: Int = #column) {
    #if DEBUG
        let logMessage = "\((filePath as NSString).lastPathComponent) \(line):\(column) \(message)"
        logger.log("\(logMessage, privacy: .public)")
    #endif
}

この問題について、もしかしてSwift Macroを使えばうまくいくのかもしれないと思って、ログ採取をSwift Macroで制御する方法を試してみました。その話は別記事で紹介したいと思います。


この記事の画像はZkitchで作成しました。Zkitchは、画面のスクリーンショットに矢印やテキスト等の説明を入れることができるアプリです。

説明入りの画像をよく作成される方は、ぜひお試しください。

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?