确定FileHandler正在使用的文件

时间:2022-11-18 16:56:00

I am creating a java.util.logging.FileHandler that is allowed to cycle through files. When multiple instances of my application are run, a new log file is created for each instance of the application. I need to know what file is being used by the application because I want to upload the log file to my servers for further review. How can I tell what file is being used by a certain FileHandler?

我正在创建一个允许循环文件的java.util.logging.FileHandler。运行我的应用程序的多个实例时,将为该应用程序的每个实例创建一个新的日志文件。我需要知道应用程序正在使用哪个文件,因为我想将日志文件上传到我的服务器以供进一步查看。如何判断某个FileHandler正在使用哪个文件?

4 个解决方案

#1


The easiest way is to put some kind of identifier in the file name itself, i.e. the pattern argument when you create the FileHandler. Since these are instances of the same application, one way to distinguish them is by their process id, so you could make that part of the pattern. A better approach is to pass in an identifier through the command line and use that to make your filename. That way you control the files being created in some sense. Finally, if your application has some knowledge of why it's different from all the others, for example it connects to a particular database server, then you could just use that database server name as part of the filename.

最简单的方法是在文件名本身中放置某种标识符,即创建FileHandler时的pattern参数。由于这些是同一个应用程序的实例,因此区分它们的一种方法是通过它们的进程ID,因此您可以创建该模式的那一部分。更好的方法是通过命令行传入标识符并使​​用它来创建文件名。这样你可以控制在某种意义上创建的文件。最后,如果您的应用程序知道为什么它与其他所有应用程序不同,例如它连接到特定的数据库服务器,那么您可以将该数据库服务器名称用作文件名的一部分。

EDIT: There does not seem to be any API to get the name of the file being used by a FileHandler. I would suggest looking into the logging extensions in x4juli (which ports a lot of the log4j functionality to the java.util.logging specs):

编辑:似乎没有任何API来获取FileHandler使用的文件的名称。我建议查看x4juli中的日志记录扩展(它将很多log4j功能移植到java.util.logging规范中):

You should be able to substitute an instance of their FileHandler which provides a getFile() method:

您应该能够替换提供getFile()方法的FileHandler实例:

#2


Actually, you could do this much simpler by simply extending FileHandler yourself. For example...

实际上,你可以通过简单地自己扩展FileHandler来做到这一点。例如...

MyFileHandler.java:

import java.io.IOException;
import java.util.logging.FileHandler;
public class MyFileHandler extends FileHandler {
    protected String _MyFileHandler_Patern;
    public MyFileHandler(String pattern) throws IOException {
        _MyFileHandler_Patern = pattern;
    }
    public String getMyFileHandlerPattern() {
        return _MyFileHandler_Patern;
    }
}

DeleteMe.java:

import java.io.IOException;
import java.util.logging.Handler;
import java.util.logging.Logger;
public class DeleteMe {
    public static void main(String[] args) throws IOException {
        Logger log = Logger.getLogger(DeleteMe.class.getName());
        MyFileHandler output = new MyFileHandler("output.log");
        log.addHandler(output);
        for (Handler handler : log.getHandlers()) {
            if (handler instanceof MyFileHandler) {
                MyFileHandler x = (MyFileHandler) handler;
                if ("output.log".equals(x.getMyFileHandlerPattern())) {
                    System.out.println("found hanlder writing to output.log");
                }
            }
        }
    }
}

#3


OK, I do have to say that FileHandler not providing a way to determine the log file is seriously dumb.

好吧,我不得不说FileHandler没有提供确定日志文件的方法是非常愚蠢的。

I wound up writing a function called "chooseFile()" which searches /tmp for the next available log file name and returns that file. You can then pass the name of that file into new FileHandler().

我结束了编写一个名为“chooseFile()”的函数,该函数在/ tmp中搜索下一个可用的日志文件名并返回该文件。然后,您可以将该文件的名称传递给新的FileHandler()。

/**
 * Utility: select a log file.  File is created immediately to reserve
 * its name.
 */
static public File chooseFile(final String basename) throws IOException {
    final int nameLen = basename.length();
    File tmpDir = new File(System.getProperty("java.io.tmpdir"));
    String[] logs = tmpDir.list(new FilenameFilter() {
        public boolean accept(File d, String f) { 
            return f.startsWith(basename);
        }
    });
    int count = 0;
    if (logs.length > 0) {
        for (String name : logs) {
            int n = atoi(name.substring(nameLen));
            if (n >= count) count = n + 1;
        }
    }
    String filename = String.format("%s%d.log", basename, count); 
    File logFile = new File(tmpDir, filename);
    logFile.createNewFile();
    return logFile;
}

#4


