The article draws a distinction between logging and print debugging, which it should, but in recent work that distinction has been less important to me in practice.
I mostly write Zig these days (love it) and the main thing I'm working on is an interactive program. So the natural way to test features and debug problems is to spin the demo program up and provide it with input, and see what it's doing.
The key is that Zig has a lazy compilation model, which is completely pervasive. If a branch is comptime-known to be false, it gets dropped very early, it has to parse but that's almost it. You don't need dead-code elimination if there's no dead code going in to that phase of compilation.
So I can be very generous in setting up logging, since if the debug level isn't active, that logic is just gone with no trace. When a module starts getting noisy in the logs, I add a flag at the top `const extra = false;`, and drop `if (extra)` in front of log statements which I don't need to have printing. That way I can easily flip the switch to get more detail on any module I'm investigating. And again, since that's a comptime-known dead branch, it barely impacts compiling, and doesn't impact runtime at all.
I do delete log statements where the information is trivial outside of the context of a specific thing I'm debugging, but the gist of what I'm saying is that logging and print debugging blend together in a very nice way here. This approach is a natural fit for this kind of program, I have some stubs for replacing live interaction with reading and writing to different handles, but I haven't gotten around to setting it up, or, as a consequence, firing up lldb at any point.
With the custom debug printers found in the Zig repo, 'proper' debugging is a fairly nice experience for Zig code as well, I use it heavily on other projects. But sometimes trace debugging / print debugging is the natural fit to the program, and I like that the language makes it basically free do use. Horses for courses.