12

残念ながら、System.out(または時々System.err)に情報を出力する気まぐれなライブラリを使用しています。これを防ぐ最も簡単な方法は何ですか?

私は、メモリへの出力ストリームを作成し、問題を引き起こすメソッドのいずれかを呼び出すたびに置き換えSystem.out、後でそれらを復元し、作成されたストリームのバッファを無視することを考えていました。errもっと簡単でエレガントな方法はありますか?

編集:すべての出力をリダイレクトしたくありません。これは簡単に実現できます。特定のライブラリ呼び出しによって潜在的に生成される出力を無視したいだけです。

4

4 に答える 4

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 と stacker に感謝します。

于 2010-11-28T10:53:44.853 に答える
4

これらの不要な印刷を永久に取り除く方法は、バイトコード操作を使用して、厄介なライブラリから印刷ステートメントを削除することです。これは、たとえばASM (または他の高レベルで使いやすいAOPフレームワークのいずれか) を使用して実行できます。

これは、実行時に行うことも、ライブラリのクラス ファイルを書き換える 1 回限りの操作として行うこともできます。方法については、ASM のドキュメントを参照してください。これが概念実証です。System.outそれが行うことは、すべての参照を、何もしないa への参照に置き換えることPrintStreamです。

まずテスト。私のプロジェクトのいくつかのユーティリティ クラスを使用して、バイトコード変換のテストを支援します (テストには、カスタム クラス ローダーを作成し、バイトコード変換を適切なクラスに適用する必要がありますが、他のクラスには適用しません)。

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";
    }
}

そして実装です。このコードを理解せずに使用しないでください。偶然にプログラムしないでください。

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);
            }
        }
    }
}
于 2010-11-25T23:16:53.613 に答える
1

2つの方法があります。1.最も簡単な方法は、プログラムを実行し、すべての出力を/ dev / null(UNIXの場合)または削除される特別なファイル(Windowsの場合)にリダイレクトすることです。この方法は簡単ですが、すべての出力が何もしません。プログラムがSTDOUTを良い目的で使用している場合、この方法を使用することはできません。

  1. JavaAPIを使用してSTDOUTを設定できます。

    System.setOut(out)System.setErr(out)

これで、独自の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);
    }

}

これは、無関係な出力を除外して適切な情報を出力できるため、より優れています。

于 2010-11-25T22:13:50.697 に答える
1
File file  = new File(filename);
PrintStream printStream = new PrintStream(new FileOutputStream(file));
System.setOut(printStream);
于 2010-11-25T22:06:09.380 に答える