Here's my rather hacky way around it. It works for the default if you don't use any format strings, and should work if you use the g and u format strings in filename, but not the others.

这是我相当愚蠢的方式。如果你不使用任何格式字符串,它适用于默认值,如果你在文件名中使用g和u格式字符串,而不是其他格式字符串,它应该工作。

public class FriendlyFileHandler extends FileHandler {

    /***
     * In order to ensure the most recent log file is the file this one owns,
     * we flush before checking the directory for most recent file.
     * 
     * But we must keep other log handlers from flushing in between and making
     * a NEW recent file.
     */
    private static Object[] flushLock = new Object[0];

    private String pattern;

    public FriendlyFileHandler(String pattern, int maxLogLengthInBytes, int count) throws IOException,
            SecurityException {
        super(pattern, maxLogLengthInBytes, count);

        this.pattern = pattern;
    }

    /***
     * Finds the most recent log file matching the pattern.
     * This is just a guess - if you have a complicated pattern 
     * format it may not work.
     * 
     * IMPORTANT: This log file is still in use. You must 
     * removeHandler() on the logger first, .close() this handler, 
     * then add a NEW handler to your logger. THEN, you can read 
     * the file. 
     * 
     * Currently supported format strings: g, u
     * 
     * @return A File of the current log file, or null on error.
     */
    public synchronized File getCurrentLogFile() {

        synchronized(flushLock) {

            // so the file has the most recent date on it.
            flush();


            final String patternRegex =
                    // handle incremental number formats
                    pattern.replaceAll("%[gu]", "\\d*") +
                    // handle default case where %g is appended to end
                    "(\\.\\d*)?$";

            final Pattern re = Pattern.compile(patternRegex);
            final Matcher matcher = re.matcher("");

            // check all files in the directory where this log would be
            final File basedir = new File(pattern).getParentFile();
            final File[] logs = basedir.listFiles(new FileFilter() {

                @Override
                public boolean accept(final File pathname) {
                    // only get files that are part of the pattern
                    matcher.reset(pathname.getAbsolutePath());

                    return matcher.find();
                }

            });

            return findMostRecentLog(logs);
        }
    }

    private File findMostRecentLog(File[] logs) {

        if (logs.length > 0) {
            long mostRecentDate = 0;
            int mostRecentIdx = 0;

            for (int i = 0; i < logs.length; i++) {
                final long d = logs[i].lastModified();

                if (d >= mostRecentDate) {
                    mostRecentDate = d;
                    mostRecentIdx = i;
                }

            }

            return logs[mostRecentIdx];
        }
        else {
            return null;
        }

    }


    @Override
    public synchronized void flush() {

        // only let one Handler flush at a time.
        synchronized(flushLock) {
            super.flush();
        }
    }

}

#1


The easiest way is to put some kind of identifier in the file name itself, i.e. the pattern argument when you create the FileHandler. Since these are instances of the same application, one way to distinguish them is by their process id, so you could make that part of the pattern. A better approach is to pass in an identifier through the command line and use that to make your filename. That way you control the files being created in some sense. Finally, if your application has some knowledge of why it's different from all the others, for example it connects to a particular database server, then you could just use that database server name as part of the filename.

最简单的方法是在文件名本身中放置某种标识符,即创建FileHandler时的pattern参数。由于这些是同一个应用程序的实例,因此区分它们的一种方法是通过它们的进程ID,因此您可以创建该模式的那一部分。更好的方法是通过命令行传入标识符并使​​用它来创建文件名。这样你可以控制在某种意义上创建的文件。最后,如果您的应用程序知道为什么它与其他所有应用程序不同,例如它连接到特定的数据库服务器,那么您可以将该数据库服务器名称用作文件名的一部分。

EDIT: There does not seem to be any API to get the name of the file being used by a FileHandler. I would suggest looking into the logging extensions in x4juli (which ports a lot of the log4j functionality to the java.util.logging specs):

编辑:似乎没有任何API来获取FileHandler使用的文件的名称。我建议查看x4juli中的日志记录扩展(它将很多log4j功能移植到java.util.logging规范中):

You should be able to substitute an instance of their FileHandler which provides a getFile() method:

您应该能够替换提供getFile()方法的FileHandler实例:

#2


Actually, you could do this much simpler by simply extending FileHandler yourself. For example...

实际上,你可以通过简单地自己扩展FileHandler来做到这一点。例如...

MyFileHandler.java:

import java.io.IOException;
import java.util.logging.FileHandler;
public class MyFileHandler extends FileHandler {
    protected String _MyFileHandler_Patern;
    public MyFileHandler(String pattern) throws IOException {
        _MyFileHandler_Patern = pattern;
    }
    public String getMyFileHandlerPattern() {
        return _MyFileHandler_Patern;
    }
}

