【发布时间】:2008-10-11 15:00:11
【问题描述】:
我使用来自java.util.logging 的标准ConsoleHandler,默认情况下控制台输出被定向到错误流(即System.err)。
如何将控制台输出更改为输出流(即System.out)?
【问题讨论】:
我使用来自java.util.logging 的标准ConsoleHandler,默认情况下控制台输出被定向到错误流(即System.err)。
如何将控制台输出更改为输出流(即System.out)?
【问题讨论】:
我到了
SimpleFormatter fmt = new SimpleFormatter();
StreamHandler sh = new StreamHandler(System.out, fmt);
logger.addHandler(sh);
【讨论】:
嗯,我只是被咬了几次,试图完成这项壮举。在用谷歌搜索我的方式之前,我设法想到了以下黑客攻击。丑陋,但它似乎完成了工作。
public class StdoutConsoleHandler extends ConsoleHandler {
protected void setOutputStream(OutputStream out) throws SecurityException {
super.setOutputStream(System.out); // kitten killed here :-(
}
}
注意:从构造函数调用 setOutputStream() 很诱人,但它确实(正如 Jon Skeet 已经指出的那样)关闭 System.err。疯狂的技能!
【讨论】:
我想出了一种方法。首先删除默认的控制台处理程序:
setUseParentHandlers(false);
然后继承ConsoleHandler并在构造函数中:
setOutputStream(System.out);
【讨论】:
OutputStream 包裹System.out 以避免在下次调用setOutputStream(...) 或LogManager.reset() 时关闭它。
ConsoleHandler 的)超级构造函数,因为它设置了 System.err,并且在调用 setOutputStream(System.out) 时将关闭它。或者,您可以只继承 StreamHandler 并调用 super( OutputStream, Formatter)。
Handler consoleHandler = new Handler(){
@Override
public void publish(LogRecord record)
{
if (getFormatter() == null)
{
setFormatter(new SimpleFormatter());
}
try {
String message = getFormatter().format(record);
if (record.getLevel().intValue() >= Level.WARNING.intValue())
{
System.err.write(message.getBytes());
}
else
{
System.out.write(message.getBytes());
}
} catch (Exception exception) {
reportError(null, exception, ErrorManager.FORMAT_FAILURE);
}
}
@Override
public void close() throws SecurityException {}
@Override
public void flush(){}
};
【讨论】:
我遇到了类似的问题。我想将 INFO 及以下记录到System.out,将WARNING 及以上记录到System.err。这是我实施的解决方案:
public class DualConsoleHandler extends StreamHandler {
private final ConsoleHandler stderrHandler = new ConsoleHandler();
public DualConsoleHandler() {
super(System.out, new SimpleFormatter());
}
@Override
public void publish(LogRecord record) {
if (record.getLevel().intValue() <= Level.INFO.intValue()) {
super.publish(record);
super.flush();
} else {
stderrHandler.publish(record);
stderrHandler.flush();
}
}
}
当然,例如,您可以通过分解对Level.INFO 的硬编码引用来使其更加灵活。但这对我来说很有效,可以获得一些基本的双流日志记录。 (顺便说一句,关于不继承 ConsoleHandler 以避免关闭 System.err 的提示非常有用。)
【讨论】:
第 1 步:将父处理程序设置为 false。
log.setUseParentHandlers(false);
第 2 步:添加写入 System.out 的处理程序
log.addHandler(new StreamHandler(System.out, new SimpleFormatter()));
就是这样..
import java.io.IOException;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;
public class App {
static final Logger log = Logger.getLogger("com.sample.app.App");
static void processData() {
log.info("Started Processing Data");
log.info("Finished processing data");
}
public static void main(String args[]) throws IOException {
log.setUseParentHandlers(false);
log.addHandler(new StreamHandler(System.out, new SimpleFormatter()));
processData();
}
}
【讨论】:
查看ConsoleHandler 的文档和源代码 - 我相信您可以轻松编写一个仅使用 System.err 而不是 System.out 的版本。 (说实话,ConsoleHandler 不允许配置它,这很可惜。)
那么这只是配置日志系统以正常方式使用您的新 StdoutHandler(或您所称的任何名称)的情况。
【讨论】:
如果还有人在寻找解决此问题的方法。 这是我最终想到的:我只是将 StreamHandler 子类化并添加了一个附加参数 MaxLevel,在 publish() 开始时检查。如果 logging event 的 level 大于 MaxLevel,则不会再执行 publish。 以下是详细信息:
MaxlevelStreamHandler.java 主类如下。
package helper;
/**
* The only difference to the standard StreamHandler is
* that a MAXLEVEL can be defined (which then is not published)
*
* @author Kai Goergen
*/
import java.io.PrintStream;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.StreamHandler;
public class MaxlevelStreamHandler extends StreamHandler {
private Level maxlevel = Level.SEVERE; // by default, put out everything
/**
* The only method we really change to check whether the message
* is smaller than maxlevel.
* We also flush here to make sure that the message is shown immediately.
*/
@Override
public synchronized void publish(LogRecord record) {
if (record.getLevel().intValue() > this.maxlevel.intValue()) {
// do nothing if the level is above maxlevel
} else {
// if we arrived here, do what we always do
super.publish(record);
super.flush();
}
}
/**
* getter for maxlevel
* @return
*/
public Level getMaxlevel() {
return maxlevel;
}
/**
* Setter for maxlevel.
* If a logging event is larger than this level, it won't be displayed
* @param maxlevel
*/
public void setMaxlevel(Level maxlevel) {
this.maxlevel = maxlevel;
}
/** Constructor forwarding */
public MaxlevelStreamHandler(PrintStream out, Formatter formatter) {
super(out, formatter);
}
/** Constructor forwarding */
public MaxlevelStreamHandler() {
super();
}
}
主类
现在要在标准输出和标准错误中显示一些事件,只需设置两个 StreamLogger,一个用于关键事件,一个用于所有其他事件,并禁用标准控制台记录器:
// setup all logs that are smaller than WARNINGS to stdout
MaxlevelStreamHandler outSh = new MaxlevelStreamHandler(System.out, formatter);
outSh.setLevel(Level.ALL);
outSh.setMaxlevel(Level.INFO);
logger.addHandler(outSh);
// setup all warnings to stdout & warnings and higher to stderr
StreamHandler errSh = new StreamHandler(System.err, formatter);
errSh.setLevel(Level.WARNING);
logger.addHandler(errSh);
// remove default console logger
logger.setUseParentHandlers(false);
logger.info("info");
logger.warning("warning");
logger.severe("severe");
希望这会有所帮助!
更新:我在 super.publish() 之后添加了 super.flush() 以确保消息立即显示。以前,我遇到过日志消息总是显示在最后的问题。它现在是上面代码的一部分。
【讨论】:
如果您使用 Java 日志记录,您可以更改默认处理程序:
例如,对于文件: 处理程序 fh = new FileHandler(FILENAME); Logger.getLogger(LOGGER_NAME).addHandler(fh);
如果你想输出到一个流,你可以使用 StreamHandler,我想你可以用任何你喜欢的输出流来配置它,包括系统流。
【讨论】:
当我们创建一个新的 ConsoleHandler 对象时,默认输出流是“system.err”。遗憾的是,Java 没有为 ConsoleHandler 类提供任何公共方法来设置输出流。所以只能在创建对象的时候设置。由于 ConsoleHandler 类扩展了 StreamHandler,它有一个受保护的方法“setOutputStream”来显式设置输出流。要为 ConsoleHandler 设置输出流,只需在新调用对象创建时覆盖此方法。
ConsoleHandler consoleHandler = new ConsoleHandler (){
@Override
protected synchronized void setOutputStream(OutputStream out) throws SecurityException {
super.setOutputStream(System.out);
}
};
【讨论】:
ConsoleHandler 将在构造过程中获取System.err 的快照。一种选择是将全局错误流与全局输出流交换,然后创建 ConsoleHandler。
ConsoleHandler h = null;
final PrintStream err = System.err;
System.setErr(System.out);
try {
h = new ConsoleHandler(); //Snapshot of System.err
} finally {
System.setErr(err);
}
这假定代码有权修改错误流并且没有其他正在运行的代码正在访问错误流。简而言之,这是一种选择,但还有更安全的选择。
【讨论】:
如果你设置了 setUseParentHandlers(false);只有那个类有它设置。应用程序中的其他类仍会将其传递给 stderr。
【讨论】:
只需在构造函数调用 Super(System.out,) 中扩展 StreamHandler &。这将避免关闭 System.err - 谢谢
【讨论】: