2010-11-25 3 views
12

Я использую своенравную библиотеку, которая, к сожалению, печатает информацию до System.out (или иногда System.err). Каков самый простой способ предотвратить это?«Отключить» выходной поток

Я думал о создании выходного потока в память, заменяя System.out и err перед каждым вызовом одного из способов устранения неполадок, позже их восстанавливать и просто игнорировать буфер созданного потока. Есть ли более простой и элегантный способ?

EDIT: Я не хочу перенаправлять весь вывод - это легко осуществить. Я просто хочу игнорировать вывод, потенциально генерируемый некоторыми вызовами библиотеки.

ответ

16

Я закончил тем, что делал что-то вроде:

PrintStream out = System.out; 
System.setOut(new PrintStream(new OutputStream() { 
    @Override public void write(int b) throws IOException {} 
})); 
try { 
    <library call> 
} finally { 
    System.setOut(out); 
} 

Благодаря AlexR и укладчик перенаправлении меня это короткое решение.

1
File file = new File(filename); 
PrintStream printStream = new PrintStream(new FileOutputStream(file)); 
System.setOut(printStream); 
+0

Так что просто выбросьте его в случайный файл? А потом удалите файл? Звучит немного расточительно, разве не лучше сбросить его в буфер памяти? – Oak 2010-11-25 22:12:43

+0

@Oak Если вас не интересуют сообщения - как продлить PrintStream и игнорировать все (пустые реализации)? – stacker 2010-11-25 22:32:23

+0

Подклассификация с пустой реализацией на самом деле является лучшей идеей до сих пор - очень простая, довольно короткая с анонимным классом. – Oak 2010-11-25 22:33:57

1

Существует 2 способа. 1. Самый простой способ - запустить вашу программу и перенаправить весь вывод на/dev/null (если вы в unix) или на специальный файл, который будет удален (для окон) Этот способ прост, но это означает, что все ваши выход ни к чему. Если ваша программа использует STDOUT для хороших вещей, вы не можете использовать этот способ.

  1. Вы можете установить STDOUT с помощью java API.

    System.setOut (уходит) System.setErr (уходит)

Теперь вы можете реализовать свой собственный OutputStream:

import java.io.IOException; 
import java.io.OutputStream; 
import java.util.regex.Pattern; 


public class FilterOutputStream extends OutputStream { 
    private OutputStream out; 
    private Pattern filterOut; 
    public FilterOutputStream(OutputStream out, Pattern filterOut) { 
     this.out = out; 
     this.filterOut = filterOut; 
    } 


    @Override 
    public void write(int b) throws IOException { 
     String callerClassName = new Throwable().getStackTrace()[1].getClassName(); 
     if (filterOut.matcher(callerClassName).find()) { 
      return; 
     } 
     out.write(b); 
    } 

} 

Это лучше, потому что вы можете отфильтровать ненужную выход и печать хорошая информация.

4

Способа избавиться от этих нежелательных отпечатков постоянно будет использовать байткод манипуляции удалить операторы печати из беспокойной библиотеки. Это можно сделать, например, используя ASM (или один из других более высоких уровней и более простой в использовании каркасы AOP).

Вы можете сделать это либо во время выполнения, либо в виде однократной операции перезаписи файлов классов библиотеки. Обратитесь к документации ASM, чтобы узнать, как это сделать. Вот доказательство концепции. Что он делает, так это то, что он заменяет все ссылки на System.out ссылкой на PrintStream, который ничего не делает.

Сначала испытания. Они используют некоторые служебные классы от my project, чтобы помочь в тестировании преобразований байт-кода (для тестирования требуется создание пользовательского загрузчика классов и применение преобразований байт-кода в нужный класс, но не любые другие классы).

package net.orfjackal.dimdwarf.aop; 

import net.orfjackal.dimdwarf.aop.conf.*; 
import org.junit.*; 
import org.objectweb.asm.*; 
import org.objectweb.asm.util.CheckClassAdapter; 

import java.io.*; 
import java.lang.instrument.ClassFileTransformer; 
import java.lang.reflect.*; 

import static org.hamcrest.MatcherAssert.assertThat; 
import static org.hamcrest.Matchers.*; 

public class RemoveCallsToSystemOutTest { 

    private PrintStream originalOut; 
    private ByteArrayOutputStream collectedOut; 

    @Before 
    public void collectSystemOut() { 
     originalOut = System.out; 
     collectedOut = new ByteArrayOutputStream(); 
     System.setOut(new PrintStream(collectedOut)); 
    } 

    @After 
    public void restoreOriginalSystemOut() { 
     System.setOut(originalOut); 
    } 

    @Test 
    public void the_target_class_prints_when_not_manipulated() throws Exception { 
     String safetyCheck = callPrintSomething(TroublesomePrinter.class); 

     assertThat(safetyCheck, is("it did execute")); 
     assertThat(collectedOut.size(), is(greaterThan(0))); 
    } 

    @Test 
    public void the_target_class_does_not_print_when_it_has_been_manipulated() throws Exception { 
     String safetyCheck = callPrintSomething(instrumentClass(TroublesomePrinter.class)); 

     assertThat(safetyCheck, is("it did execute")); 
     assertThat(collectedOut.size(), is(0)); 
    } 

    private static String callPrintSomething(Class<?> clazz) throws Exception { 
     Method m = clazz.getMethod("printSomething"); 
     m.setAccessible(true); 
     return (String) m.invoke(null); 
    } 

    private static Class<?> instrumentClass(Class<?> cls) throws ClassNotFoundException { 
     ClassFileTransformer transformer = new AbstractTransformationChain() { 
      protected ClassVisitor getAdapters(ClassVisitor cv) { 
       cv = new CheckClassAdapter(cv); 
       cv = new RemoveCallsToSystemOut(cv); 
       return cv; 
      } 
     }; 
     ClassLoader loader = new TransformationTestClassLoader(cls.getPackage().getName() + ".*", transformer); 
     return loader.loadClass(cls.getName()); 
    } 
} 

class TroublesomePrinter { 
    public static String printSomething() { 
     System.out.println("something"); 
     return "it did execute"; 
    } 
} 

И затем реализация. Обратите внимание, что вы не должны использовать этот код, не понимая его. Не следует program by coincidence.

class SilentSystem { 
    public static final PrintStream out = new PrintStream(new OutputStream() { 
     public void write(int b) { 
     } 
    }); 
} 

class RemoveCallsToSystemOut extends ClassAdapter { 

    public RemoveCallsToSystemOut(ClassVisitor cv) { 
     super(cv); 
    } 

    @Override 
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 
     return new MyMethodAdapter(super.visitMethod(access, name, desc, signature, exceptions)); 
    } 

    private static class MyMethodAdapter extends MethodAdapter { 
     public MyMethodAdapter(MethodVisitor mv) { 
      super(mv); 
     } 

     @Override 
     public void visitFieldInsn(int opcode, String owner, String name, String desc) { 
      if (opcode == Opcodes.GETSTATIC 
        && owner.equals("java/lang/System") 
        && name.equals("out") 
        && desc.equals("Ljava/io/PrintStream;")) { 
       super.visitFieldInsn(opcode, "net/orfjackal/dimdwarf/aop/SilentSystem", name, desc); 
      } else { 
       super.visitFieldInsn(opcode, owner, name, desc); 
      } 
     } 
    } 
} 
Смежные вопросы