Для отладки приложения, обычно, используется комплексный подход и он диктуется ситуацией.
Во-первых, для отладки поведения можно использоваться тем, что называется debugger - ставить точки прерывания и идти по шагам с проверкой, как меняются отслеживаемые данные.
Во-вторых, для отладки приложения используется логирование по уровням.
Логирование на уровне TRACE: вы пишите все, что считаете нужным, все, что вам поможет потом понять, как шел процесс. На практике, это огромное количество сообщений, полезность которых в обычной ситуации крайне низкая.
Логирование на уровне INFO: вы пишете информационные сообщения, к примеру, об смене состояния процесса.
И так далее, по уровням.
Ваша система логирования отсекает все то, что ниже приемлемого уровня, не фиксируя это.
Кроме вышеперечисленного, для отладки, а точнее, для того, чтобы быть уверенным, что все идет именно так, как должно идти, пишут тесты. Unit-тесты, интеграционные тесты, тесты UI и т.п. И все это, в совокупности (логирование и исполнение тестов), дает относительно полное понимание, что все идет так как надо, а если не идет - вы видите, что в тесте A все сломалось, а в логах - что сломалось именно.