关闭

PrintStream类的学习与多目的地输出重定向问题

726人阅读 评论(0) 收藏 举报

最近开发的Dodo工具箱中需要一个日志功能,当然现在已经有很成熟很好用的Log4j,但是我觉得作为一个菜鸟,有必要自己开发一个简单的日志处理模块。下面简介该模块的设计与输出重定向这个难点。

用例设计:


类设计:


实现难点——输出重定向:

        我是这样设想的,日志来源于三个方面 1其他模块使用Logger记录的日志信息 2 系统执行过程中需要输出的信息 3 系统在运行过程中碰到的错误信息。日志输出的目的也有三个地方,第一:控制台 第二:日志文件 第三:gui即时信息窗口。

        如果只是单目的地的重定向十分简单,比如说想把System.err的信息重定向到文件out,只需在系统启动时调用如下两行即可:

PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream("out")));
System.setErr(out);
           

        现在的难点在于需要将错误信息输出到多个目的地(文件,控制台,gui窗口),没有一个适合的PrintStream的子类来完成这个任务,那么我们就需要继承PrintStream,并在这个子类中重新print方法来完成我们的输出逻辑,我定义的该类的名称为IORedirect,该类的源码如下:

package toolBox.core.utility;

import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.Date;
import java.util.Vector;

import com.ibm.icu.text.SimpleDateFormat;

/**
 * The class pipelines print/println's to several PrintStream. Useful for
 * directing system.out and system.err to external files etc.
 * 
 * @author jiangkai
 * 
 */
public class IORedirect extends PrintStream {
	
	protected static SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	
	/**The default target output stream*/
	protected PrintStream default_target=null;

	/**The different target output streams*/
	protected Vector<PrintStream> targets=null;
	
	/** */
	protected Vector<Boolean> appendHeader = null;
	
	/** */
	protected String prefix = null;
	
	/**
	 * initializes the object, with a default printstream.
	 */
	public IORedirect(PrintStream s) {
		this(s,"defaultoutput");
	}
	
	public IORedirect(PrintStream s,String p)
	{
		super(s);
		default_target = s;
		prefix = p;
		
		targets = new Vector<PrintStream>();
		appendHeader = new Vector<Boolean>();
		
		clear();
	}
	
	/**
	 * removes all streams and places the default printstream, if any, again in
	 * the list.
	 * 
	 * @see #getDefault()
	 */
	public void clear() {
		
		targets.clear();
		appendHeader.clear();
		
		if (getDefault() != null)
		{
			targets.add(getDefault());
			appendHeader.add(true);
		}
	}

	/**
	 * returns the default printstrean, can be NULL.
	 * 
	 * @return the default printstream
	 * @see #m_Default
	 */
	public PrintStream getDefault() {
		return default_target;
	}

	public void defaultPrint(String x)
	{
		if(default_target!=null)
			default_target.print(x);
	}
	
	public void defaultPrintln(String x)
	{
		if(default_target!=null)
			default_target.println(x);
	}
	
	/**
	 * adds the given PrintStream to the list of streams, with NO timestamp and
	 * NO prefix.
	 * 
	 * @param p
	 *            the printstream to add
	 */
	public void addPrintStream(PrintStream p) {
		addPrintStream(p,false);
	}

	public void addPrintStream(PrintStream p,boolean ah)
	{
		if (!targets.contains(p))
		{
			targets.add(p);
			appendHeader.add(ah);
		}
	}
	
	public void removePrintStream(PrintStream p) {
		if (targets.contains(p))
		{
			int index = targets.indexOf(p);
			targets.remove(index);
			appendHeader.remove(index);
		}
	}

	public boolean containsPrintStream(PrintStream p) {
		return targets.contains(p);
	}

	public int size() {
		return targets.size();
	}

	public void flush() {
		for (PrintStream element : targets)
			element.flush();
	}

	protected void printHeader()
	{
		for(PrintStream element:targets)
			if(appendHeader.get(targets.indexOf(element)))
				element.print(dateformat.format(new Date())+" "+prefix+"\n\t");
	}
	
	@Override
	public void print(int x) {
		printHeader();
		for (PrintStream element : targets)
			element.print(x);
		flush();
	}

	@Override
	public void print(long x) {
		printHeader();
		for (PrintStream element : targets)
			element.print(x);
		flush();
	}

	@Override
	public void print(float x) {
		printHeader();
		for (PrintStream element : targets)
			element.print(x);
		flush();
	}

	@Override
	public void print(double x) {
		printHeader();
		for (PrintStream element : targets)
			element.print(x);
		flush();
	}

	@Override
	public void print(boolean x) {
		printHeader();
		for (PrintStream element : targets)
			element.print(x);
		flush();
	}

