Jared Sinclair - Big Nerd Ranch Wed, 01 Jun 2022 19:32:13 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.5 Own Your Crash Logs with PLCrashReporter: Part 3 https://bignerdranch.com/blog/own-your-crash-logs-with-plcrashreporter-part-3/ Tue, 12 Jan 2021 13:00:55 +0000 https://www.bignerdranch.com/?p=4663 In the third post of our PLCrashReporter series, we will walk through how to get PLCrashReporter up and running on your machine. And make sure to check out Part 1 and Part 2 of our series if you haven’t already. Walkthrough It’s time to install PLCrashReporter and get your first crash report! Step 1: Download […]

The post Own Your Crash Logs with PLCrashReporter: Part 3 appeared first on Big Nerd Ranch.

]]>
In the third post of our PLCrashReporter series, we will walk through how to get PLCrashReporter up and running on your machine. And make sure to check out Part 1 and Part 2 of our series if you haven’t already.

Walkthrough

It’s time to install PLCrashReporter and get your first crash report!

Step 1: Download the Library

First, you need to download the latest release of PLCrashReporter to your computer. As of this writing, 1.5.1 is the latest release. Visit the PLCrashReporter releases page and download PLCrashReporter-1.5.1.zip.

You may have noticed another zip file with the word “Static” included in its filename. This is the static library version of PLCrashReporter which we won’t use for this walkthrough. We’ll use the dynamic framework. To learn more about dynamic frameworks versus static libraries check out this BNR blog post on libraries vs frameworks.

Step 2: Set Up the Project

Now that you have the library downloaded, it’s time to put it in an Xcode project.

Open Xcode 12 and create a new iOS app project for this walkthrough. Make sure to use the Swift language with SwiftUI for your app interface, but select to use a UIKit App Delegate for its lifecycle. Name your app “PLCExample.”

Unzip PLCrashReporter-1.5.1.zip and go into the “iOS Framework” folder where you’ll see an item named CrashReporter.framework. Copy and paste it into your project’s directory.

Finally, drag the newly-pasted framework from your project’s directory into your Xcode project. It should look like this:

framework on pl crash reporterNow PLCrashReporter is in your project!

Step 3: Add a Bridging Header

Because PLCrashReporter is an Objective-C library, you’ll need to set up a bridging header before you can start playing with it in your Swift project.

The easiest way to create a bridging header is to create a new Objective-C class in Xcode. Xcode will offer to set up the bridging header for you so you just need to accept its offer, then delete the Objective-C class files while keeping the bridging header.

Once you have your bridging header, open it up and add #import <CrashReporter/CrashReporter.h> This exposes PLCrashReporter to your Swift project.

Step 4: Enable the Crash Reporter

It’s time to start writing some code. To kick things off, you need to enable PLCrashReporter. Open up your AppDelegate and put the following in didFinishLaunchingWithOptions:

let reporter = PLCrashReporter()
reporter.enable()

That’s it! You’ve got PLCrashReporter up and running, ready to record crashes. Time to buckle up and crash your app. We will cover this in our next post!

The post Own Your Crash Logs with PLCrashReporter: Part 3 appeared first on Big Nerd Ranch.

]]>
Own Your Crash Logs with PLCrashReporter: Part 2 https://bignerdranch.com/blog/own-your-crash-logs-with-plcrashreporter-part-2/ Mon, 21 Dec 2020 17:00:01 +0000 https://www.bignerdranch.com/?p=4642 In part two of our PLCrashReporter series, we will examine how crashes are created and learn more about specific crash types.

The post Own Your Crash Logs with PLCrashReporter: Part 2 appeared first on Big Nerd Ranch.

]]>
Creating Crash Logs

This is part two of our PLCrashReporter series. In this post, we will examine how crashes are created and learn more about specific crash types.

