Настройка ведения журнала Java FileHandler для создания каталогов, если они не существуют

Я пытаюсь настроить Java Logging API FileHandler, чтобы зарегистрировать мой сервер в файле в папке в моем домашнем каталоге, но я не хочу создавать эти каталоги на каждой машине, на которой он работает.

Например, в файле logging.properties я указываю:

java.util.logging.FileHandler
java.util.logging.FileHandler.pattern=%h/app-logs/MyApplication/MyApplication_%u-%g.log

Это позволит мне собирать журналы в моем домашнем каталоге (% h) для MyApplication и вращать их (используя переменные% u и% g).

Log4j поддерживает это, когда я указываю в своих log4j.properties:

log4j.appender.rolling.File=${user.home}/app-logs/MyApplication-log4j/MyApplication.log

Похоже, что есть ошибка против Logging FileHandler: Ошибка 6244047: невозможно указать drictorys для регистрации FileHandler, если они не существуют

Похоже, что они не планируют его исправлять или не раскрывают какие-либо свойства для решения этой проблемы (помимо того, что ваше приложение анализирует logging.properties или жесткий код, необходимый для этого пути):

Похоже, что java.util.logging.FileHandler не делает ожидайте, что указанный каталог может не существовать. Обычно он должен проверьте это состояние в любом случае. Кроме того, это должен проверить запись в каталоге разрешений. Другой вопрос что делать, если одна из этих проверок не проходит.

Одна из возможностей - создать отсутствующие каталоги в пути, если пользователь имеет соответствующие разрешения. Другая заключается в том, чтобы выбросить исключение IO с помощью ясное сообщение, что не так. последний подход выглядит более согласованным.

Ответ 1

Кажется, что log4j версия 1.2.15 делает это.

Вот фрагмент кода, который делает это

public
 synchronized
 void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
                                                        throws IOException {
    LogLog.debug("setFile called: "+fileName+", "+append);

    // It does not make sense to have immediate flush and bufferedIO.
    if(bufferedIO) {
      setImmediateFlush(false);
    }

    reset();
    FileOutputStream ostream = null;
    try {
          //
          //   attempt to create file
          //
          ostream = new FileOutputStream(fileName, append);
    } catch(FileNotFoundException ex) {
          //
          //   if parent directory does not exist then
          //      attempt to create it and try to create file
          //      see bug 9150
          //
          String parentName = new File(fileName).getParent();
          if (parentName != null) {
             File parentDir = new File(parentName);
             if(!parentDir.exists() && parentDir.mkdirs()) {
                ostream = new FileOutputStream(fileName, append);
             } else {
                throw ex;
             }
          } else {
             throw ex;
          }
    }
    Writer fw = createWriter(ostream);
    if(bufferedIO) {
      fw = new BufferedWriter(fw, bufferSize);
    }
    this.setQWForFiles(fw);
    this.fileName = fileName;
    this.fileAppend = append;
    this.bufferedIO = bufferedIO;
    this.bufferSize = bufferSize;
    writeHeader();
    LogLog.debug("setFile ended");
}

Этот фрагмент кода из FileAppender, RollingFileAppender расширяет FileAppender.

Здесь не проверяется, есть ли у нас разрешение на создание родительских папок, но если родительские папки не существуют, то он попытается создать родительские папки.

отредактированы

Если вам нужна дополнительная функциональность, вы всегда можете расширить RollingFileAppender и переопределить метод setFile().

Ответ 2

Вы можете написать что-то вроде этого.

package org.log;

import java.io.IOException;
import org.apache.log4j.RollingFileAppender;

public class MyRollingFileAppender extends RollingFileAppender {

    @Override
    public synchronized void setFile(String fileName, boolean append,
        boolean bufferedIO, int bufferSize) throws IOException {
        //Your logic goes here
        super.setFile(fileName, append, bufferedIO, bufferSize);
    }

}

Затем в вашей конфигурации

log4j.appender.fileAppender=org.log.MyRollingFileAppender

Это отлично работает для меня.

Ответ 3

Чтобы обойти ограничения инфраструктуры Java Logging и нерешенную ошибку: Ошибка 6244047: невозможно указать drictorys для регистрации FileHandler, если они не существуют

