如果I /
O确实是瓶颈,并且您不需要文件旋转和文件锁定,则创建一个Handler,将来自LogRecord
+“跟踪消息”的全格式输出字符串/字节缓冲区排队。然后将完全格式化的输出字符串/字节缓冲区移交/排队到线程中以执行I / O。
否则,如果您需要使用FileHandler并将LogRecord
+跟踪传递给publish方法,则可以将FileHandler子类化,然后在LogRecord和跟踪之间创建自定义格式器可见的映射。有几种方法可以做到:
- 创建一个对处理程序和格式化程序都可见的映射。
- 创建一个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。否则,您的日志将记录错误的源类和源方法名称。



