Tomcat源码分析(六)--日志记录器和国际化
  fztgkkRjHIsV 2023年11月14日 15 0


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

        

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);  
}


只要实现Logger就能有一个自己的日志记录器,其中setContainer是把日志记录器跟具体的容器关联,setVerbosity是设置日志的级别,log是具体的日志记录函数。FATAL,ERROR,WARNING,INFORMATION,DEBUG代表日志记录的五个级别,看单词就能明白意思。这里主要讲解一下FileLogger类,这是Tomcat的其中一个日志记录器,它把日志记录在一个文件中,FileLogger的启动方法和关闭仅仅是出发一个生命周期事件,并不做其他的事情:


public void start() throws LifecycleException {  
  
// Validate and update our current component state  
if (started)  
throw new LifecycleException  
"fileLogger.alreadyStarted"));  
null);//触发生命周期事件  
true;  
  
  }

这里有一行代码sm.getString("fileLogger.alreadyStarted"),牵涉到国际化的问题,等下再说这个问题。现在先看日志记录器,FileLogger的open方法打开一个文件用来记录日志:

private void open() {  
  
// Create the directory if necessary  
new File(directory);//directory等于logs,即在文件夹logs下新建日志文件  
if (!dir.isAbsolute())  
new File(System.getProperty("catalina.base"), directory);  
       dir.mkdirs();  
  
// Open the current log file  
try {  
           String pathname = dir.getAbsolutePath() + File.separator +  
               prefix + date + suffix;  
new PrintWriter(new FileWriter(pathname, true), true);  
catch (IOException e) {  
null;  
       }  
  
   }

这里得到一个PrintWriter的输出流writer,在log方法记录日志的时候会用到,下面看log方法:

public void log(String msg) {  
  
// Construct the timestamp we will use, if requested  
new Timestamp(System.currentTimeMillis());  
0, 19);  
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) {  
" " + msg);//写入时间和日志信息进日志文件中。  
else {  
              writer.println(msg);  
          }  
      }  
  
  }


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

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


LocalStrings_es.properties  
LocalStrings_ja.properties  
LocalStrings.properties


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


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中已经初始化:


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


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


代码很好理解,如果已经有StringManager实例了就直接从managers(这是一个Hashtable)中拿,没有就新建一个。看StringManager的构造方法:

public synchronized static StringManager getManager(String packageName) {  
       StringManager mgr = (StringManager)managers.get(packageName);  
if (mgr == null) {  
new StringManager(packageName);//新建一个StringManager  
           managers.put(packageName, mgr);  
       }  
return mgr;  
   }


private StringManager(String packageName) {  
".LocalStrings";  
       bundle = ResourceBundle.getBundle(bundleName);  
   }


看到我们熟悉的ResourceBundle类了,根据上面的讲解,ResourceBundle.getBundle(bundleName)能拿到默认的属性文件,也就是上面的LocalStrings.properties文件。再回到sm.getString("fileLogger.alreadyStarted"),看sm的getString方法:


public String getString(String key) {  
if (key == null) {  
"key is null";  
  
throw new NullPointerException(msg);  
       }  
  
null;  
  
try {  
           str = bundle.getString(key);  
catch (MissingResourceException mre) {  
"Cannot find message associated with key '" + key + "'";  
       }  
  
return str;  
   }


重点是budle.getString(key),这句代码能拿到LocalStrings.properties文件的key属性(这里是fileLogger.alreadyStarted)的值,即File Logger has already been started。这样我们便能定义多个属性文件,一个表示英文,一个表示汉语,一个属性文件表示一个语言,就能实现应用的国际化了。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月14日 0

暂无评论

推荐阅读
fztgkkRjHIsV