我有2个FileHandlers可以写出两个单独的文件,并且发生的I / O数量使我的应用程序速度大大降低:

  • 我决定让FileHandlers在单独的线程上运行。
  • 因为它们位于单独的线程中,所以我需要一个“队列”的概念,以便这些单独的线程可以轮询此队列并打印出所有传入的消息。
  • 我已经对消息进行了预格式化,以便所使用的任何参数在它们实际到达FileHandler中的打印输出之前都不会更改。
  • 现在,我意识到我无法使用记录器提供的“log”方法,因为该方法试图在当前线程上调用方法来格式化和打印消息。
  • 因此,我只需调用一个将跟踪消息添加到队列中的方法。
  • 然后,我使用FileHandlers的run()方法通过publish()打印出消息。
  • 我现在意识到publish()只接受一个LogRecord,这只是一个级别+消息。
  • 我的踪迹还有更多,不能简单地放在一条整体消息中,我希望能够使用我设置为FileHandler的“Formatter”。
  • 因此,我在FileHandler中创建了记录器的实例,因此可以使用log方法并按照Formatter中的设计来格式化字符串。

  • 哪个可行,有点。

    ...

    这有点愚蠢,值得继续这样,围绕java.util.Logger而不是与其一起工作吗? java.util.Logger的有用部分之一是每个类都有一个单独的记录器实例,并且能够更好地控制消息...

    有什么建议?

    代码很长,但是我想从上面的描述中很容易理解,如果不告诉我,我会上传到某个地方。

    最佳答案

    如果I / O确实是瓶颈,并且您不需要文件旋转和文件锁定,则创建一个Handler,将LOGRecord +“跟踪消息”中的全格式输出字符串/字节缓冲区排队。然后将完全格式化的输出字符串/字节缓冲区移交/排队到线程中以执行I / O。

    否则,如果您需要使用FileHandler并想将LogRecord +跟踪传递给publish方法,则可以将FileHandler子类化,然后在LogRecord和跟踪之间创建自定义格式器可见的映射。有几种方法可以做到:

  • 创建一个对处理程序和格式化程序都可见的Map。
  • 创建一个LogRecord子类来保存跟踪,并将每个LogRecord转换为新的子类,然后超级发布该LogRecord子类。然后将每个LogRecord强制转换为格式化程序以访问跟踪。

  • 4.现在,我意识到我无法使用记录器提供的“log”方法,因为它试图在当前线程上调用方法来格式化和打印消息。

    默认情况下,Logger.log创建LogRecords并为附加的处理程序和父处理程序调用handler.publish。是handler.publish在当前线程上执行I / O。您需要做的是删除在发布时执行I / O的所有处理程序,并将其替换为仅在发布时将LogRecords排队的处理程序。

    这是如何创建AsyncFileHandler的示例:
        public class AsyncFileHandler extends FileHandler implements Runnable {
    
            private static final int offValue = Level.OFF.intValue();
            private final BlockingQueue<LogRecord> queue = new ArrayBlockingQueue<>(5000);
            private volatile Thread worker;
    
            public AsyncFileHandler() throws IOException {
                super();
            }
    
            public AsyncFileHandler(String pattern, int limit, int count, boolean append)
                    throws IOException {
                super(pattern, limit, count, append);
            }
    
            @Override
            public void publish(LogRecord record) {
                int levelValue = getLevel().intValue();
                if (record.getLevel().intValue() < levelValue || levelValue == offValue) {
                    return;
                }
    
                final Thread t = checkWorker();
                record.getSourceMethodName(); //Infer caller.
                boolean interrupted = Thread.interrupted();
                try {
                    for (;;) {
                        try {
                            boolean offered = queue.offer(record, 10, TimeUnit.MILLISECONDS);
                            if (t == null || !t.isAlive()) {
                                if (!offered || queue.remove(record)) {
                                    handleShutdown(record);
                                }
                                break;
                            } else {
                                if (offered || handleFullQueue(record)) {
                                    break;
                                }
                            }
                        } catch (InterruptedException retry) {
                            interrupted = true;
                        }
                    }
                } finally {
                    if (interrupted) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
    
            private boolean handleFullQueue(LogRecord r) {
                super.publish(r);
                return true; //true if handled.
            }
    
            private void handleShutdown(LogRecord r) {
                super.publish(r);
            }
    
            @Override
            public void close() {
                try {
                    try {
                        final Thread t = this.worker;
                        if (t != null) {
                            t.interrupt();
                            shutdownQueue();
                            t.join();
                            shutdownQueue();
                        }
                    } finally {
                        super.close();
                    }
                } catch (InterruptedException reAssert) {
                    Thread.currentThread().interrupt();
                }
            }
    
            private void shutdownQueue() {
                for (LogRecord r; (r = queue.poll()) != null;) {
                    handleShutdown(r);
                }
            }
    
            @Override
            public void run() {
                try {
                    final BlockingQueue<LogRecord> q = this.queue;
                    for (;;) {
                        super.publish(q.take());
                    }
                } catch (InterruptedException shutdown) {
                    shutdownQueue();
                    Thread.currentThread().interrupt();
                }
            }
    
            private Thread checkWorker() {
                Thread t = worker;
                if (t == null) {
                    t = startWorker();
                }
                return t;
            }
    
            private synchronized Thread startWorker() {
                if (worker == null) {
                    worker = Executors.defaultThreadFactory().newThread(this);
                    worker.setDaemon(true);
                    worker.setContextClassLoader(getClass().getClassLoader());
                    worker.start();
                }
                return worker;
            }
        }
    

    LogRecord文档中有建议,甚至original authors fail to follow中也有MemoryHandler。内容如下:

    因此,如果日志记录处理程序想要将LogRecord传递给另一个线程,或通过RMI传输它,并且如果希望随后获取方法名称或类名称信息,则应调用getSourceClassName或getSourceMethodName中的一个来强制将值设置为填写。

    因此,如果要将LogRecords缓冲在队列中,则必须在将记录添加到队列之前调用getSourceClassName或getSourceMethodName。否则,您的日志将记录错误的源类和源方法名称。

    07-26 09:28