A crash handler has a three-phase life cycle:

  1. Preparing to handle crashes – There are a lot of secondary responsibilities to account for, but the essential task of this phase is to register a function (or functions) with the OS that will be executed if and when a crash occurs.
  2. Handling an actual crash – As a crash occurs, the code that was registered during the first phase is executed. During this phase, information about the nature of the crash (e.g., the call stack for the crashed thread) is captured. This information is written to disk for later use.
  3. Recovering a crash log – On iOS this phase does not occur until the user launches the app again. Without a subsequent launch, there is no opportunity for an in-process crash reporter to process the crash log. On macOS, it is possible for an app to run separate processes that detect and report crashes.

Next, let’s look closer at the first phase. How does one prepare to intercept crashes? To answer that, we need to look at how crashes are propagated. On Apple platforms, there are two pathways via which application crashes flow: POSIX signals and Mach exceptions.

POSIX Signals

First up are POSIX signals. When an illegal instruction or a request for termination occurs, the kernel sends a POSIX signal to the offending thread. These signals have a shortlist of usual suspects:

  • SIGSEGV – Memory errors
  • SIGILL – Illegal instructions
  • SIGABRT – Usually when the process itself calls abort()
  • SIGKILL – For example when you issue the killall -9 command in a shell. That “9” is the value of SIGKILL.

Once that signal is delivered, the OS terminates the process. Between when the signal is sent and the process is terminated, any signal handlers that were registered for the process are given an opportunity to respond.

Mach Exceptions

Apple’s operating systems run atop a Darwin kernel. Darwin is a descendant of the Mach kernel. Mach kernels, much like Darwin, use exception messages, rather than POSIX signals, to communicate about unexpected errors in the program flow. Mach exceptions are messages sent over IPC ports, which can be subscribed to by any interested observer with sufficient permissions for the process they’re interested in.

On Darwin, Mach exception messages are actually the underlying mechanism beneath the implementation of POSIX signals. Darwin registers a Mach exception handler that reflects Mach exceptions into POSIX signals. This is why, for example, when you look at a memory access error crash the description of the crash includes EXC_BAD_ACCESS (SIGSEGV), the Mach exception and the POSIX signal respectively.

It is possible for your app to register its own Mach exception server, but a thorough exception server implementation requires use of undocumented or private API and is fraught with even more peril than writing your own POSIX signal handler.

When writing a custom crash logger, you have to decide which of these mechanisms you will use to intercept crashes. While this might seem like a purely academic choice, there is at least one salient edge case: POSIX signal handlers are run on the crashed thread, not a separate thread, thus using the same stack as the crashed thread. If that thread encountered a stack overflow, there will by definition be no available space on top of the stack for your signal handler to be executed, and you will be unable to capture the crash. A Mach exception handler is immune to this edge case because your handler — or, more accurately, your exception server — is listening for exceptions on a dedicated thread, which likely has enough room on its call stack to execute crash-handling code.

PLCrashReporter can use either POSIX signals or Mach exceptions, but the authors strenuously recommend against using a Mach exception server in production code.

Async Signal Safety

Within your crash-handling code, there are truly profound limitations on what APIs you are able to use. There is almost nothing available to you. There is a shortlist of what are called “async-signal-safe” APIs that can be used within a signal handler. Because the heap could be corrupted, they can only use stack memory. Fortunately, they include essential file-system functions like open(2) and write(2), which is how a custom crash handler is able to save a crash log to disk.

The “async” part is misleading from the perspective of a practicing iOS or macOS developer. It doesn’t mean they’re safe for concurrent access from multiple threads. Rather, an API is considered async-signal-safe if it is guaranteed to be fully re-entrant. A crash could occur at any moment during program execution, including somewhere within a call to a function that your crash handler might need to call itself! If your crash handler then called that same function during the course of handling the crash, and that function isn’t async-signal-safe, your crash handler might deadlock, leading to a lost crash log. You need async-signal-safe turtles all the way down.

