Вывод через System.out - это не "плохо", а всего-навсего "плохо чаще чем хорошо". Если кто-то дает такой совет, то имеет ввиду при этом "по сравнению с использованием логгирующего фреймворка". На самом деле оба подхода имеют свои достоинства и недостатки, просто, как правило, для более-менее сложных и, особенно, чувствительных к производительности систем преимущества использования фреймворков в подавляющем большинстве случаев очевидно перевешивают недостатки.
Производительность.
Если делать вывод через System.out, он всегда будет синхронным, т.е. поток будет ждать завершения операции. И изменить это потом нельзя, не трогая код. Фреймворк же может буфферизировать вывод, сортировать в очереди и т.д. и, почти всегда по дефолту будет делать вывод в другом потоке. Разумеется, все это не бесплатно (память, потоки), но, по мере роста сложности системы, накладные расходы очень быстро компенсируются общим приростом производительности. Если же говорить только о, непосредственно, IO вывода в консоль, то абсолютные накладные расходы от использования фреймворка (для большинства случаев) пренебрежимо малы.
Удобство использования в разных сценариях.
Если код, например, изначально был под десктоп, а потом его понадобилось перенести на сервере, то вывод System.out, конечно, можно перенаправлять в файлы. Но это придется делать довольно отвратительными внешними костылями (ротация файлов, разные блокировки файлов на Win/Linux). Фреймворк, как правило, предоставляет из коробки решения не только этих очевидных проблем, но и кучу плюшек (типа вывода на серверы логов, в сокеты или даже SNMP), причем, делает это эффективнее среднестатистического костылестроителя и позволяет легко переключаться "на лету". Опять же, не бесплатно. Цена: одна лишняя (по сравнению с System.out, который будет работать всегда и везде) зависимость - от самого фреймворка, (а, реально, еще и ворох транзитивных). Однако, для систем с большим количеством других зависимостей этот недостаток практически незаметен.
Другой екстремальный сценарий: старый, давно проверенный и железобетонно-надежный серверный код, логгинг которого последние три года отправлялся в null, вдруг понадобилось использовать... в консольном приложении. А там System.out на System.out-е :) Та-дам!
Гибкость/Масштабируемость.
Фреймворки предоставляют наборы абстакций (аппендеры, очереди, категории, фильтры и т.д.), позволяющих, не трогая рабочий код, легко переконфигурировать логику вывода логов. Представьте на минутку кривизну костыля, который придется строить вокруг System.out, чтоб, например, временно дублировать в отдельный файл записи, выданные кодом из одного определенного пакета или класса, продублировать вывод на второй сервер логов или, сколько кода придется перелопатить, чтоб покрасить в красный цвет записи, содержащие слово CRITICAL... (а если одновременно под ANSI и под HTML?) :) И какова при этом будет вероятность нечаяно сломать этот костыль или этот код :)
Читаемость/Сопровождаемость кода.
Хоршие фреймворки обычно хорошо спроектированы и, при правильном использовании, позволяют безболезненно обновляться, расширять функционал или даже вообще заменяться на другие с минимальными затратами. И все это - совершенно прозрачно с т.з. программиста, которому достаточно просто вместо "System.out.println(" писать, например, "Logger.log(", и можно перестать нервничать по поводу логов и начать жить :)
Недостатки, собственно, вытекают из преимуществ. Мне, лично, приходят в голову только два с половиной сценария, в которых я бы точно не стал брать фреймворк: 1. написание очень маленькой, тривиальной программы, 1.5. написание иллюстрирующего кода (ответ на Тостере, пример использования чего-то в документации или в багрепорте и т.д.) и 2. написание бенчмарка (чтоб не грузить JVM "неизвестно чем").
Так что, собственно, абсолютно универсального совета "не использовать" нет - есть здравый смысл и конкретные потребности в конкретных случаях.