DeleteMe.java:

import java.io.IOException;
import java.util.logging.Handler;
import java.util.logging.Logger;
public class DeleteMe {
    public static void main(String[] args) throws IOException {
        Logger log = Logger.getLogger(DeleteMe.class.getName());
        MyFileHandler output = new MyFileHandler("output.log");
        log.addHandler(output);
        for (Handler handler : log.getHandlers()) {
            if (handler instanceof MyFileHandler) {
                MyFileHandler x = (MyFileHandler) handler;
                if ("output.log".equals(x.getMyFileHandlerPattern())) {
                    System.out.println("found hanlder writing to output.log");
                }
            }
        }
    }
}

#3


OK, I do have to say that FileHandler not providing a way to determine the log file is seriously dumb.

好吧,我不得不说FileHandler没有提供确定日志文件的方法是非常愚蠢的。

I wound up writing a function called "chooseFile()" which searches /tmp for the next available log file name and returns that file. You can then pass the name of that file into new FileHandler().

我结束了编写一个名为“chooseFile()”的函数,该函数在/ tmp中搜索下一个可用的日志文件名并返回该文件。然后,您可以将该文件的名称传递给新的FileHandler()。

/**
 * Utility: select a log file.  File is created immediately to reserve
 * its name.
 */
static public File chooseFile(final String basename) throws IOException {
    final int nameLen = basename.length();
    File tmpDir = new File(System.getProperty("java.io.tmpdir"));
    String[] logs = tmpDir.list(new FilenameFilter() {
        public boolean accept(File d, String f) { 
            return f.startsWith(basename);
        }
    });
    int count = 0;
    if (logs.length > 0) {
        for (String name : logs) {
            int n = atoi(name.substring(nameLen));
            if (n >= count) count = n + 1;
        }
    }
    String filename = String.format("%s%d.log", basename, count); 
    File logFile = new File(tmpDir, filename);
    logFile.createNewFile();
    return logFile;
}

#4


Here's my rather hacky way around it. It works for the default if you don't use any format strings, and should work if you use the g and u format strings in filename, but not the others.

这是我相当愚蠢的方式。如果你不使用任何格式字符串,它适用于默认值,如果你在文件名中使用g和u格式字符串,而不是其他格式字符串,它应该工作。

public class FriendlyFileHandler extends FileHandler {

    /***
     * In order to ensure the most recent log file is the file this one owns,
     * we flush before checking the directory for most recent file.
     * 
     * But we must keep other log handlers from flushing in between and making
     * a NEW recent file.
     */
    private static Object[] flushLock = new Object[0];

    private String pattern;

    public FriendlyFileHandler(String pattern, int maxLogLengthInBytes, int count) throws IOException,
            SecurityException {
        super(pattern, maxLogLengthInBytes, count);

        this.pattern = pattern;
    }

    /***
     * Finds the most recent log file matching the pattern.
     * This is just a guess - if you have a complicated pattern 
     * format it may not work.
     * 
     * IMPORTANT: This log file is still in use. You must 
     * removeHandler() on the logger first, .close() this handler, 
     * then add a NEW handler to your logger. THEN, you can read 
     * the file. 
     * 
     * Currently supported format strings: g, u
     * 
     * @return A File of the current log file, or null on error.
     */
    public synchronized File getCurrentLogFile() {

        synchronized(flushLock) {

            // so the file has the most recent date on it.
            flush();


            final String patternRegex =
                    // handle incremental number formats
                    pattern.replaceAll("%[gu]", "\\d*") +
                    // handle default case where %g is appended to end
                    "(\\.\\d*)?$";

            final Pattern re = Pattern.compile(patternRegex);
            final Matcher matcher = re.matcher("");

            // check all files in the directory where this log would be
            final File basedir = new File(pattern).getParentFile();
            final File[] logs = basedir.listFiles(new FileFilter() {

                @Override
                public boolean accept(final File pathname) {
                    // only get files that are part of the pattern
                    matcher.reset(pathname.getAbsolutePath());

                    return matcher.find();
                }

            });

            return findMostRecentLog(logs);
        }
    }

    private File findMostRecentLog(File[] logs) {

        if (logs.length > 0) {
            long mostRecentDate = 0;
            int mostRecentIdx = 0;

            for (int i = 0; i < logs.length; i++) {
                final long d = logs[i].lastModified();

                if (d >= mostRecentDate) {
                    mostRecentDate = d;
                    mostRecentIdx = i;
                }

            }

            return logs[mostRecentIdx];
        }
        else {
            return null;
        }

    }


    @Override
    public synchronized void flush() {

        // only let one Handler flush at a time.
        synchronized(flushLock) {
            super.flush();
        }
    }

}