Some additional things you cannot do because they aren’t async-signal-safe:

  • You cannot allocate memory because malloc isn’t async-signal-safe, and also because the heap might be corrupted. Any memory your crash handler needs to perform its duties must be allocated during app launch when the handler is first registered. Its memory budget is thus fixed and predetermined, even though a crash log could contain any amount of information. Consider how many megabytes of data could be in a single crash log if there are deep call stacks and hundreds of libraries.
  • Signal handlers cannot use Objective-C or Swift. Period. This is because the Objective-C runtime makes extensive use of non-recursive locking and because both Objective-C and Swift provide no way to prevent their runtimes from calling malloc.
  • You cannot dispatch to other threads or queues. Allowing program execution to continue beyond the crash could lead to corruption of the user’s data [2].

Popping Back Off The Stack…

Putting this all together, to write a proper crash reporter requires:

  • Knowledge of UNIX/Mach/Darwin systems beyond the ken of mere mortals
  • Writing mission-critical code without the benefit of any higher-level abstractions
  • Heroic reasoning about code that cannot be debugged with a debugger
  • Being able to finesse many megabytes of data (binary images, call stacks, etc.) through a tiny synchronous window of CPU time
  • Doing all of the above on a shoestring memory budget

By now, you should be sufficiently terrified of writing your own crash logger. If you aren’t, you’re braver than most, or you weren’t paying attention. The rest of us are fortunate that there are existing implementations.

A reliable, well-maintained, open-source library for capturing crashes on Apple platforms is PLCrashReporter. It has changed hands a few times as its ownership hopped from Plausible Labs to Hockey App to Microsoft, but the fundamental design of the library remains the same.

The next post in our series will walk you through adding the PLCrashReporter library to an application so that you can obtain crash logs directly on your device without having to resort to a third party service.

[2]: Landon Fuller, the primary author of PLCrashReporter, gives an example of how allowing program execution to continue after the crash can corrupt or destroy user data in the section “Failure Case: Async-Safety and Data Corruption” of Reliable Crash Reporting.

The post Own Your Crash Logs with PLCrashReporter: Part 2 appeared first on Big Nerd Ranch.

]]>
Own Your Crash Logs with PLCrashReporter: Part 1 https://bignerdranch.com/blog/own-your-crash-logs-with-plcrashreporter-part-1/ Mon, 21 Dec 2020 16:36:10 +0000 https://www.bignerdranch.com/?p=4641 In this four-part series, we will first dive deep into crashes and will provide you with a step-by-step tutorial on how to get PLCrashReporter up and running

The post Own Your Crash Logs with PLCrashReporter: Part 1 appeared first on Big Nerd Ranch.

]]>
Welcome to the PLCrashReporter blog post series. For those unfamiliar with this crash reporting library, it is defined as follows:

PLCrashReporter is a reliable open source library that provides an in-process live crash reporting framework for use on iOS, macOS, and tvOS. The library detects crashes and generates reports to help your investigation and troubleshooting with the information of application, system, process, thread, etc. as well as stack traces.

In the upcoming posts we will first dive deep into crashes by covering:

  • Crash fundamentals
  • Obtaining crash logs
  • Creating crash logs
  • POSIX signals
  • Mach exceptions
  • Async safety

Then, we will provide you with a step-by-step tutorial on how to get PLCrashReporter up and running.

Who This Series is For

You’re curious about crashes – What even is a crash? Obvious questions can be difficult to answer. If there’s a chance you might get asked this in an interview, you’ll be better prepared to answer it after reading.

You want to know how crash logs are created – You’re equally horrified and mesmerized by whatever dark art must be summoned to intercept a crash and finagle it into some useful output.

You’re interested in PLCrashReporter – You’ve heard of PLCrashReporter before but have never peeked under the hood or used it in a project. Now’s your chance. [1]

