Debuggers are often mildly useful, because they halt a program which usually must run real-time.
UART is still pretty common for console, logging or tracing etc. I often use the latter. Console/logging in my view is printing human-readable text over serial so you can see whats going on. This often includes the use of printf() and whatnot, which is often quite slow. For faster routines, you could also use tracing libraries. Segger has SystemView using RTT transport, or you could roll your own library. I did the latter as its integrated in my build system capturing trace messages and then performing the printf on the host machine. The buffer can either be transmitted via an UART or via a debug probe (e.g. RTT).
If tracing is still problematic, then a last resort could be to use a SPI bus at max bitrate, and send individual data bytes for debugging, and trace+decode them with a scope/LA. You could use GPIOs to sync up to the serial stream. Its far more invasive form of debugging, but if you need to trace data from a very short IRQ, it may be the only viable way to do it.
I never bother with USB CDCs for debugging. Perhaps for field diagnostics (such as a serial console with hidden commands), but other than that I would only use USB CDCs for the operational aspect of a board.
Its far easier to use one of the many dozen USB-UART converters (the high-speed FTDI chips can do 12Mbaud+).