How do Logger interpolation formats, privacy options and compile-time checks work, and what does your own process see when it reads its log back?

23.10.4 · L3 · Learning

Predict first

Which interpolated values are rendered when the process reads its own log entries back?

// === main.swift ===
import OSLog
let log = Logger(subsystem: "com.example.lab", category: "fmt")
let user = "alice@example.com"
log.notice("plain \(user)")
log.notice("hex \(UInt(255), format: .hex) pad \(7, format: .decimal(minDigits: 3)) fixed \(3.14159, format: .fixed(precision: 2))")
let store = try OSLogStore(scope: .currentProcessIdentifier)
for e in try store.getEntries(matching: NSPredicate(format: "subsystem == %@", "com.example.lab")) {
    if let l = e as? OSLogEntryLog { print(l.composedMessage) }
}
  • plain alice@example.com and hex ff pad 007 fixed 3.14
  • plain <private>
  • nothing is stored

Predict first

Does a signed Int with format: .hex compile?

// === main.swift ===
import OSLog
let log = Logger(subsystem: "com.example.lab", category: "fmt")
log.notice("hex \(255, format: .hex)")
  • No: signed integers must use .decimal
  • Yes, prints ff
  • Yes, prints 255

Mental model. A form with typed fields: the printer refuses a letter in a number box.

Logger messages are not ordinary strings: the compiler builds an OSLogMessage from the literal and its interpolations, so the format string is a compile-time constant and values are stored separately. You choose formats per value: format: .hex for unsigned integers, .decimal(minDigits: 3) for signed ones, .fixed(precision: 2) for doubles; the check reads back hex ff pad 007 fixed 3.14. The compiler enforces the types: a signed Int with .hex fails the build (“Signed integers must be formatted using .decimal”). Privacy is per value (.public, .private, .private(mask: .hash), Q460).

A detail worth knowing: redaction applies to log data read by someone else without the right to see it; when the process reads its own entries through OSLogStore(scope: .currentProcessIdentifier) the dynamic string comes back unredacted (the check prints plain alice@example.com). So an in-app diagnostics export can contain personal data you thought was private, and you must filter before sharing (Q347). Treat the store as readable by your own code, log show output as redacted unless the device is configured otherwise, and test what a collected log looks like before relying on it.

The in-process store shows the value; formats are applied.

The OSLog interpolation restricts format types.

Common traps

  • Assuming private values are hidden from your own export. Check what the store returns.
  • Building message text with string concatenation. It loses laziness, privacy and structure.

They might follow up with

  • Logger or os_log? Logger is the Swift-friendly front end (Q460).
  • Signposts? Q430.

Related questions

  • 23.4.1 How does the unified logging system work: Logger, levels, subsystems and categories, privacy, and how do you…
  • 14.3.3 How do you keep personal data out of logs, screenshots, the pasteboard and the app switcher?
  • 21.2.2 How do signposts and Points of Interest work, and how do you turn a measurement into an XCTest performance…

← Why can a dSYM exist yet give no file and line numbers, and how do…