You’re looking for an alternative to third-party crash reporting services – There are a number of reasons why you wouldn’t want to use a paid crash reporting service. Your institution might not permit closed-source libraries, which most (perhaps all?) third-party crash reporters require. Third-party services cost money, which is a deal-breaker for some small projects. Some have dubious privacy policies that might permit them to gather more than just crash logs. Or a Facebook bug might bring down their infrastructure, leaving your app in the lurch.

You need an alternative to TestFlight and App Store crash reporting for internal distributions – TestFlight gathers crash logs for you, which appear in the Xcode Organizer, but these are, naturally, only available for TestFlight builds. If you’re using Enterprise or Ad Hoc distribution, you need another way to gather development-build crash logs.

If none of these describe you, well, you should keep reading anyway, because what have you got to lose?

Crash Fundamentals

It seems obvious, but let’s ask the question: what is a crash? The video for WWDC 2008 session 414 says it well:

A crash is a sudden termination of your app when it attempts to do something that is not allowed.

There are a number of types of crashes. Let’s take a look at them.

First, there are assertions and preconditions. These occur when code, either in your own source or in libraries that your source is using, deliberately stops the process. For example:

  • Force unwrapping a Swift.Optional – The Swift compiler’s implementation of the ! postfix operator will terminate the process if code attempts to unwrap a .none value.
  • Out-of-bounds Swift.Array access – The standard library will trigger a precondition if the code attempts to access an array using an invalid index.
  • Swift arithmetic errors – Some arithmetic errors, like integer overflow, will cause the app to terminate.
  • Uncaught NSExceptions – Exceptions raised by Objective-C code will cause the app to terminate unless they are explicitly caught. This is particularly pernicious for Swift code since it is unable to catch NSExceptions.
  • Your assertions – Your own assertions and preconditions can cause the app to crash, too, if you’re using any.

Next, there are terminations by the operating system, i.e. something that results in your process receiving the SIGKILL signal. We’ll delve into signals more in the next post. Some examples of these terminations:

  • Watchdog timeout – If your app occupies the main thread without returning for too long, typically for ten seconds or more, then the OS will terminate your application.
  • Device overheating – If the device temperature is climbing too high, the OS will start lopping off the greediest processes to cool down the CPU.
  • Out of memory – If the process attempts to consume more memory than the OS is willing to provide it, the app will be terminated.
  • Invalid code signature – Ever tried launching an app with an expired provisioning profile? Anyone? Anyone? It is terminated the moment you launch it.

Last, but certainly not least, there are memory errors. Our old pal EXC_BAD_ACCESS. Memory errors are some of the most troublesome issues to debug, particularly when multi-threading or manual reference-counting is involved. Some examples:

  • Over-releasing a reference-counted object – Sending the objc_release message to an object that already has a zero reference count is not allowed.
  • Dereferencing a null pointer – Doing so has undefined behavior according to C standards, and in Objective-C will cause a crash due to a memory violation.
  • C array buffer overflow – Reading or writing beyond the bounds of an array is undefined behavior. By itself, it won’t crash, but it could lead to other memory access violations. Note that this differs from the assertions provided by Swift.Array and NSArray, which proactively look for out-of-bounds accesses.
  • Stack overflow – No, not that stack overflow. This stack overflow:

    Stack overflow refers specifically to the case when the execution stack grows beyond the memory that is reserved for it. For example, if you call a function which recursively calls itself without termination, you will cause a stack overflow as each function call creates a new stack frame and the stack will eventually consume more memory than is reserved for it. (Nick Meyer“What is the difference between a stack overflow and buffer overflow?”)

Obtaining Crash Logs

