zoukankan      html  css  js  c++  java
  • Tomcat源码分析(六)日志记录器和国际化

    本系列转载自 http://blog.csdn.net/haitao111313/article/category/1179996  

    日志记录器挺简单的,没有很多东西,最主要的就是一个Logger接口:

            

    1. public interface Logger {  
    2.     public static final int FATAL = Integer.MIN_VALUE;  
    3.     public static final int ERROR = 1;  
    4.     public static final int WARNING = 2;  
    5.     public static final int INFORMATION = 3;  
    6.     public static final int DEBUG = 4;  
    7.     public Container getContainer();  
    8.     public void setContainer(Container container);  
    9.     public String getInfo();  
    10.     public int getVerbosity();  
    11.     public void setVerbosity(int verbosity);  
    12.     public void addPropertyChangeListener(PropertyChangeListener listener);  
    13.     public void log(String message);  
    14.     public void log(Exception exception, String msg);  
    15.     public void log(String message, Throwable throwable);  
    16.     public void log(String message, int verbosity);  
    17.     public void log(String message, Throwable throwable, int verbosity);  
    18.     public void removePropertyChangeListener(PropertyChangeListener listener);  
    19. }  
    只要实现Logger就能有一个自己的日志记录器,其中setContainer是把日志记录器跟具体的容器关联,setVerbosity是设置日志的级别,log是具体的日志记录函数。FATAL,ERROR,WARNING,INFORMATION,DEBUG代表日志记录的五个级别,看单词就能明白意思。这里主要讲解一下FileLogger类,这是Tomcat的其中一个日志记录器,它把日志记录在一个文件中,FileLogger的启动方法和关闭仅仅是出发一个生命周期事件,并不做其他的事情:

    1. public void start() throws LifecycleException {  
    2.   
    3.       // Validate and update our current component state  
    4.       if (started)  
    5.           throw new LifecycleException  
    6.               (sm.getString("fileLogger.alreadyStarted"));  
    7.       lifecycle.fireLifecycleEvent(START_EVENT, null);//触发生命周期事件  
    8.       started = true;  
    9.   
    10.   }  
    这里有一行代码sm.getString("fileLogger.alreadyStarted"),牵涉到国际化的问题,等下再说这个问题。现在先看日志记录器,FileLogger的open方法打开一个文件用来记录日志:

    1. private void open() {  
    2.   
    3.        // Create the directory if necessary  
    4.        File dir = new File(directory);//directory等于logs,即在文件夹logs下新建日志文件  
    5.        if (!dir.isAbsolute())  
    6.            dir = new File(System.getProperty("catalina.base"), directory);  
    7.        dir.mkdirs();  
    8.   
    9.        // Open the current log file  
    10.        try {  
    11.            String pathname = dir.getAbsolutePath() + File.separator +  
    12.                prefix + date + suffix;  
    13.            writer = new PrintWriter(new FileWriter(pathname, true), true);  
    14.        } catch (IOException e) {  
    15.            writer = null;  
    16.        }  
    17.   
    18.    }  
    这里得到一个PrintWriter的输出流writer,在log方法记录日志的时候会用到,下面看log方法:
    1. public void log(String msg) {  
    2.   
    3.       // Construct the timestamp we will use, if requested  
    4.       Timestamp ts = new Timestamp(System.currentTimeMillis());  
    5.       String tsString = ts.toString().substring(019);  
    6.       String tsDate = tsString.substring(010);  
    7.   
    8.       // If the date has changed, switch log files  
    9.       if (!date.equals(tsDate)) {  //如果日期改变,则新建一个日志文件。  
    10.           synchronized (this) {  
    11.               if (!date.equals(tsDate)) {  
    12.                   close();  
    13.                   date = tsDate;  
    14.                   open();  
    15.               }  
    16.           }  
    17.       }  
    18.   
    19.       // Log this message, timestamped if necessary  
    20.       if (writer != null) {  
    21.           if (timestamp) {  
    22.               writer.println(tsString + " " + msg);//写入时间和日志信息进日志文件中。  
    23.           } else {  
    24.               writer.println(msg);  
    25.           }  
    26.       }  
    27.   
    28.   }  


    writer.println把时间和日志信息写进日志文件中。当然,这个日志记录器一般是给Tomcat自己用的,我们也可以实现Logger接口,然后重写它的open方法(打开我们自己的日志文件)和log方法(用来在我们自己的日志文件中记录日志信息)。

    现在再来看刚才提到的代码sm.getString("fileLogger.alreadyStarted")。在理解这句代码前先看一个jdk的类ResourceBundle,这个类提供了国际化的方便。这个类的作用就是读取.properties文件,但是会根据文件名来获取当前系统的语言信息,然后读取对应文件的属性值。当然首先要有各国不同的属性文件,才能国际化,Tomcat的每个包下都有几个不同的属性文件,org.apache.catalina.logger包下有如下三个属性文件:

    1. LocalStrings_es.properties  
    2. LocalStrings_ja.properties  
    3. LocalStrings.properties  
    分别表示三种语言的属性文件。LocalStrings.properties属性文件是默认的属性文件。看LocalStrings.properties属性中的内容:

    1. fileLogger.alreadyStarted=File Logger has already been started  
    2. fileLogger.notStarted=File Logger has not yet been started  
    3. tomcatLogger.alreadyStarted=Tomcat Logger has already been started  
    4. tomcatLogger.notStarted=Tomcat Logger has not yet been started  

    ResourceBundle类通过getBundle方法获取,参数是属性文件的包名家名字前缀,上面就是包名加LocalStrings。通过getString(String key)方法获取属性文件中的参数:
    现在来看代码sm.getString("fileLogger.alreadyStarted"),sm是StringManager的实例,在FileLogger中已经初始化:

    1. private StringManager sm =StringManager.getManager(Constants.Package);  

    Constants.Package得到包的名字,getManager方法代码如下:

    1. public synchronized static StringManager getManager(String packageName) {  
    2.        StringManager mgr = (StringManager)managers.get(packageName);  
    3.        if (mgr == null) {  
    4.            mgr = new StringManager(packageName);//新建一个StringManager  
    5.            managers.put(packageName, mgr);  
    6.        }  
    7.        return mgr;  
    8.    }  

    代码很好理解,如果已经有StringManager实例了就直接从managers(这是一个Hashtable)中拿,没有就新建一个。看StringManager的构造方法:
    1. private StringManager(String packageName) {  
    2.        String bundleName = packageName + ".LocalStrings";  
    3.        bundle = ResourceBundle.getBundle(bundleName);  
    4.    }  
    看到我们熟悉的ResourceBundle类了,根据上面的讲解,ResourceBundle.getBundle(bundleName)能拿到默认的属性文件,也就是上面的LocalStrings.properties文件。再回到sm.getString("fileLogger.alreadyStarted"),看sm的getString方法:

    1. public String getString(String key) {  
    2.        if (key == null) {  
    3.            String msg = "key is null";  
    4.   
    5.            throw new NullPointerException(msg);  
    6.        }  
    7.   
    8.        String str = null;  
    9.   
    10.        try {  
    11.            str = bundle.getString(key);  
    12.        } catch (MissingResourceException mre) {  
    13.            str = "Cannot find message associated with key '" + key + "'";  
    14.        }  
    15.   
    16.        return str;  
    17.    }  

    重点是budle.getString(key),这句代码能拿到LocalStrings.properties文件的key属性(这里是fileLogger.alreadyStarted)的值,即File Logger has already been started。这样我们便能定义多个属性文件,一个表示英文,一个表示汉语,一个属性文件表示一个语言,就能实现应用的国际化了。
  • 相关阅读:
    [翻译]XNA 3.0 Game Programming Recipes之fiftynine
    [翻译]XNA 3.0 Game Programming Recipes之fiftyseven
    [翻译]XNA 3.0 Game Programming Recipes之fiftyeight
    [翻译]XNA外文博客文章精选之two
    [翻译]XNA 3.0 Game Programming Recipes之fiftyfour
    [翻译]XNA 3.0 Game Programming Recipes之fiftythree
    [翻译]XNA外文博客文章精选之three
    [翻译]XNA 3.0 Game Programming Recipes之fiftysix
    [翻译]XNA 3.0 Game Programming Recipes之fiftyfive
    [翻译]XNA外文博客文章精选之one
  • 原文地址:https://www.cnblogs.com/chenying99/p/2798442.html
Copyright © 2011-2022 走看看