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

原创 2012年03月22日 15:19:04

最近开发的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的子类,关于写法很简单,这里不再啰嗦。


IO学习(十五)PrintStream打印流

先看看PrintStream的继承体系,它是FileOutputStream的子类,是一个处理流 在理解PrintStream如何使用之前,先了解一下System类中三个字段 ...
  • Bazingaea
  • Bazingaea
  • 2016年05月21日 21:04
  • 487

java io系列16之 PrintStream(打印输出流)详解

本章介绍PrintStream以及 它与DataOutputStream的区别。我们先对PrintStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解。 转载请注明出处:ht...
  • wangtaomtk
  • wangtaomtk
  • 2016年09月13日 16:32
  • 457

java将控制台信息输出到文件

添加以下代码: import java.io.*; public class IO2File { public static void main(String[] args) throws...
  • tolcf
  • tolcf
  • 2016年03月17日 17:11
  • 6181

如何在写GUI程序时,将调试信息打印在控制台窗口

如果要是控制台窗口支持中文显示,sh
  • ztstupid
  • ztstupid
  • 2014年07月26日 01:59
  • 444

PrintStream类和PrintWriter类

PrintStream主要操作byte流,而PrintWriter用来操作字符流。读取文本文件时一般用后者。 java的一个字符(char)是16bit的,一个BYTE是8bit的 Prin...
  • woolnil
  • woolnil
  • 2013年06月28日 14:58
  • 3271

JAVA学习之PrintStream类

1.类说明 System.out是标准输出,是PrintStream的实例,一般可以理解为向控制台输出内容。 2.方法说明 3.使用示例 import java.io.PrintS...
  • u013555159
  • u013555159
  • 2016年06月15日 10:04
  • 400

使用PrintWriter(System.out)没有数据输出到控制台上的问题

问题:我采用服务器在每获取一个客户端的socket时就开启一个新的线程来处理这个请求,我把PrintWriter pw = new PrintWriter(System.out,true)这句放入到t...
  • yinbucheng
  • yinbucheng
  • 2016年07月27日 15:07
  • 1456

将文件中的数据读取,打印到控制台

读取方式一: FileReader fr=new FileReader("abc"); int ch=0; while((ch=fr.read())!=-1) { System.out....
  • fandoudou123
  • fandoudou123
  • 2015年09月13日 19:22
  • 237

java system.out.println() 详解(其中包,类,对象的关系) system.out 和 printstream类的关系浅谈

java.lang.system.out.printlb() java.lang 与 system.out.println()的关系 首先lang是一个包,system是类,Out是对象,...
  • evan_0097
  • evan_0097
  • 2016年10月04日 11:07
  • 1297

java PrintStream类

作用: 将基本数据类型的数据格式化成字符串输出。提供一系列的print和println方法PrintStream(OutputStream out,boolean autoflush)是否自动刷新。P...
  • xxs1984
  • xxs1984
  • 2011年03月21日 14:09
  • 1998
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:PrintStream类的学习与多目的地输出重定向问题
举报原因:
原因补充:

(最多只允许输入30个字)