If an app crashes in the woods, but there was no log left behind, would anyone be able to fix it? A detailed, accurate crash log is necessary to correctly diagnose a crash. So what transpires between the moment a process is notified of an impending crash and when the crash log is written to disk? There are, on Apple platforms, two mechanisms that produce crash logs:

  • Out-of-process – The operating system’s own crash handler will capture information about a crashed process and will encode that information in a .crash log. Those logs are stashed in an OS-specific location for later use. This all happens outside of your app’s process. If the crashed app was distributed via Test Flight or the App Store, the crash log will also be uploaded to App Store Connect servers and can be viewed in the Xcode Organizer.
  • In-process – In addition to the operating system’s handler, your app can register its own crash handler. Unlike the system handler, however, your crash handler runs in the same process as the crashed code.

The differences between capturing crashes in-process and capturing them out-of-process is more than superficial. There are profound risks and challenges to overcome to safely and reliably capture crashes in-process. Like all third-party crash reporting tools, PLCrashReporter is obligated to run in-process. These challenges will be a recurring theme across future posts in this series.

Up Next

That’s it for the first post in the series! Next, we will discuss crash log creation, POSIX signals, Mach exceptions, and async-signal safety.

[1] From Plausible Labs website: “PLCrashReporter was the first crash logging solution available for iOS. Today, PLCrashReporter is used by most 3rd-party crash logging services for iOS and macOS, and can be found reporting crashes in the Netflix, Amazon Prime Video, Dropbox, Yahoo Mail, Kindle, and Chase Mobile iOS apps, as well as tens of thousands of other applications. With Microsoft’s purchase of HockeyApp in 2014, PLCrashReporter sits at the core of Microsoft’s crash logging solutions for Apple’s platforms. And in October 2019, Plausible and Microsoft reached an agreement to transfer stewardship of PLCrashReporter to the App Center team at Microsoft, where it will continue to be developed as an open-source project.

The post Own Your Crash Logs with PLCrashReporter: Part 1 appeared first on Big Nerd Ranch.

]]>
Migrating to Unified Logging, Swift Edition https://bignerdranch.com/blog/migrating-to-unified-logging-swift-edition/ https://bignerdranch.com/blog/migrating-to-unified-logging-swift-edition/#respond Mon, 17 Sep 2018 09:00:00 +0000 https://nerdranchighq.wpengine.com/blog/migrating-to-unified-logging-swift-edition/ Thinking of migrating your iOS or macOS app from a bunch of `NSLog` or `print` statements to the new(ish) Unified Logging system? Read this for some facts and tidbits that might surprise you, along with a few suggestions for how to make the most of your transition.

The post Migrating to Unified Logging, Swift Edition appeared first on Big Nerd Ranch.

]]>

Thinking of migrating your iOS or macOS app from a bunch of NSLog or print statements to the new(ish) Unified Logging system? Keep reading for some facts and tidbits that might surprise you, along with a few suggestions for how to make the most of your transition.

If you’re not sure why you’d want to use Unified Logging, here’s a quick run-down of some key benefits:

  1. It’s the new standard. The Unified Logging system is the biggest change to logging on Apple Platforms in years. It has all the telltale signs of something that Apple intends as the way to do logging going forward.

  2. Improve your app’s performance without sacrificing log coverage. The new logging system is designed from the ground up to limit the observer effect that logging has traditionally had on production code. You can now have it both ways: thorough log coverage and great performance.

  3. Upgrade your debugging workflow. Messages logged with the new APIs can be “tagged” with customized subsystems and categories which empowers Console.app to show or hide messages with expressive search filters. This can save you oodles of time when debugging complex issues that span more than one module or process.

I could go on with other benefits, but those will become apparent as we explore the differences between Unified Logging and what it replaces.

Four Ways Unified Logging is Different Than Legacy Logging Techniques

The following is not an exhaustive list of everything that’s new or different with Unified Logging, but rather a few key differences that may have a tremendous impact on how you go about migrating away from legacy logging functions.

1: OSLog isn’t a function. It’s a data type.

