7 ms·
I am also in the camp that has very little use for debuggers. A point that may be pedantic: I don't add (and then remove) "print" statements. I add logging cod
by geophile 1y ago
I am also in the camp that has very little use for debuggers.
A point that may be pedantic: I don't add (and then remove) "print" statements. I add logging code, that stays forever. For a major interface, I'll usually start with INFO level debugging, to document function entry/exit, with param values. I add more detailed logging as I start to use the system and find out what needs extra scrutiny. This approach is very easy to get started with and maintain, and provides powerful insight into problems as they arise.
I also put a lot of work into formatting log statements. I once worked on a distributed system, and getting the prefix of each log statement exactly right was very useful -- node id, pid, timestamp, all of it fixed width. I could download logs from across the cluster, sort, and have a single file that interleaved actions from across the cluster.
- hobs 1y agoA log is very different than a debugger though, one tells you what happened, one shows you the entire state and doesn't make you assemble it in your head.
- demosthanos 1y agoYour framing makes it sound like the log is worse in some way, but what the log gives you that the debugger makes you assemble in your head is a timeline of when things happen. Being able to see time is a pretty big benefit for most types of software. I can always drop an entire state object into the log if I need it, but the only way for a debugger to approximate what a log can give me is for me to step through a bunch of break points and hold the time stream in my head. The one place where a debugger is straight up better is if I know exactly which unit of code is failing and that unit has complicated logic that is worth stepping through line by line. That's what they were designed for, and they're very useful for that, but it's also not the most common kind of troubleshooting I run into.
- hobs 1y agoIt's not worse or better, but its not really comparable is all I am really saying, I would not use them for the same things.
- switchbak 1y agoIn the early 2000’s I whipped up a tool to convert log statements into visual swim lanes like the Chrome profiler does. That thing was a godsend for reasoning about complex parallelism.
- EFreethought 1y agoWould you be willing to share this tool?
- switchbak 1y agoOh that's long lost to the trashbin of history. As I recall it was a combination of: - Log4j + MDC's so we could get a timestamp and thread ID easily - some python scripting I can't remember if it was PDF or SVG, but I do remember having to choose something that would allow for huge graphs and lots of scrolling! These days there's better options on github, but nice to see I was on a decent path.
- fingerlocks 1y agoAll these print debugging advocates are blowing my mind. Are most people unaware that both lldb and gdb have conditional pass throughout breakpoints with function hooks? In other words, you can create a breakpoint that just prints its location and doesn’t pause execution. You can script this so all function entry/exists, or whatever, are logged without touching the code or needing to recompile. You can then bulk toggle these breakpoints, at runtime, so you only see a particular subset when things get interesting. Modifying the code to print stuff will feel barbaric after driving around a fine tuned debugging experience.
- dpkirchner 1y agoI can't tell you how many times lldb has failed to hit breakpoints or has dumped me in some library without symbols. This was in Xcode while writing an iOS app, maybe it's better in other environments. Print debugging, while not clever or powerful, has never once failed me.
- fingerlocks 1y agoSometimes the debugserver is flakey, I’ll give you that. But some that also sounds like UI quirks such as ambiguous breakpoints on a function definition with default initialized default values. You can attach lldb without Xcode. Or you can open the lldb terminal in Xcode, pause execution, and inspect the breakpoints manually
- mort96 1y agoI've never had a debugger show me the entire state. I'm not even sure I want to know the entire state, but GDB has a lot of issue with anything but the most basic data structures most of the time, and I always need to explicitly ask for what I want to see by calling things like operator[] (and then hope that the particular operator[] wasn't optimized out of the final binary). It's not exactly a great experience.
- switchbak 1y agoWhat I find annoying is how these async toolkits screw up the stack trace, so I have little what the real program flow looks like. That reduces much of the benefit off the top. Some IDEs promise to solve that, but I’ve not been impressed thus far. YMMV based on language/runtime/toolkit of course. This might get added to my wishlist for my next language of choice.
- AdieuToLogic 1y ago> A point that may be pedantic: I don't add (and then remove) "print" statements. I add logging code, that stays forever. For a major interface, I'll usually start with INFO level debugging, to document function entry/exit, with param values. This is an anti-pattern which results in voluminous log "noise" when the system operates as expected. To the degree that I have personally seen gigabytes per day produced by employing it. It also can litter the solution with transient concerns once thought important and are no longer relevant. If detailed method invocation history is a requirement, consider using the Writer Monad[0] and only emitting log entries when either an error is detected or in an "unconditionally emit trace logs" environment (such as local unit/integration tests). 0 - https://williamyaoh.com/posts/2020-07-26-deriving-writer-monad.html https://williamyaoh.com/posts/2020-07-26-deriving-writer-mon...
- strken 1y agoIt's absolutely not an anti-pattern if you have appropriate tools to handle different levels of logging, and especially not if you can filter debug output by area. You touch on this, but it's a bit strange to me that the default case is assumed to be "all logs all the time". I usually roll my own wrapper around an existing logging package, but https://www.npmjs.com/package/debug https://www.npmjs.com/package/debug is a good example of what life can be like if you're using JS. Want to debug your rate limiter? Write `DEBUG=app:middleware:rate-limiter npm start` and off you go.
- AdieuToLogic 1y ago> It's absolutely not an anti-pattern if you have appropriate tools to handle different levels of logging, and especially not if you can filter debug output by area. It is an anti-pattern due to what was originally espoused: I add logging code, that stays forever. For a major interface, I'll usually start with INFO level debugging, to document function entry/exit, with param values. There is no value for logging "function entry/exit, with param values" when all collaborations succeed and the system operates as intended. Note that service request/response logging is its own concern and is out of scope for this discussion. Also, you did not address the non-trivial cost implications of voluminous log output. > You touch on this, but it's a bit strange to me that the default case is assumed to be "all logs all the time". Regarding the above, production-ready logging libraries such as Logback[0], log4net[1], log4cpp[2], et al, allow for run-time configuration to determine what "areas" will have their entries emitted. So "all logs all the time" is a non sequitur in this context. What is relevant is the technique identified of emitting execution context when it matters and not when it doesn't. As to your `npm` example, I believe this falls under the scenario I explicitly identified thusly: ... an "unconditionally emit trace logs" environment (such as local unit/integration tests). 0 - https://logback.qos.ch/ https://logback.qos.ch/ 1 - https://logging.apache.org/log4net/index.html https://logging.apache.org/log4net/index.html 2 - https://log4cpp.sourceforge.net/ https://log4cpp.sourceforge.net/
- orthoxerox 1y agoThis sounds like TRACE level information to me.