Since heavily adopting Combine in some of our work, we’ve found debugging much harder than typical closure-based asynchronous code. Pyramid of Hell was, well, hell. On the flip side, we found reading those stack traces much easier. In Combine, it’s fairly well detailed that stack traces are super long or way too short, making it super difficult to reason about:
These little hiccups make selling a framework to your colleagues a little more difficult. So, I embarked on learning how to create a custom operator. My aim was to build an operator that could log file, function, line and
Output. That has led to
log — a very small OSLog-based operator.
It’s a small extension on
Publisher which performs a
map over the existing operator, logs out some useful information such as the file, line, function and the
Self.Output of your current publisher. For network requests, this is the
(data: Data, response: URLResponse). I’ve found that particularly useful for seeing what the URLResponse status code is, error, etc.
How do you use it?
Frustratingly, you do have to tack it onto your publisher streams at the point at which you wish to log. For my use case, that’s not too bad. We have a generic
get<T: Decodable> method through which all our GET request go. As such, all our network requests get logged at a low cost:
Now, we can see precisely which network call failed, where, and why. Secondly, because it’s using OSLog, we’re able to pull that information off test devices and inspect failures that are happening in beta! When a device is connected, we can see the stream of logs too:
As you can see, it pulls out the
get(request:)and we can view the output below. I’ve redacted a few bits for security reasons.