日志记录器挺简单的,没有很多东西,最主要的就是一个Logger接口:
[java]
- public interface Logger {
- public static final int FATAL = Integer.MIN_VALUE;
- public static final int ERROR = 1;
- public static final int WARNING = 2;
- public static final int INFORMATION = 3;
- public static final int DEBUG = 4;
- public Container getContainer();
- public void setContainer(Container container);
- public String getInfo();
- public int getVerbosity();
- public void setVerbosity(int verbosity);
- public void addPropertyChangeListener(PropertyChangeListener listener);
- public void log(String message);
- public void log(Exception exception, String msg);
- public void log(String message, Throwable throwable);
- public void log(String message, int verbosity);
- public void log(String message, Throwable throwable, int verbosity);
- public void removePropertyChangeListener(PropertyChangeListener listener);
- }
[java]
- public void start() throws LifecycleException {
- // Validate and update our current component state
- if (started)
- throw new LifecycleException
- (sm.getString("fileLogger.alreadyStarted"));
- lifecycle.fireLifecycleEvent(START_EVENT, null);//触发生命周期事件
- started = true;
- }
[java]
- private void open() {
- // Create the directory if necessary
- File dir = new File(directory);//directory等于logs,即在文件夹logs下新建日志文件
- if (!dir.isAbsolute())
- dir = new File(System.getProperty("catalina.base"), directory);
- dir.mkdirs();
- // Open the current log file
- try {
- String pathname = dir.getAbsolutePath() + File.separator +
- prefix + date + suffix;
- writer = new PrintWriter(new FileWriter(pathname, true), true);
- } catch (IOException e) {
- writer = null;
- }
- }
[java]
- public void log(String msg) {
- // Construct the timestamp we will use, if requested
- Timestamp ts = new Timestamp(System.currentTimeMillis());
- String tsString = ts.toString().substring(0, 19);
- String tsDate = tsString.substring(0, 10);
- // If the date has changed, switch log files
- if (!date.equals(tsDate)) { //如果日期改变,则新建一个日志文件。
- synchronized (this) {
- if (!date.equals(tsDate)) {
- close();
- date = tsDate;
- open();
- }
- }
- }
- // Log this message, timestamped if necessary
- if (writer != null) {
- if (timestamp) {
- writer.println(tsString + " " + msg);//写入时间和日志信息进日志文件中。
- } else {
- writer.println(msg);
- }
- }
- }
writer.println把时间和日志信息写进日志文件中。当然,这个日志记录器一般是给Tomcat自己用的,我们也可以实现Logger接口,然后重写它的open方法(打开我们自己的日志文件)和log方法(用来在我们自己的日志文件中记录日志信息)。
现在再来看刚才提到的代码sm.getString("fileLogger.alreadyStarted")。在理解这句代码前先看一个jdk的类ResourceBundle,这个类提供了国际化的方便。这个类的作用就是读取.properties文件,但是会根据文件名来获取当前系统的语言信息,然后读取对应文件的属性值。当然首先要有各国不同的属性文件,才能国际化,Tomcat的每个包下都有几个不同的属性文件,org.apache.catalina.logger包下有如下三个属性文件:
[java]
- LocalStrings_es.properties
- LocalStrings_ja.properties
- LocalStrings.properties
[java]
- fileLogger.alreadyStarted=File Logger has already been started
- fileLogger.notStarted=File Logger has not yet been started
- tomcatLogger.alreadyStarted=Tomcat Logger has already been started
- tomcatLogger.notStarted=Tomcat Logger has not yet been started
ResourceBundle类通过getBundle方法获取,参数是属性文件的包名家名字前缀,上面就是包名加LocalStrings。通过getString(String key)方法获取属性文件中的参数:
现在来看代码sm.getString("fileLogger.alreadyStarted"),sm是StringManager的实例,在FileLogger中已经初始化:
[java]
- private StringManager sm =StringManager.getManager(Constants.Package);
Constants.Package得到包的名字,getManager方法代码如下:
[java]
- public synchronized static StringManager getManager(String packageName) {
- StringManager mgr = (StringManager)managers.get(packageName);
- if (mgr == null) {
- mgr = new StringManager(packageName);//新建一个StringManager
- managers.put(packageName, mgr);
- }
- return mgr;
- }
代码很好理解,如果已经有StringManager实例了就直接从managers(这是一个Hashtable)中拿,没有就新建一个。看StringManager的构造方法:
[java]
- private StringManager(String packageName) {
- String bundleName = packageName + ".LocalStrings";
- bundle = ResourceBundle.getBundle(bundleName);
- }
[java]
- public String getString(String key) {
- if (key == null) {
- String msg = "key is null";
- throw new NullPointerException(msg);
- }
- String str = null;
- try {
- str = bundle.getString(key);
- } catch (MissingResourceException mre) {
- str = "Cannot find message associated with key '" + key + "'";
- }
- return str;
- }
重点是budle.getString(key),这句代码能拿到LocalStrings.properties文件的key属性(这里是fileLogger.alreadyStarted)的值,即File Logger has already been started。这样我们便能定义多个属性文件,一个表示英文,一个表示汉语,一个属性文件表示一个语言,就能实现应用的国际化了。