	@Override
	public void print(char x) {
		printHeader();
		for (PrintStream element : targets)
			element.print(x);
		flush();
	}

	@Override
	public void print(char[] x) {
		printHeader();
		for (PrintStream element : targets)
			element.print(x);
		flush();
	}

	@Override
	public void print(String x) {
		printHeader();
		for (PrintStream element : targets)
			element.print(x);
		flush();
	}

	@Override
	public void print(Object x) {
		printHeader();
		for (PrintStream element : targets)
			element.print(x);
		flush();
	}

	@Override
	public void println(int x) {
		printHeader();
		for (PrintStream element : targets)
			element.println(x);
		flush();
	}

	@Override
	public void println(long x) {
		printHeader();
		for (PrintStream element : targets)
			element.println(x);
		flush();
	}

	@Override
	public void println(float x) {
		printHeader();
		for (PrintStream element : targets)
			element.println(x);
		flush();
	}

	@Override
	public void println(double x) {
		printHeader();
		for (PrintStream element : targets)
			element.println(x);
		flush();
	}

	@Override
	public void println(boolean x) {
		printHeader();
		for (PrintStream element : targets)
			element.println(x);
		flush();
	}

	@Override
	public void println(char x) {
		printHeader();
		for (PrintStream element : targets)
			element.println(x);
		flush();
	}

	@Override
	public void println(char[] x) {
		printHeader();
		for (PrintStream element : targets)
			element.println(x);
		flush();
	}

	@Override
	public void println(String x) {
		printHeader();
		for (PrintStream element : targets)
			element.println(x);
		flush();
	}

	@Override
	public void println(Object x) {
		printHeader();
		for (PrintStream element : targets)
			element.println(x);
		flush();
	}

	/**
	 * Writes <code>len</code> bytes from the specified byte array starting at
	 * offset <code>off</code> to this stream. If automatic flushing is enabled
	 * then the <code>flush</code> method will be invoked.
	 * 
	 * <p>
	 * Note that the bytes will be written as given; to write characters that
	 * will be translated according to the platform's default character
	 * encoding, use the <code>print(char)</code> or <code>println(char)</code>
	 * methods.
	 * 
	 * @param buf
	 *            A byte array
	 * @param off
	 *            Offset from which to start taking bytes
	 * @param len
	 *            Number of bytes to write
	 */
	public void write(byte buf[], int off, int len) {
		for (PrintStream element : targets)
			element.write(buf, off, len);
		flush();
	}

	/**
	 * Writes the specified byte to this stream. If the byte is a newline and
	 * automatic flushing is enabled then the <code>flush</code> method will be
	 * invoked.
	 * 
	 * <p>
	 * Note that the byte is written as given; to write a character that will be
	 * translated according to the platform's default character encoding, use
	 * the <code>print(char)</code> or <code>println(char)</code> methods.
	 * 
	 * @param b
	 *            The byte to be written
	 * @see #print(char)
	 * @see #println(char)
	 */
	public void write(int b) {
		for (PrintStream element : targets)
			element.write(b);
		flush();
	}

	public static void main(String[] args) throws FileNotFoundException
	{
		IORedirect stdRedirect = new IORedirect(System.err,"stderr");
//		stdRedirect.addPrintStream(System.err);
		System.setErr(stdRedirect);
		System.err.println("error");
		//stdRedirect.addPrintStream(System.out);
//		System.setOut(stdRedirect);
//		System.out.println("no");
	}
}
              现在一目了然了,我们的输出逻辑在于重写PrintStream类中的Print函数,在该函数中我们自己定义需要向那写输出流输出日志信息,我在这里的写法是
	@Override
	public void println(boolean x) {
		printHeader();
		for (PrintStream element : targets)
			element.println(x);
		flush();
	}
对于targets中的每一个输出流输出信息,当然,你可以在这边直接写文件输出的逻辑,窗口输出的逻辑,但是当前这种写法使得IORedirect这个类更加通用,不管你是什么输出流,只要注册到该IORediect类即可。

          下面贴出该类的使用办法:

		IORedirect stdoutRedirect = new IORedirect(System.out, "stdout");
		System.setOut(stdoutRedirect);
		stdoutRedirect.addPrintStream(m_windowStream, true);
		stdoutRedirect.addPrintStream(m_FileStream,true);


这样,即将m_windowStream和m_FileStream注册到了该重定向对象中,这样系统的输出会输出到m_windowStream和m_FileStream这两个输出流中去,当然了,m_windowStream和m_FileStream也必须是PrintStream的子类,关于写法很简单,这里不再啰嗦。


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:223658次
    • 积分:1890
    • 等级:
    • 排名:千里之外
    • 原创:34篇
    • 转载:35篇
    • 译文:0篇
    • 评论:12条
    最新评论