Я придумал два подхода (хотя на самом деле работает только первый подход), оба требуют, чтобы ваш статический метод void main() для вашего приложения инициализировал систему ведения журнала.

например.

public static void main(String[] args) {    

    initLogging();
     ...
    }

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

private static void initLogging() {
    try {
        //Create logging.properties specified directory for logging in home directory
        //TODO: If they ever fix this bug (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6244047) in the Java Logging API we wouldn't need this hack
        File homeLoggingDir = new File (System.getProperty("user.home")+"/webwars-logs/weblings-gameplatform/");
        if (!homeLoggingDir.exists() ) {
            homeLoggingDir.mkdirs();
            logger.info("Creating missing logging directory: " + homeLoggingDir);
        }
    } catch(Exception e) {
        e.printStackTrace();
    }

    try {
        logger.info("[GamePlatform] : Starting...");
    } catch (Exception exc) {
        exc.printStackTrace();

    }
}

Второй подход может поймать IOException и создать каталоги, перечисленные в исключении, проблема с этим подходом заключается в том, что структура ведения журнала уже не смогла создать FileHandler, поэтому ловить и разрешать ошибку по-прежнему оставляет систему ведения журнала плохой состояние.

Ответ 4

Как возможное решение, я думаю, что есть два подхода (посмотрите на некоторые из предыдущих ответов). Я могу расширить класс обработчика журналов Java и написать собственный собственный обработчик. Я мог бы также скопировать функциональность log4j и адаптировать ее к инфраструктуре Java Logging.

Здесь пример копирования базового FileHandler и создания CustomFileHandler см. pastebin для полного класса:

Ключ - это метод openFiles(), где он пытается создать FileOutputStream и проверять и создавать родительский каталог, если он не существует (мне также пришлось копировать защищенные паролем методы LogManager, почему они даже сделали эти пакеты защищенными в любом случае):

// Private method to open the set of output files, based on the
// configured instance variables.
private void openFiles() throws IOException {
    LogManager manager = LogManager.getLogManager();

...

    // Create a lock file. This grants us exclusive access
    // to our set of output files, as long as we are alive.
    int unique = -1;
    for (;;) {
        unique++;
        if (unique > MAX_LOCKS) {
            throw new IOException("Couldn't get lock for " + pattern);
        }
        // Generate a lock file name from the "unique" int.
        lockFileName = generate(pattern, 0, unique).toString() + ".lck";
        // Now try to lock that filename.
        // Because some systems (e.g. Solaris) can only do file locks
        // between processes (and not within a process), we first check
        // if we ourself already have the file locked.
        synchronized (locks) {
            if (locks.get(lockFileName) != null) {
                // We already own this lock, for a different FileHandler
                // object. Try again.
                continue;
            }
            FileChannel fc;
            try {
                File lockFile = new File(lockFileName);
                if (lockFile.getParent() != null) {
                    File lockParentDir = new File(lockFile.getParent());
                    // create the log dir if it does not exist
                    if (!lockParentDir.exists()) {
                        lockParentDir.mkdirs();
                    }
                }

                lockStream = new FileOutputStream(lockFileName);
                fc = lockStream.getChannel();
            } catch (IOException ix) {
                // We got an IOException while trying to open the file.
                // Try the next file.
                continue;
            }
            try {
                FileLock fl = fc.tryLock();
                if (fl == null) {
                    // We failed to get the lock. Try next file.
                    continue;
                }
                // We got the lock OK.
            } catch (IOException ix) {
                // We got an IOException while trying to get the lock.
                // This normally indicates that locking is not supported
                // on the target directory. We have to proceed without
                // getting a lock. Drop through.
            }
            // We got the lock. Remember it.
            locks.put(lockFileName, lockFileName);
            break;
        }
    }

...   }

Ответ 5

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

Я подклассифицировал java.util.logging.FileHandler и реализовал все конструкторы со своими супервызовами. Я помещаю статический блок кода в класс, который создает папки для моего приложения в папке user.home, если они не существуют.

В моем файле свойств logging я заменил java.util.logging.FileHandler своим новым классом.