每一个Java程序员都熟悉将System.out.println
调用添加到麻烦代码中以了解程序行为的过程。当然,一旦找出了问题的原因,就可以删除打印语句,只在下一个问题出现时将它们放回原处。日志API旨在解决这个问题。API的主要优点如下:
com.mycompany.myapp
,与包名称类似。注意
许多应用程序使用其他日志记录框架,例如Log4J 2(https://logging.apache.org/log4j/2.x)和Logback(https://logback.qos.ch),它们提供了比标准Java日志记录框架更高的性能。这些框架有稍微不同的API。记录诸如slf4j(https://www.slf4j.org)和commons logging(https://commons.apache.org/proper/commonslogging)提供了一个统一的API,这样您就可以在不重写应用程序的情况下替换日志框架。为了使问题更加混乱,Log4J 2也可以使用SLF4J的组件。在这本书中,我们覆盖了标准的Java日志记录框架。对于许多用途来说,它已经足够好了,学习它的API将为您了解替代方案做好准备。
注意
至于Java 9,Java平台有一个独立的轻量级日志记录系统,它不依赖于
java.logging
模块(包含标准Java日志记录框架)。该系统仅用于Java API中。如果存在java.logging
模块,日志消息将自动转发给它。第三方日志框架可以提供适配器来接收平台日志消息。我们不讨论平台日志,因为它不打算被应用程序程序员使用。
对于简单的日志记录,使用全局日志记录程序并调用其info
方法:
Logger.getGlobal().info("File->Open menu item selected");
默认情况下,记录的打印方式如下:
May 10, 2013 10:12:15 PM LoggingImageViewer fileOpen
INFO: File->Open menu item selected
但是如果你调用
Logger.getGlobal().setLevel(Level.OFF);
在适当的位置(如main
的开头),所有日志记录都将被抑制。
既然你已经看到了“傻瓜式日志”,那么我们继续进行工业强度的日志。在专业应用程序中,您不希望将所有记录都记录到单个全局记录器中。相反,您可以定义自己的记录器。
调用getLogger
方法以创建或检索日志:
private static final Logger myLogger = Logger.getLogger("com.mycompany.myapp");
提示
不被任何变量引用的日志可以被回收。要防止这种情况发生,请使用静态变量保存对日志的引用,如上面的示例所示。
与包名称类似,日志名称是分层的。实际上,它们比包更具有层次性。包与其父级之间没有语义关系,但日志父级和子级共享某些属性。例如,如果在日志“com.mycompany
”上设置日志级别,则子记录器继承该级别。
有下面七个日志级别:
SEVERE
WARNING
INFO
CONFIG
FINE
FINER
FINEST
默认情况下,实际记录前三个级别。您可以设置不同的级别,例如,
logger.setLevel(Level.FINE);
现在FINE
和上面的所有级别都会被记录下来。
您还可以使用Level.ALL
打开所有级别的日志记录,或使用Level.OFF
关闭所有日志记录。
所有级别都有日志记录方法,例如
logger.warning(message);
logger.fine(message);
等等。或者,可以使用log
方法并提供级别,例如
logger.log(Level.FINE, message);
提示
默认的日志记录配置记录
INFO
级别或更高级别的所有记录。因此,您应该使用CONFIG
,FINE
,FINER
和FINEST
级别来调试对诊断有用但对用户毫无意义的消息。
小心
如果将日志级别设置为比
INFO
更精细的值,则还需要更改日志处理程序配置。默认的日志处理程序禁止显示INFO
以下信息。有关详细信息,请参阅下一节。
默认日志记录显示包含日志记录调用的类和方法的名称,如从调用堆栈推断的那样。但是,如果虚拟机优化了执行,则可能无法获得准确的调用信息。您可以使用logp
方法给出调用类和方法的精确位置。方法签名是
void logp(Level l, String className, String methodName, String message)
跟踪执行流有一些方便的方法:
void entering(String className, String methodName)
void entering(String className, String methodName, Object param)
void entering(String className, String methodName, Object[] params)
void exiting(String className, String methodName)
void exiting(String className, String methodName, Object result)
例如
int read(String file, String pattern)
{
logger.entering("com.mycompany.mylib.Reader", "read",
new Object[] { file, pattern });
. . .
logger.exiting("com.mycompany.mylib.Reader", "read", count);
return count;
}
这些调用生成以字符串条目ENTRY
和RETURN
开始的FINER
级别的日志记录。
注意
在将来的某个时候,将重写带有
Object[]
参数的日志记录方法,以支持变量参数列表(“varargs”)。然后,您就可以调用logger.entering("com.mycompany.mylib.reader", "read", file, pattern)
。
日志记录的一个常见用途是记录意外的异常。两种方便的方法包括在日志记录中描述异常。
void throwing(String className, String methodName, Throwable t)
void log(Level l, String message, Throwable t)
典型的用法是
if (. . .)
{
var e = new IOException(". . .");
logger.throwing("com.mycompany.mylib.Reader", "read", e);
throw e;
}
以及
try
{
. . .
}
catch (IOException e)
{
Logger.getLogger("com.mycompany.myapp").log(Level.WARNING, "Rea");
}
throwing
调用记录一个级别FINER
的记录和一条以THROW
开头的消息。
您可以通过编辑配置文件来更改日志记录系统的各种属性。默认配置文件位于
conf/logging.properties
(或者在Java 9以前是jar/lib/logging.properties
)。
要使用另一个文件,请通过启动应用程序,将java.util.logging.config.file
属性设置为文件位置。
java -Djava.util.logging.config.file=configFile MainClass
要更改默认日志记录级别,请编辑配置文件并修改行
.level=INFO
您可以通过添加诸如
com.mycompany.myapp.level=FINE
也就是说,将.level
后缀附加到记录器名称中。
正如您将在本节后面看到的,记录器实际上不会将消息发送到作为处理程序工作的控制台。处理程序也有级别。要在控制台上看到FINE
消息,还需要设置
java.util.logging.ConsoleHandler.level=FINE
小心
日志管理器配置中的设置不是系统属性。使用
-Dcom.mycompany.myapp.level=FINE
启动程序对记录器没有任何影响。
The log manager is initialized during VM startup, before main executes. If
you want to customize the logging properties but didn’t start your application
with the -Djava.util.logging.config.file command-line option,
call System.setProperty(
“java.util.logging.config.file”, file) in your program.
But then you must also call
LogManager.getLogManager().readConfiguration() to
reinitialize the log manager.
日志管理器在VM启动期间(在main
执行之前)初始化。如果您想自定义日志记录属性,但没有使用-Djava.util.logging.config.file
命令行选项启动应用程序,请在程序中调用System.setProperty("java.util.logging.config.file", file)
。但是,还必须调用LogManager.getLogManager().readConfiguration()
以重新初始化日志管理器。
至于Java 9,您可以通过调用以下代码来更新日志记录配置。
LogManager.getLogManager().updateConfiguration(mapper);
从java.util.logging.config.file
系统属性指定的位置读取新配置。然后应用映射器解析旧配置或新配置中所有键的值。映射器是一个Function
。它将现有配置中的键映射到替换函数。每个替换函数接收与键关联的新旧值(如果没有关联的值,则为null
),并生成替换,如果在更新中应删除键,则为null
。
这听起来相当复杂,所以让我们来举几个例子。一个有用的映射方案是合并旧的和新的配置,当新的和旧的配置中都存在一个键时,优先选择新的值。然后mapper
是
key -> ((oldValue, newValue) -> newValue == null ? oldValue : newValue)
或者您可能只想更新以com.mycompany
开头的密钥,而不更改其他密钥:
key -> key.startsWith("com.mycompany")
? ((oldValue, newValue) -> newValue)
: ((oldValue, newValue) -> oldValue)
也可以使用jconsole
程序更改正在运行的程序中的日志记录级别。有关信息,请参阅www.oracle.com/technetwork/articles/java/jconsole- 1564139.html#LoggingControl
。
注意
日志属性文件由
java.util.logging.LogManager
类处理。可以通过将java.util.logging.manager
系统属性设置为子类的名称来指定不同的日志管理器。或者,您可以保留标准日志管理器,并且仍然可以从日志属性文件中绕过初始化。将java.util.logging.config.class
系统属性设置为以其他方式设置日志管理器属性的类的名称。有关详细信息,请参阅LogManager
类的API文档。
您可能希望将日志消息本地化,以便国际用户能够阅读它们。应用程序国际化是第二卷第7章的主题。简而言之,在本地化日志消息时需要记住以下几点。
本地化应用程序在resource bundles
中包含特定于区域设置的信息。资源包由一组不同地区(如美国或德国)的映射组成。例如,资源包可以将字符串“readingFile
”映射为英文字符串“Reading file
”或者德文“Achtung! Datei wird eingelesen
”。
一个程序可以包含多个资源包,例如,一个用于菜单,另一个用于日志消息。每个资源包都有一个名称(例如“com.mycompany.logmessages
”)。要添加到资源束的映射,请为每个区域设置提供一个文件。英语消息映射位于文件com/mycompany/logmessages_en.properties
中,德语消息映射位于文件com/mycompany/logmessages_de.properties
中。(en
和de
是语言代码。)您将文件与应用程序的类文件放在一起,这样ResourceBundle
类将自动定位它们。这些文件是纯文本文件,由诸如
readingFile=Achtung! Datei wird eingelesen
renamingFile=Datei wird umbenannt
. . .
请求记录器时,可以指定资源束:
Logger logger = Logger.getLogger(loggerName, "com.mycompany.logmessages");
然后为日志消息指定资源绑定键,而不是实际的消息字符串:
logger.info("readingFile");
通常需要在本地化消息中包含参数。消息可能包含占位符:{0}
、{1}
,依此类推。例如,要将文件名包含在日志消息中,请使用如下占位符:
Reading file {0}.
Achtung! Datei {0} wird eingelesen.
然后,要将值传递给占位符,请调用以下方法之一:
logger.log(Level.INFO, "readingFile", fileName);
logger.log(Level.INFO, "renamingFile", new Object[] { oldName, newName });
或者,如Java 9所示,可以在logrb
方法中指定资源束对象(而不是名称):
logger.logrb(Level.INFO, bundle, "renamingFile", oldName, newName);
注意
这是唯一使用变量参数作为消息参数的日志记录方法。
默认情况下,记录器将记录发送到ConsoleHandler
,该处理程序将记录打印到System.err
流。具体来说,记录器将记录发送到父处理程序,最终祖先(名为“”)具有一个ConsoleHandler
。
和记录器一样,处理程序也有一个日志级别。对于要记录的记录,其记录级别必须高于记录器和处理程序的阈值。日志管理器配置文件将默认控制台处理程序的日志级别设置为
java.util.logging.ConsoleHandler.level=INFO
要用FINE
级别记录,请更改配置中的默认记录器级别和处理程序级别。或者,您可以完全绕过配置文件并安装自己的处理程序。
Logger logger = Logger.getLogger("com.mycompany.myapp");
logger.setLevel(Level.FINE);
logger.setUseParentHandlers(false);
var handler = new ConsoleHandler();
handler.setLevel(Level.FINE);
logger.addHandler(handler);
默认情况下,记录器将记录发送到自己的处理程序和父处理程序。我们的日志程序是原始日志程序(名为“”)的一个子程序,它将所有级别INFO
及以上的记录发送到控制台。但是,我们不希望看到这些记录两次,因此我们将useParentHandlers
属性设置为false
。
若要将日志记录发送到其他位置,请添加其他处理程序。日志API为此提供了两个有用的处理程序:FileHandler
和SocketHandler
。SocketHandler
将记录发送到指定的主机和端口。更感兴趣的是在文件中收集记录的FileHandler
。
您只需将记录发送到默认的文件处理程序,如下所示:
var handler = new FileHandler();
logger.addHandler(handler);
记录被发送到用户主目录中的javan.log
文件,其中n是使文件唯一的数字。如果系统没有用户主目录的概念(例如,在Windows 95/98/Me中),则文件存储在默认位置,如C:\Windows
。默认情况下,记录的格式为XML。典型的日志记录具有以下形式
2002-02-04T07:45:15
1012837515710
1
com.mycompany.myapp
INFO
com.mycompany.mylib.Reader
read
10
Reading file corejava.gif
您可以通过在日志管理器配置中设置各种参数(参见表7.1)或使用其他构造函数(参见本节末尾的API注释)来修改文件处理程序的默认行为。
表7.1 文件处理程序配置参数
配置属性 | 描述 | 默认值 |
---|---|---|
java.util.logging.FileHandler.level | 处理程序级别 | Level.ALL |
java.util.logging.FileHandler.append | 控制处理程序是应附加到现有文件,还是应为每个运行的程序打开一个新文件 | false |
| java.util.logging.FileHandler.limit | 打开另一个文件之前要写入文件的最大字节数(0=无限制) | 0 (no limit) in the 50000
configuration |
| java.util.logging.FileHandler.pattern | 日志文件名的模式。模式变量见表7.2。 | %h/java%u.log |
| java.util.logging.FileHandler.count | 旋转序列中的日志数 | 1 (不旋转) |
| java.util.logging.FileHandler.filter | 要使用的筛选器类 | 无过滤 |
| java.util.logging.FileHandler.encoding | 要使用的字符编码 | 平台编码 |
| java.util.logging.FileHandler.formatter | 记录格式化程序 | java |
您可能不想使用默认的日志文件名。因此,您应该使用另一个模式,例如%h/myapp.log
。(有关模式变量的说明,请参见表7.2。)
表7.2 日志文件模式变量
变量 | 描述 |
---|---|
%h | user.home 系统属性的值 |
%t | 系统临时目录 |
%u | 用于解决冲突的唯一数字 |
%g | 旋转日志的生成号(如果指定了旋转,并且模式不包含%g,则使用一个%g后缀) |
%% | %字符 |
如果多个应用程序(或同一应用程序的多个副本)使用同一个日志文件,则应打开append
标志。或者,在文件名模式中使用%u
,以便每个应用程序创建日志的唯一副本。
打开文件旋转也是一个好主意。日志文件以旋转顺序保存,如myapp.log.0
、myapp.log.1
、myapp.log.2
等。每当文件超过大小限制时,将删除最旧的日志,重命名其他文件,并创建一个生成号为0
的新文件。
提示
许多程序员使用日志作为技术支持人员的帮助。如果程序在字段中出现错误行为,用户可以将日志文件发回进行检查。在这种情况下,您应该打开附加标志,使用旋转日志,或者两者都使用。
您还可以通过扩展Handler
或StreamHandler
类来定义自己的处理程序。我们在本节末尾的示例程序中定义这样的处理程序。这个处理程序在一个窗口中显示记录(参见图7.2)。
图7.2 在窗口中显示记录的日志处理程序
处理程序扩展StreamHandler
类并安装一个流,其write
方法在文本区域中显示流输出。
class WindowHandler extends StreamHandler
{
public WindowHandler()
{
. . .
var output = new JTextArea();
setOutputStream(new OutputStream()
{
public void write(int b) {} // not called
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
}
. . .
}
这种方法只有一个问题:处理程序缓冲记录,并且只在缓冲区满时将它们写入流。因此,我们重写publish
方法以在每条记录之后刷新缓冲区:
class WindowHandler extends StreamHandler
{
. . .
public void publish(LogRecord record)
{
super.publish(record);
flush();
}
}
如果要编写更多的异域流处理程序,请扩展该Handler
类并定义publish
、flush
和close
方法。
默认情况下,根据记录级别筛选记录。每个记录器和处理程序都可以有一个可选的过滤器来执行额外的过滤。要定义过滤器,请实现Filter
接口并定义方法
boolean isLoggable(LogRecord record)
使用所需的任何条件分析日志记录,并为应包含在日志中的记录返回true
。例如,特定的过滤器可能只对entering
和exiting
方法生成的消息感兴趣。然后,过滤器应该调用record.getMessage()
并检查它是以ENTRY
开头还是以RETURN
开头。
要将过滤器安装到记录器或处理程序中,只需调用setFilter
方法。请注意,一次最多只能有一个过滤器。
ConsoleHandler
和FileHandler
类以文本和XML格式发出日志记录。但是,您也可以定义自己的格式。您需要扩展Formatter
类并重写该方法
String format(LogRecord record)
该方法格式化记录的消息部分,替换参数并应用本地化。
许多文件格式(如XML)需要一个环绕格式化记录的头和尾部分。要实现这一点,请重写方法
String getHead(Handler h)
String getTail(Handler h)
最后,调用setFormatter
方法将格式化程序安装到处理程序中。
由于有如此多的日志记录选项,很容易失去对基本原理的跟踪。以下配方总结了最常见的操作。
对于一个简单的应用程序,选择一个记录器。最好将日志程序命名为与主应用程序包相同的名称,例如·com.mycompany.myprog·。您可以通过调用
Logger logger = Logger.getLogger("com.mycompany.myprog");
为了方便起见,您可能需要添加静态字段
private static final Logger logger =
Logger.getLogger("com.mycompany.myprog");
到具有大量日志记录活动的类。
默认日志配置将级别INFO
或更高级别的所有消息记录到控制台。用户可以覆盖默认配置,但是正如您所看到的,这个过程有点复杂。因此,最好在应用程序中安装更合理的默认值。
以下代码确保所有消息都记录到特定于应用程序的文件中。将代码放入应用程序的main
方法中。
if (System.getProperty("java.util.logging.config.class") == null
&& System.getProperty("java.util.logging.config.file") == n
{
try
{
Logger.getLogger("").setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = 10;
var handler = new FileHandler("%h/myapp.log", 0, LOG_ROTATION_COUNT);
Logger.getLogger("").addHandler(handler);
}
catch (IOException e)
{
logger.log(Level.SEVERE, "Can't create log file handler",
}
}
现在,你已经准备好记录你核心的内容了。请记住,所有具有级别INFO
、WARNING
和SEVERE
的消息都会显示在控制台上。因此,请为对程序用户有意义的消息保留这些级别。对于记录面向程序员的消息,FINE
级别是一个很好的选择。
每当您试图调用System.out.println
时,请发出一条日志消息:
logger.fine("File open dialog canceled");
记录意外的异常也是一个好主意。例如:
try
{
. . .
}
catch (SomeException e)
{
logger.log(Level.FINE, "explanation", e);
}
清单7.2将这个方法与一个附加的扭曲一起使用:日志消息也显示在一个日志窗口中。
清单7.2 logging/LoggingImageViewer.java
package logging;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.logging.*;
import javax.swing.*;
/**
* A modification of the image viewer program that logs various events.
* @version 1.03 2015-08-20
* @author Cay Horstmann
*/
public class LoggingImageViewer
{
public static void main(String[] args)
{
if (System.getProperty("java.util.logging.config.class") == null
&& System.getProperty("java.util.logging.config.file") == null)
{
try
{
Logger.getLogger("com.horstmann.corejava").setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = 10;
var handler = new FileHandler("%h/LoggingImageViewer.log", 0, LOG_ROTATION_COUNT);
Logger.getLogger("com.horstmann.corejava").addHandler(handler);
}
catch (IOException e)
{
Logger.getLogger("com.horstmann.corejava").log(Level.SEVERE,
"Can't create log file handler", e);
}
}
EventQueue.invokeLater(() ->
{
var windowHandler = new WindowHandler();
windowHandler.setLevel(Level.ALL);
Logger.getLogger("com.horstmann.corejava").addHandler(windowHandler);
var frame = new ImageViewerFrame();
frame.setTitle("LoggingImageViewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Logger.getLogger("com.horstmann.corejava").fine("Showing frame");
frame.setVisible(true);
});
}
}
/**
* The frame that shows the image.
*/
class ImageViewerFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 400;
private JLabel label;
private static Logger logger = Logger.getLogger("com.horstmann.corejava");
public ImageViewerFrame()
{
logger.entering("ImageViewerFrame", "");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// set up menu bar
var menuBar = new JMenuBar();
setJMenuBar(menuBar);
var menu = new JMenu("File");
menuBar.add(menu);
var openItem = new JMenuItem("Open");
menu.add(openItem);
openItem.addActionListener(new FileOpenListener());
var exitItem = new JMenuItem("Exit");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
logger.fine("Exiting.");
System.exit(0);
}
});
// use a label to display the images
label = new JLabel();
add(label);
logger.exiting("ImageViewerFrame", "");
}
private class FileOpenListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
logger.entering("ImageViewerFrame.FileOpenListener", "actionPerformed", event);
// set up file chooser
var chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// accept all files ending with .gif
chooser.setFileFilter(new javax.swing.filechooser.FileFilter()
{
public boolean accept(File f)
{
return f.getName().toLowerCase().endsWith(".gif") || f.isDirectory();
}
public String getDescription()
{
return "GIF Images";
}
});
// show file chooser dialog
int r = chooser.showOpenDialog(ImageViewerFrame.this);
// if image file accepted, set it as icon of the label
if (r == JFileChooser.APPROVE_OPTION)
{
String name = chooser.getSelectedFile().getPath();
logger.log(Level.FINE, "Reading file {0}", name);
label.setIcon(new ImageIcon(name));
}
else logger.fine("File open dialog canceled.");
logger.exiting("ImageViewerFrame.FileOpenListener", "actionPerformed");
}
}
}
/**
* A handler for displaying log records in a window.
*/
class WindowHandler extends StreamHandler
{
private JFrame frame;
public WindowHandler()
{
frame = new JFrame();
var output = new JTextArea();
output.setEditable(false);
frame.setSize(200, 200);
frame.add(new JScrollPane(output));
frame.setFocusableWindowState(false);
frame.setVisible(true);
setOutputStream(new OutputStream()
{
public void write(int b)
{
} // not called
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
}
public void publish(LogRecord record)
{
if (!frame.isVisible()) return;
super.publish(record);
flush();
}
}
java.util.logging.Logger 1.4
Logger getLogger(String loggerName)
Logger getLogger(String loggerName, String bundleName)
bundleName
的资源包中。void severe(String message)
void warning(String message)
void info(String message)
void config(String message)
void fine(String message)
void finer(String message)
void finest(String message)
void entering(String className, String methodName)
void entering(String className, String methodName, Object param)
void entering(String className, String methodName, Object[] param)
void exiting(String className, String methodName)
void exiting(String className, String methodName, Object result)
void throwing(String className, String methodName, Throwable t)
void log(Level level, String message)
void log(Level level, String message, Object obj)
void log(Level level, String message, Object[] objs)
void log(Level level, String message, Throwable t)
{0}
、{1}
等)。void logp(Level level, String className, String methodName, String message)
void logp(Level level, String className, String methodName, String message, Object obj)
void logp(Level level, String className, String methodName, String message, Object[] objs)
void logp(Level level, String className, String methodName, String message, Throwable t)
void logrb(Level level, String className, String methodName, ResourceBundle bundle, String message, Object... params)
9void logrb(Level level, String className, String methodName, ResourceBundle bundle, String message, Throwable thrown)
9Level getLevel()
void setLevel(Level l)
Logger getParent()
void setParent(Logger l)
Handler[] getHandlers()
void addHandler(Handler h)
void removeHandler(Handler h)
boolean getUseParentHandlers()
void setUseParentHandlers(boolean b)
true
,则记录器将所有记录转发给其父级的处理程序。Filter getFilter()
void setFilter(Filter f)
java.util.logging.Handler
1.4
abstract void publish(LogRecord record)
abstract void flush()
abstract void close()
Filter getFilter()
void setFilter(Filter f)
Formatter getFormatter()
void setFormatter(Formatter f)
Level getLevel()
void setLevel(Level l)
java.util.logging.ConsoleHandler
1.4
ConsoleHandler()
java.util.logging.FileHandler
1.4
FileHandler(String pattern)
FileHandler(String pattern, boolean append)
FileHandler(String pattern, int limit, int count)
FileHandler(String pattern, int limit, int count, boolean append)
FileHandler(String pattern, long limit, int count, boolean append)
9limit
是打开新日志文件之前的最大字节数。count
是旋转序列中的文件数。如果append
为true
,则应将记录追加到现有日志文件。java.util.logging.LogRecord
1.4
Level getLevel()
String getLoggerName()
ResourceBundle getResourceBundle()
String getResourceBundleName()
null
。String getMessage()
Object[] getParameters()
null
。Throwable getThrown()
null
。String getSourceClassName()
String getSourceMethodName()
long getMillis()
Instant getInstant()
9java.time.Instant
形式的创建时间(见第二卷第6章)。long getSequenceNumber()
int getThreadID()
LogRecord
类分配,与其他线程ID没有关系。java.util.logging.LogManager
1.4
static LogManager getLogManager()
LogManager
实例。void readConfiguration()
void readConfiguration(InputStream in)
java.util.logging.config.file
或给定输入流指定的文件读取日志配置。void updateConfiguration(InputStream in, Function> mapper)
9void updateConfiguration(Function 9
将日志配置与系统属性java.util.logging.config.file
或给定输入流指定的文件合并。有关mapper
参数的描述,请参见第407页的第7.5.3节“更改日志管理器配置”。
java.util.logging.filter
1.4
boolean isLoggable(LogRecord record)
true
。java.util.logging.Formatter
1.4
abstract String format(LogRecord record)
String getHead(Handler h)
String getTail(Handler h)
String formatMessage(LogRecord record)