You will sometimes hear folks refer colloquially to Unified Logging as “oh-ess-log”, which might send you on a goose chase through the Foundation docs for an OSLog function. There isn’t one. The true analog to NSLog is any of the various functions like os_log that are defined in <os/log.h>, most of which take an os_log_t (a.k.a. OSLog via the Swift overlay) as an argument. The OSLog type is used to associate related messages so they can participate in Console.app’s search and filter features.

2: Console.app is mandatory.1

Unified Logging doesn’t serialize log messages in plain text, nor to a human-readable file format. Instead all messages are written to disk in an opaque data format that can only be read by opening a log archive (see below for how to obtain one) in Console.app. Console.app can unpack the archived messages, displaying them in a manner that is easy to search and filter. The opaqueness of the data format is a major departure from logging systems you may be familiar with. It was undertaken by Apple in an effort to limit the deleterious effects that logging traditionally has on performance and disk space.

3: From Swift, you can only pass a StaticString as a log message.

The following Swift code will not compile:

let foo = "Something happened."
os_log(foo) 

// Error: Cannot convert value of type 'String' to expected argument type 'StaticString'

That’s because the Swift compiler resolves the implicit type of an otherwise unconstrained string literal to String, but the os_log function requires a StaticString. You can fix it either by giving the variable an explicit type:

let foo: StaticString = "Something happened."
os_log(foo)

or by eliminating the variable:

os_log("Something happened.")

The static string string can also be a C-style format string:

os_log("We bolster %ld husk nuts to each girdle jerry.", 12)

Please note that you cannot use Swift’s pleasant string interpolation when logging a message:

let count = 12
os_log("We bolster (count) husk nuts to each girdle jerry.")

// Error: Cannot convert value of type 'String' to expected argument type 'StaticString'

This is probably the most significant departure you will encounter when migrating your Swift code from NSLog to Unified Logging. Be prepared for much dirt in your git working directory as you slog your way through substituting os_log calls for each NSLog or print call.

It is possible to log a String, but only as an argument to a static format string:

let count = 12
let string = "We bolster (count) husk nuts to each girdle jerry."
os_log("%@", string)

This workaround comes with a big gotcha, as we will see next.

4: Barring exceptions, your format arguments will be <redacted>.

By default when you log a String as a format argument:

os_log("What is %@?", "threeve")

the message will be rendered like this in Console.app:

Process Message
MyApp What is <redacted>?

To reveal the full log message in production logs, you have to explicitly mark that format argument as {public}:

os_log("What is %{public}@?", "threeve")

Then the log message will appear unredacted in your production logs:

Process Message
MyApp What is threeve?

Alternatively, you can achieve the same effect on a temporary basis without the {public} scope modifier by doing either of the following before running the app:

Some argument types do not require these workarounds. Scalar values — bools, integers, etc. — will default to an implied public scope when used as format arguments. You can also mark a scalar argument as {private} if you need to ensure that the value will be redacted in production, overriding the default public scope:

os_log("My secret ID is %{private}ld.", user.secretId)

Dos & Don’ts

Here are some things I consider best practices, in no particular order:

Do: Pay attention to log levels (types).

There are five standard log types, which mostly correspond to what used to be called “levels”, that are defined by the Unified Logging system. Here they are along with brief summaries cribbed directly from the official documentation:

  • default: Use this level to capture information about things that might result in a failure.
  • info: Use this level to capture information that may be helpful, but isn’t essential, for troubleshooting errors.
  • debug: Use this level to capture information that may be useful during development or while troubleshooting a specific problem.
  • error: Use this log level to capture process-level information to report errors in the process.
  • fault: Use this level to capture system-level or multi-process information to report system errors.

Choose the most appropriate type on a case-by-case basis, as any two given types are not treated equally by the logging system. This WWDC video has a helpful deep-dive into these differences.

Don’t: Use OSLog.default in shipping code.

You are not obligated to initialize your own OSLog instances. The OSLog.default value is available as a bare-bones alternative and is the default value for functions like os_log which require an OSLog argument. However, when you use OSLog.default, your ability to filter your log messages is limited because no values are provided for subsystem or category:

