Как изменить вывод консоли ведения журналов Java с std err на std out?

Я использую стандартный ConsoleHandler из java.util.logging, и по умолчанию вывод консоли направляется в поток ошибок (т.е. System.err).

Как изменить вывод консоли на поток вывода (например, System.out)?


person Obediah Stane    schedule 11.10.2008    source источник


Ответы (13)


я прибыл в

 SimpleFormatter fmt = new SimpleFormatter();
 StreamHandler sh = new StreamHandler(System.out, fmt);
 logger.addHandler(sh);
person Frank Vlach    schedule 19.01.2011
comment
Это не отключает обработчик по умолчанию. Просто следуйте ответу stackoverflow.com/a/2533250/873282. - person koppor; 18.11.2012

Хм, я несколько раз укусил за ногу, пытаясь совершить этот подвиг. Прежде чем погуглить сюда, мне удалось наколдовать следующий лайфхак. Некрасиво, но, кажется, выполняет свою работу.

public class StdoutConsoleHandler extends ConsoleHandler {
  protected void setOutputStream(OutputStream out) throws SecurityException {
    super.setOutputStream(System.out); // kitten killed here :-(
  }
}

Осторожно: вызов setOutputStream() из конструктора заманчив, но он (как уже указал Джон Скит) закрывает System.err. Безумные навыки!

person volley    schedule 18.03.2011

Я придумал один способ. Сначала удалите обработчик консоли по умолчанию:

setUseParentHandlers (ложь);

Затем подкласс ConsoleHandler и в конструкторе:

setOutputStream(System.out);

person Obediah Stane    schedule 11.10.2008
comment
Вам лучше обернуть System.out самореализованным OutputStream, чтобы не закрывать его при следующем вызове setOutputStream(...) или LogManager.reset(). - person amotzg; 27.09.2012
comment
Кроме того, постарайтесь не вызывать суперконструктор (из ConsoleHandler), так как он устанавливает System.err, и вы закроете его при вызове setOutputStream(System.out). Или вы можете просто создать подкласс StreamHandler и вызвать super( OutputStream, Formatter). - person amotzg; 27.09.2012

У меня была аналогичная проблема. Я хотел записать 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, были очень полезными.)

person ocarlsen    schedule 18.05.2014
comment
Идеально. Надо сказать, что дизайнеры не совсем упростили задачу... почему они не могут предвидеть эту потребность? Имейте в виду, что то же самое и с другими фреймворками ведения журналов: ваша первая задача? Перейти через несколько сложных обручей! - person mike rodent; 16.04.2016

Ознакомьтесь с документацией и исходным кодом ConsoleHandler. — я уверен, что вы могли бы легко написать версию, которая просто использует System.err вместо System.out. (Жаль, что ConsoleHandler не позволяет это настроить, если честно.)

Тогда это просто случай настройки системы ведения журнала для использования вашего нового StdoutHandler (или как вы его называете) обычным способом.

person Jon Skeet    schedule 11.10.2008
comment
Я думаю, что ConsoleHandler используется по умолчанию, есть StreamHandler, который может печатать в любой другой поток. - person Uri; 11.10.2008
comment
Да, но я подозреваю, что вы захотите создать подкласс StreamHandler, чтобы избежать попыток закрыть System.err. - person Jon Skeet; 11.10.2008

Шаг 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();
     }
}
person Hari Krishna    schedule 03.06.2020
comment
Я не вижу журналы, пока приложение не закрыто. Вы хоть представляете, как посмотреть логи в реальном времени? - person akvyalkov; 03.11.2020

Если есть еще кто ищет решение этой проблемы. Вот что у меня получилось в итоге: я просто создал подкласс StreamHandler и добавил дополнительный параметр MaxLevel, который проверяется в начале publish(). Если уровень события регистрации больше, чем MaxLevel, дальнейшая публикация выполняться не будет. Вот подробности:

Основной класс 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();
    }
}

Основной класс

Чтобы теперь отображать некоторые события в stdout, а некоторые в stderr, просто настройте два 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.flush() сразу после super.publish(), чтобы убедиться, что сообщение отображается немедленно. Раньше у меня были проблемы с тем, что лог-сообщения всегда показывались в конце. Теперь это часть кода выше.

person Kai    schedule 18.04.2012

Если вы используете ведение журнала Java, вы можете изменить обработчик по умолчанию:

Например, для файлов: Handler fh = new FileHandler(FILENAME); Logger.getLogger(LOGGER_NAME).addHandler(fh);

Если вы хотите выводить в поток, вы можете использовать StreamHandler, я думаю, вы можете настроить его с любым потоком вывода, который вам нравится, включая системный поток.

person Uri    schedule 11.10.2008

Когда мы создаем новый объект 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);
            }
        };
person Virendra Singh    schedule 25.02.2017

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

Это предполагает, что код имеет разрешение на изменение потока ошибок и что никакой другой работающий код не обращается к потоку ошибок. Короче говоря, это вариант, но есть более безопасные альтернативы.

person jmehrens    schedule 10.03.2016

Если вы установите setUseParentHandlers(false); он установлен только в ЭТОМ классе. Другие классы в приложении все равно будут передавать его в stderr.

person Community    schedule 18.12.2008
comment
Если сделать это для корневого класса ведения журнала (Logger.GLOBAL_LOGGER_NAME), ведение журнала консоли будет отключено для всех классов. - person koppor; 18.11.2012

Просто расширьте StreamHandler и в конструкторе вызовите Super(System.out,). Это позволит избежать закрытия System.err - спасибо

person Hema    schedule 21.10.2009

person    schedule
comment
Но в наши дни... есть ли способ сделать это без реализации пользовательского обработчика?? - person Victor; 26.08.2013