System.setOut
будет перенаправлять весь вывод - но предоставленный вами PrintStream может решить, как будет обрабатываться вывод. Таким образом, я уверен, что вы могли бы предоставить такой поток, который фактически печатал бы операторы из вашего приложения.
Единственная сложная вещь на самом деле заключается в возможности определить, что является допустимым вызовом, а что нет. Работающим, но, вероятно, очень медленным способом сделать это, было бы позвонить Thread.currentThread().getStackTrace()
и посмотреть, какой код (или пакет, по крайней мере) вызывает вас (просто возвращая, если он не действителен). Я бы не рекомендовал это, так как падение производительности было бы ошеломляющим, особенно при каждом чтении байта.
Лучшей идеей может быть установить флаг ThreadLocal во всех ваших действительных, контейнерных потоках. Затем вы можете реализовать PrintStream примерно так:
public class ThreadValidity extends ThreadLocal<Boolean>
{
private static final INSTANCE = new ThreadValidity();
@Override Boolean initialValue() { return false; }
public static ThreadValidity getInstance() { return INSTANCE; }
}
class VerifyingPrintStream extends PrintStream
{
private boolean isValidThread()
{
return ThreadValidity.instance().get();
}
public void println(String s)
{
if (!isValidThread()) return;
super.println(s);
}
public void println(Object o)
{
if (!isValidThread()) return;
super.println(o);
}
// etc
}
В качестве альтернативы, если вы можете изменить println
s в коде контейнера, все станет проще. Вы можете передать все записи консоли конкретному работнику; и попросите этого работника «украсть» System.out (сохраните его в своем собственном поле и используйте его непосредственно для записи выходных данных) при установке фактического System.out для неоперативного записывающего.