Image of a log message using the default log.

When you initialize your own OSLog, you provide it with a subsystem and a category. This makes it a snap to filter the visible output in Console.app:

Image of a log message using a custom log.

Do: Name your subsystems and categories consistently.

Adhere to a consistent naming convention across your application. It’s worth spending some time looking at how Apple chooses their values for these for logs emanating from their own processes as this should inform your own conventions. Here are my recommendations, which are drawn from Apple’s established patterns:

  1. Always name your subsystems using a reverse domain name style. All of Apple’s own logs have subsystems prefixed with com.apple, such as com.apple.Siri or com.apple.coredata.

  2. If your code is organized into frameworks, use the bundle ID of the calling module as the subsystem for all logs in that module, e.g. com.company.MyApp for application-level logs and com.company.MyApp.SomeFramework for framework-level logs.

  3. Do not use a reverse domain name style for a category name. Instead use a short, human-readable name like “Web Service”.

  4. Choose category names that help narrow the scope of the calling code within its module or that can associate related logs that span multiple files or subsystems. For example, if you have logs that are specific to only one Authenticator class within a custom framework, you might give its logs the category name Authenticator to be used exclusively by that class. Alternatively, if you have lots of authentication-related work spanning more than one class or more than one framework, you could have them all use a category name like Authentication, which would help you see authentication activity across your entire application.

Don’t: Hide your logs behind conditionals.

Because the rendering of the logs is done after the fact by Console.app, there’s no need to filter your log messages programmatically through #if directives or other conditionals. Log everything simply and directly, using an appropriate type, and let the system take care of the rest.

Do: Practice gathering a sysdiagnose.

Get really good at obtaining a sysdiagnose from hardware out in the wild. Full instructions are available here, but the gist of the process is:

  • Press and hold a bunch of buttons.
  • Wait ten minutes.
  • Navigate to a screen deep inside Settings.app
  • Tap the zipped sysdiagnose and AirDrop it to your Mac (it’ll be ~300MB in size).

Once you have the sysdiagnose on your Mac, you can open the .logarchive file it contains in Console.app and see a dump of all the logs on that device. If you’ve heeded my advice on categories and subsystems, you should be able to filter down to the information you need in short order.

[record needle scratch] Hold on. Did you say I have to wait ten minutes?

Yes, it can take up to ten minutes for sysdiagnose to appear after one has been requested. Because of that delay, you don’t want to make sysdiagnoses part of a daily debugging routine. Instead, sysdiagnoses are useful in situations like this:

  • A minimally tech-savvy customer reports a bug in production within a few minutes of the event. Walk them through the sysdiagnose steps and find a way for them to send it to you.

  • You or someone on your team encounters a bug while away-from-keyboard. Trigger a sysdiagnose immediately, and then grab it from the device once it’s back in the office.

Don’t: Force all format arguments to a public scope.

If you are used to reading all your log output in plain text, it can be a real jolt to have to deal with the Unified Logging system’s private-by-default policy. Resist the temptation to force all your format arguments to a {public} scope. Not only does that risk disclosing your customers’ private information (say, if they send a sysdiagnose to another company), but it also risks exposing your company’s secrets. It’s not difficult to imagine a scenario where an error log accidentally reveals your OAuth credentials in plain text in production logs.

Do: Watch these WWDC videos.

Both of these are required material for anyone interested in Unified Logging:

Departing Note

Image of a log message thanking the reader.

  1. Okay, technically you can also use Xcode’s console pane to view the output, but only if your app is connected to the debugger at the time. Xcode’s console pane has far fewer features than Console.app, so it’s not particularly useful for a deep examination of your logs.

The post Migrating to Unified Logging, Swift Edition appeared first on Big Nerd Ranch.

]]>
https://bignerdranch.com/blog/migrating-to-unified-logging-swift-edition/feed/ 0