What is the point of the Unified Logging System and the new `Logger` API if it's so inflexible and clumsy?

Hi there!

Sorry in advance, this is going to be a long post of Apple developer pains which I want to share with you, and, hopefully, find the answer and help Apple become better.

I'm at the very beginning of my new and exciting personal project which (I hope) may one day feed me and be my daily source of inspiration. I'm not a newbie in Apple development nor am I a senior-level developer — just a fellow developa'.

Here's the problem I bring to you — why Apple promotes Unified Logging System and recommends using it as the primary way to implement logging in 3rd-party apps? No doubt, OSLog is a great, secure, efficient, and centralized way to gather diagnostics information, and I, starting my new project, am itching to choose exactly this 1st-party logging infrastructure. This decision in theory has a number of benefits:

  1. I don't have to depend on 3rd-party logging frameworks which may eventually be discontinued;
  2. I have extensive documentation, great WWDC sessions explaining how to use the framework, and stackoverflow answers from the whole Apple dev community in case I experience any troubles;
  3. I have this cool Console.app and upcoming Xcode 15 tools with great visualization and filtering of my logs;
  4. It's quite a robust and stable infrastructure which I may restfully rely on.

But... the thing is there's this big elephant in the room — this API is non-customizable, inconvenient, and hard to use in terms of the app architecture. I can't write my own protocol wrapper around it to abstract my domain logic from implementation details or just simplify the usage at the call site. I can't configure my own format for log messages (this is debatable, since Console.app doesn't provide "***** strings" as Xcode 14 and earlier, but still). And what's most important — I can't conveniently retrieve the logs!

I can't implement the functionality where my user just taps the button, and the logs are sent on the background queue to my support email (eskimo's answer). They would have to go through this monstrous procedure of holding volume buttons on the iPhone, connecting their device to the Mac, gathering sysdiagnose, entering some weird Terminal commands (jeez, these nerdy developers...), etc. If it ever succeeds, of course, and something doesn't go wrong, leaving my user angry and dissatisfied with my app.

Regarding the protocol wrapper, I can't do something like this:

protocol Logging {
    var logger: Logger { get }
    func info(_ message: OSLogMessage)
}

extension Logging {
    var logger: Logger {
        return Logger(
            subsystem: "com.my.bundle.id",
            category: String(describing: Self.self)
        )
    }
    func info(_ message: OSLogMessage) {
        logger.info(message)
    }
}

class MyClass: Logging {
    func someImportantMethod() {
        // ...
        self.info("Some useful debug info: \(someVar, privacy: .public)")
    }
}

I've been investigating this topic for 2 days, and it's the farthest I want to go in beating my head over how to do two simple things:

  1. How to isolate logging framework implementation decision from my main code and write convenience wrappers?
  2. How to easily transfer the log files from the user to the developer?

And I'm not the only one struggling. Here's just one example among hundreds of other questions that are being asked on dev forums: https://www.hackingwithswift.com/forums/ios/unified-logging-system-retrieve-logs-on-device/838. I've read almost all Apple docs which describe the modern Unified Logging System, I've read through eskimo's thread on Apple Developer Forum about the API, but I still haven't found the answer.

Maybe, I've misperceived this framework and it's not the tool I'm searching for? Maybe, it focuses on different aspects of logging, e.g. signposting, rather than logging the current state of the app? What am I missing?

Accepted Reply

Everything in engineering is a trade-off. The system log balances many different requirements, both from internal and external clients. If you don’t like the balance that Apple has struck, you should write or acquire your own logging library. Indeed, this is exactly what the Swift on Server folks have done with SwiftLog.

Also, keep in mind that the system log has evolved over time. Recent releases have seen improvements both large, like the new Xcode UI, and small, like the .currentProcessIdentifier scope. If you want to guide that evolution, I encourage you to file enhancement request for the features you need.


I can't implement the functionality where my user just taps the button, and the logs are sent on the background queue to my support email (eskimo's answer).

Hmmm, that answer was from 6 years ago, and things have changed a little since then. Indeed, you’re aware of Your Friend the System Log but you seem to have missed its discussion of the the .currentProcessIdentifier scope.

