Удаление доступа к System.out в Java - PullRequest
5 голосов
/ 10 ноября 2009

Я поддерживаю приложение, которое действует как контейнер для нескольких отдельных программ.Эти программы имеют свои собственные средства ведения журнала, то есть все, что они регистрируют, делает со специальным файлом журнала.

Тем не менее, разработчики приложений, похоже, любят , чтобы выбросить System.out.println, а e.printStackTrace вызывает всеболее, что делает невозможным поддерживать чистую консоль при запуске контейнера.

Как можно предотвратить загрязнение этих приложений System.out и System.err?


Замечания по реализации:

  • приложения используют Log4j для ведения журнала;
  • контейнер также использует консоль для ведения журнала, но он строго зарезервирован для событий и проблем жизненного цикла, поэтому мне все еще нужна консоль;
  • приложения загружаются с помощью пользовательского загрузчика классов, но проверки безопасности не применяются.

Обновление :

Простое перенаправление System.out не будет работать, поскольку он перенаправляет все выходные данные, поэтому что-то вроде этого не получается:

    System.setOut(new PrintStream(new OutputStream() {

        @Override
        public void write(int b) {

            throw new Error("Not on my watch you don't");

        }
    }));

    Logger logger = Logger.getLogger(Runner.class);
    logger.info("My log message");

Это должно быть успешно.

Обновление 2:

ПриложениеЗагрузка и настройка выполняется с использованием кода, аналогичного

App app = new UrlClassLoader(...).loadClass(className)).newInstance();
app.setLogger(loggerForClass(app));

Log4j загружается из загрузчика системного класса.

Ответы [ 12 ]

12 голосов
/ 10 ноября 2009

Предполагая, что вы можете контролировать вывод своих контейнеров, вы можете сделать следующее:

import java.io.*;
public class SysOut {
    public static void main(String[] args) throws Exception {
            PrintStream pw = new PrintStream(new FileOutputStream("a.txt"));
            PrintStream realout = System.out;
            System.setOut(pw);
            System.out.println("junk");
            realout.print("useful");
}
}

$ java SysOut 
useful
$ cat a.txt 
junk
10 голосов
/ 10 ноября 2009

Вы можете использовать System.setOut() и System.setErr() для перенаправления stdout и stderr на экземпляры PrintStream.

6 голосов
/ 10 ноября 2009

Хотя Java определяет стандартные System.out и System.err, они могут быть перезаписаны вашими собственными потоками. Смотри http://www.devx.com/tips/Tip/5616

По сути, вы можете настроить новые потоки, которые либо направляют в журнал, либо просто позволяют данным сваливаться в ничто. Мое предпочтение было бы последним, так как это мгновенно избавило бы разработчиков от необходимости полагаться на System.out и ошибаться, поскольку все, что они там пишут, просто исчезало.

** Обновление: Я просто перечитал ваши условия в вопросе и вижу, что вам все еще нужна консоль для контейнерного приложения. Это может сработать, даже если вы напишите обертку вокруг стандартного потока, чтобы вы могли проверить каждый вызов и посмотреть, поступает ли он из родительского приложения (и передать его) или дочернего приложения (и заблокировать его)

5 голосов
/ 10 ноября 2009

Используйте терапию отвращения. Посещение «Инспекторов» назначается всякий раз, когда проверяется любой код, содержащий неприятные конструкции.

Nice cubicle you got ere, be shame if anyfing appened to it.
3 голосов
/ 11 ноября 2009

System.setOut будет перенаправлять весь вывод - но предоставленный вами PrintStream может решить, как будет обрабатываться вывод. Таким образом, я уверен, что вы могли бы предоставить такой поток, который фактически печатал бы операторы из вашего приложения.

Единственная сложная вещь на самом деле заключается в возможности определить, что является допустимым вызовом, а что нет. Работающим, но, вероятно, очень медленным способом сделать это, было бы позвонить Thread.currentThread().getStackTrace() и посмотреть, какой код (или пакет, по крайней мере) вызывает вас (просто возвращая, если он не действителен). Я бы не рекомендовал это, так как падение производительности было бы ошеломляющим, особенно при каждом чтении байта.

Лучшей идеей может быть установить флаг ThreadLocal во всех ваших действительных, контейнерных потоках. Затем вы можете реализовать PrintStream примерно так:

public class ThreadValidity extends ThreadLocal<Boolean>
{
    private static final INSTANCE = new ThreadValidity();

    @Override Boolean initialValue() { return false; }
    public static ThreadValidity getInstance() { return INSTANCE; }
}

class VerifyingPrintStream extends PrintStream
{
    private boolean isValidThread()
    {
        return ThreadValidity.instance().get();
    }

    public void println(String s)
    {
        if (!isValidThread()) return;
        super.println(s);
    }

    public void println(Object o)
    {
        if (!isValidThread()) return;
        super.println(o);
    }

    // etc
}

В качестве альтернативы, если вы можете изменить println s в коде контейнера, все станет проще. Вы можете передать все записи консоли конкретному работнику; и попросите этого работника «украсть» System.out (сохраните его в своем собственном поле и используйте его непосредственно для записи выходных данных) при установке фактического System.out для неоперативного записывающего.

3 голосов
/ 11 ноября 2009

Если у вас есть безголовый механизм сборки, ant или что-то подобное, вы можете добавить CheckStyle к сборке и настроить checkstyle на сбой сборки, если он обнаружит в коде любой System.out.println или e.printStackTrace.

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

2 голосов
/ 18 ноября 2009

Ключевым моментом здесь является настройка log4j до перенаправления выходных потоков, например

BasicConfigurator.configure();
System.setOut(...);
System.setErr(...);

System.out.println("I fail");
Logger.getLogger(...).info("I work");
1 голос
/ 11 ноября 2009

Закройте потоки System.out и System.err.

1 голос
/ 11 ноября 2009

Вы можете получить и сохранить System.out / err перед их заменой.

OutputStream out=System.getOut();  // I think the names are right
System.setOut(some predefined output stream, null won't work);
out.println("Hey, this still goes to the output");
System.out.println("Oh noes, this does not");

Я использовал это для перехвата всех файлов System.out.println в кодовой базе и префикса каждой строки вывода с именем метода / номером строки, из которой он получен.

1 голос
/ 10 ноября 2009

Что я сделал, так это перенаправил PrintStream for System.out и System.err на ведение общего журнала в качестве журнала INFO и ERROR соответственно.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...