Воссоздайте System.out для повторной печати в КОНСОЛИ после System.out.close()

У меня есть настольное приложение, когда происходит зависание на несколько минут, есть поток, который отслеживает зависание и начинает сбрасывать трассировки стека всех потоков (это делается в собственном вызове, чтобы можно было вызвать JVM_DumpAllStacks) во временный файл. Затем временный файл считывается как строка после собственного вызова и используется для входа в собственную структуру ведения журнала приложения.

Проблема в том, что после всего этого процесса я не могу восстановить System.out в поток CONSOLE.

Это лучше объясняется в приведенном ниже коде.

public String getAllStackTraces() {

System.out.println("This will be printed in CONSOLE");

// This is NECESSARY for the jvm to dump stack traces in specific file which we are going to set in System.setOut call.
System.out.close(); 

File tempFile = File.createTempFile("threadDump",null,new File(System.getProperty("user.home")));
System.setOut(new PrintStream(new BufferedOutputStream(new FileOuptputStream(tempFile))));

//This native call dumps stack traces all threads to tempFile
callNativeMethodToDumpAllThreadStackTraces(); 

String stackTraces = readFileAsString(tempFile);
//close the tempFile PrintStream so as the next PrintStream object to set as 'out' and to take effect in the native side as well
System.out.close(); 

//Now I want to start printing in the CONSOLE again. How to do it again ?
//The below line does not work as FileDescriptor.out becomes invalid (i.e FileDescriptor.out.fd, handle = -1) after we do System.out.close() where out is PrintStream of console.
//System.setOut(new PrintStream(new BufferedOutputStream(new FileOuptputStream(FileDescriptor.out))));

PrintStream standardConsoleOutputStream = magicallyGetTheOutputStream() // How ???????????
System.setOut(standardConsoleOutputStream);
System.out.println("This will be printed in CONSOLE !ONLY! if we are able to get the new PrintStream of Console again magically");
} 

Теперь, есть ли способ magicallyGetTheOutputStream из консоли, чтобы снова начать печать в консоли?

Примечание. Приложение работает в Java 5 и 6.


person Senthilkumar Annadurai    schedule 09.12.2015    source источник
comment
Не можете ли вы сохранить существующий System.out, установить новый, а затем просто переназначить старый?   -  person Jan    schedule 09.12.2015
comment
Почему вы не используете какую-либо библиотеку журналов?   -  person Everv0id    schedule 09.12.2015
comment
@Ян Я не могу снова использовать ту же System.out консоли, потому что она закрыта, я должен каким-то образом снова получить новую PrintStream консоли, чтобы установить через System.setOut, и поэтому, где бы я ни делал System.out.println("something"), он будет печатать в консоли   -  person Senthilkumar Annadurai    schedule 09.12.2015
comment
@Everv0id, не могли бы вы привести мне пример, в котором какая-то библиотека протоколирования выводит все трассировки стека потоков.   -  person Senthilkumar Annadurai    schedule 09.12.2015
comment
@SenthilkumarAnnadurai просто не закрывайте System.out   -  person Everv0id    schedule 09.12.2015


Ответы (1)


Рассмотрим этот код, как сохранить оригинальный System.out не закрывая, чтобы позже восстановить его во всей красе:

    //Store, don't close
    PrintStream storeForLater = System.out;
    //Reassign
    System.out(setToNew);
    ...
    //Close reassigned
    setToNew.close();
    //Reset to old
    System.setOut(storeForLater);

В качестве альтернативы собственному коду вы можете вызвать ThreadMXBean. Возвращаемые объекты ThreadInfo содержат информацию о Блокировки удерживаются и блокировки, которых ожидает поток.

public static void dumpThreads(PrintStream out) {
    ThreadInfo[] threads = ManagementFactory.getThreadMXBean()
            .dumpAllThreads(true, true);
    for(final ThreadInfo info : threads) {
        out.println("Thread: " + info.getThreadId() 
         + "/" + info.getThreadName()
         + " in State " + info.getThreadState().name());
        if(info.getLockName() != null) {
            out.println("- Waiting on lock: " + info.getLockInfo().toString()
                     + " held by " + info.getLockOwnerId()+"/"+info.getLockOwnerName());
        }
        for(MonitorInfo mi :  info.getLockedMonitors()) {
            out.println(" Holds a lock on a " + mi.getClassName() + 
                    " from " + mi.getLockedStackFrame().getClassName()+"."+mi.getLockedStackFrame().getMethodName() 
                    + ": " + mi.getLockedStackFrame().getLineNumber());
        }
        for(StackTraceElement elm : info.getStackTrace()) {
            out.println("   at " + elm.getClassName() + "."
                    + elm.getMethodName() + ":"+elm.getLineNumber());
        }
        out.println();
    }
}
person Jan    schedule 09.12.2015
comment
Это не работает, и нативный код по-прежнему печатается в консоли вместо setToNew. - person Senthilkumar Annadurai; 09.12.2015
comment
Собственный код?? Почему бы вам не перебрать Thread.getAllStacKTraces() вместо этого? docs.oracle.com/javase/ 7/docs/api/java/язык/ - person Jan; 09.12.2015
comment
Это хорошая идея, за исключением того, что в ситуации тупиковой ситуации теряется некоторая информация, например информация об объекте, которого ожидает конкретный поток, и объекте, блокировку которого он получил. Хотя можно потратить время и определить вещи, это усложнит задачу, если стек большой и повторяющиеся имена классов и методов в стеке, а также если взаимоблокировка включает более двух потоков. Код выполняется при зависании графического интерфейса, когда вероятность взаимоблокировки высока. 'JVM_DumpAllStacks' фактически делает дамп потока и предоставляет более точную информацию. - person Senthilkumar Annadurai; 09.12.2015
comment
см. мое изменение для использования ThreadMXBean для включения информации о блокировке без собственного кода. - person Jan; 09.12.2015
comment
Это не работает, и собственный код по-прежнему печатается в консоли вместо setToNew, и вы этого не ожидаете, только собственный код, который вызывает обратно в Java для использования System.out, который перенаправляется на то, на что вы установили System.out. Меняя это поле, просто меняйте это поле, больше ничего волшебного не происходит. Если у вас есть собственный код, который печатает непосредственно на консоль или любой другой файловый дескриптор, это не затрагивается. - person Peter Lawrey; 12.12.2015
comment
Вы пробовали с JMX, как я предложил? Вы нашли другой способ? - person Jan; 15.12.2015