Is that perfect? No. Right next to that discussion is a description of an important limitation.

Does it work for some people? Yes.

Does it work for everyone? No. And if you’re one of those folks, that’s just fine, you have plenty of other options.

Regarding the protocol wrapper, I can't do something like this:

Right. The system log doesn’t support you wrapping it [1] and there are good technical reasons for that [2].

In C-based languages it’s common to get around this restriction using a macro. Now that Swift has macros it may be possible to do the same here. I’ve not seen anyone do that, but it’d be an interesting thing to try out.

Note I regularly see folks wrap the log so that they can add #file and #line markers. There’s no need to do that, because you can get that info using --source.

The abstract log handle approach you’ve described is pretty common for third-party logging libraries. Indeed, SwiftLog uses exactly that approach. However, that approach isn’t free. You pay for that flexibility, with both performance and features. It’s up to you to decide whether it’s worth the cost.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Well, you can wrap it, but you lose both performance and critical features.

[2] Specifically, the system log meets its efficiency goals by not storing your log strings. Rather, it stores the Mach-O UUID of each executable that logs and the offset into that Mach-O image of each log point. This reduces the amount of data going into the log, and supports cool features like --source.

  • Well, thank you, legend. I hope this reply will be useful for the community and a relief to thousands of souls like mine was :)

  • Note I regularly see folks wrap the log so that they can add #file and #line markers. There’s no need to do that, because you can get that info using --source.

    Where can I learn more about --source? I've not heard of this before?

Add a Comment

Replies

Everything in engineering is a trade-off. The system log balances many different requirements, both from internal and external clients. If you don’t like the balance that Apple has struck, you should write or acquire your own logging library. Indeed, this is exactly what the Swift on Server folks have done with SwiftLog.

Also, keep in mind that the system log has evolved over time. Recent releases have seen improvements both large, like the new Xcode UI, and small, like the .currentProcessIdentifier scope. If you want to guide that evolution, I encourage you to file enhancement request for the features you need.


I can't implement the functionality where my user just taps the button, and the logs are sent on the background queue to my support email (eskimo's answer).

Hmmm, that answer was from 6 years ago, and things have changed a little since then. Indeed, you’re aware of Your Friend the System Log but you seem to have missed its discussion of the the .currentProcessIdentifier scope.

Is that perfect? No. Right next to that discussion is a description of an important limitation.

Does it work for some people? Yes.

Does it work for everyone? No. And if you’re one of those folks, that’s just fine, you have plenty of other options.

Regarding the protocol wrapper, I can't do something like this:

Right. The system log doesn’t support you wrapping it [1] and there are good technical reasons for that [2].

In C-based languages it’s common to get around this restriction using a macro. Now that Swift has macros it may be possible to do the same here. I’ve not seen anyone do that, but it’d be an interesting thing to try out.

Note I regularly see folks wrap the log so that they can add #file and #line markers. There’s no need to do that, because you can get that info using --source.

The abstract log handle approach you’ve described is pretty common for third-party logging libraries. Indeed, SwiftLog uses exactly that approach. However, that approach isn’t free. You pay for that flexibility, with both performance and features. It’s up to you to decide whether it’s worth the cost.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Well, you can wrap it, but you lose both performance and critical features.

[2] Specifically, the system log meets its efficiency goals by not storing your log strings. Rather, it stores the Mach-O UUID of each executable that logs and the offset into that Mach-O image of each log point. This reduces the amount of data going into the log, and supports cool features like --source.

  • Well, thank you, legend. I hope this reply will be useful for the community and a relief to thousands of souls like mine was :)

  • Note I regularly see folks wrap the log so that they can add #file and #line markers. There’s no need to do that, because you can get that info using --source.

    Where can I learn more about --source? I've not heard of this before?

Add a Comment

Removed

Where can I learn more about --source?

In the log man page.

Also, with Xcode 15 beta you get access to this from the Xcode console. The WWDC preso shows this, starting around 05:07.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"