当前位置: 首页 > 面试题库 >

使用带有单独线程的java.util.logger在文件上写入?

胡浩瀚
2023-03-14
问题内容

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

  1. 我决定让FileHandlers在单独的线程上运行。

  2. 由于它们位于单独的线程上,因此我需要一个“队列”概念,以便这些单独的线程可以轮询此队列并打印出所有传入消息。

  3. 我已经对消息进行了预格式化,以便所使用的任何参数在它们实际到达FileHandler中的打印输出之前都不会更改。

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

  5. 因此,我只需要调用一个将跟踪消息添加到队列中的方法即可。

  6. 然后,我使用FileHandlers的run()方法使用publish()打印出消息。

  7. 我现在意识到publish()只接受LogRecord,这只是一个级别+消息。

  8. 我的踪迹还有更多,不能简单地放在一条整体消息中,我希望能够使用我设置为FileHandler的“ Formatter”。

  9. 因此,我在FileHandler中创建了记录器的实例,因此可以使用log方法并按照Formatter中的设计格式化字符串。

哪个可行,有点。

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

有什么建议?

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


问题答案:

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

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

  1. 创建一个对处理程序和格式化程序都可见的映射。
  2. 创建一个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文档中有一些建议,即使是原始作者也无法在MemoryHandler中遵循。内容如下:

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

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



 类似资料:
  • 我目前正在使用Android Room在Android上存储一些小数据。理论上,我使用allowMainThreadQueries()应该没有问题,因为我的所有数据都很小,但为了将来证明我的程序,我尝试将所有调用移动到一个单独的线程中。到目前为止,我在AppConfig中有一个静态处理程序,该类在应用程序初始启动时初始化,在应用程序范围内可见: DbHandler是一个扩展Handler的自定义类

  • 问题内容: 我正在尝试使用多个线程在Java中编写一个大文件。 我已经尝试了Java 和类。 实际上,要写入的内容是使用和写入的整个表(Postgres)。文件中的每一行都是表中的一个元组,我一次要写100行。 编写方法: 单个待写入文件由附加模式下的多个线程打开。此后,每个线程都尝试写入文件文件。 以下是我面临的问题: 有时,文件的内容将被覆盖,即:一行仍然不完整,而下一行从那里开始。我的假设是

  • 问题内容: 我想知道如何将Express中的所有api路由移到与文件分开的单独文件中 我为每个路由使用了很长的api路由列表。因此,每条路线都在其自己的文件中,例如,但是当我列出这些路线时,它会在 所以我想从下面将api端点列表的一部分删除到一个文件中。 这是我目前拥有的: server.js 路线示例 电影.js 问题答案: 您可以尝试使用以下模块化方法。定义具有每个功能逻辑的控制器文件。例如电

  • 问题内容: 我正在编写一个程序,以String数组(来自用户输入)的形式将观察结果集写入文件。我能够将观察值写入.txt文件,然后添加新观察值而不删除先前的数据,但是我所有的数据都在同一行上。 我需要将每组观察结果放在单独的行上。 另外,我将需要稍后能够访问该文件并从中读取文件。 我的代码当前如下所示:(观察到的是字符串数组) 我的输出当前看起来像这样: 我希望它看起来像这样: …等等 我该怎么做

  • 问题内容: 我有以下两个软件包: com.mycorp.project.first com.mycorp.project.second 我想配置Log4J(SLF4J),以将日志从一个包写入一个文件,而从另一个包写入第二个文件。我不希望它们混在一起。 需要明确的是,这是一个正在运行的项目/一个进程。 我已经尝试使用过滤器和记录器,但是log4j似乎忽略了它们。这两个文件始终相同。 编辑:到目前为止