天天看點

【鬥醫】【3】Web應用開發20天

在上面提及異常的中英文從資源檔案中讀取,若讀取失敗需要日志記錄,是以使用網上正在鬧騰的logback來記錄。關于logback與log4j這裡不做評判和說明,所有疑問可以請教谷歌。

一、logback的使用前期準備

1、在官網http://logback.qos.ch/download.html下載下傳logback;在官網http://www.slf4j.org/download.html下載下傳slf4j

2、在d:\medical\war\web-inf下建立lib檔案夾,用于放置本應用的所需jar包

3、解壓上面的下載下傳,把logback-access-1.0.13.jar、logback-classic-1.0.13.jar、logback-core-1.0.13.jar和slf4j-api-1.7.5.jar複制到d:\medical\war\web-inf\lib中

二、測試類

下面着手寫一個與本應用無關的測試類framelogger.java

1、打開eclipse,在medical工程上右鍵,選擇“new > class”,package填寫“com.medical.frame”,name填寫“frameloggerdemo”,點選“finish”

2、要使用日志類,應用先定義一個logger,然後再使用這個對象,是以代碼如下:

1

2

3

4

5

6

7

8

9

10

11

12

<code>package</code> <code>com.medical.frame;</code>

<code>import</code> <code>org.slf4j.logger;</code>

<code>import</code> <code>org.slf4j.loggerfactory;</code>

<code>public</code> <code>class</code> <code>frameloggerdemo</code>

<code>{</code>

<code>    </code><code>private</code> <code>static</code> <code>final</code> <code>logger logger = loggerfactory.getlogger(frameloggerdemo.</code><code>class</code><code>);</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            </code> 

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args)</code>

<code>    </code><code>{</code>

<code>        </code><code>logger.info(</code><code>"it's test for logback."</code><code>);</code>

<code>    </code><code>}</code>

<code>}</code>

3、你會發現上面代碼編譯不通過。這很正常,因為eclipse沒有導入所需的jar包。右鍵medical工程選擇“properties &gt; java build path &gt; libraries &gt; add jars...”,在jar選擇視窗中把d:\medical\war\web-inf\lib下的jar包添加進來,如圖:

【鬥醫】【3】Web應用開發20天

4、我想不僅把日志列印到控制台還要列印到日志檔案中,更甚者想插入到資料庫中,xml配置檔案可以協助完成。在d:\medical\war\下建立etc檔案夾,然後在etc下建立logconfig.xml檔案。

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

<code>&lt;?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code> <code>encoding</code><code>=</code><code>"utf-8"</code><code>?&gt;</code>

<code>&lt;</code><code>configuration</code><code>&gt;</code>

<code>    </code><code>&lt;!--日志檔案的存儲位址,一定要使用絕對路徑--&gt;</code>

<code>    </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"log_home"</code> <code>value</code><code>=</code><code>"d:/log"</code> <code>/&gt;</code>

<code>    </code><code>&lt;!--日志檔案字尾格式--&gt;</code>

<code>    </code><code>&lt;</code><code>timestamp</code> <code>key</code><code>=</code><code>"filesuffix"</code> <code>datepattern</code><code>=</code><code>"yyyymmdd'_'hhmm"</code> <code>/&gt;</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  </code> 

<code>    </code><code>&lt;!--控制台輸出--&gt;</code>

<code>    </code><code>&lt;</code><code>appender</code> <code>name</code><code>=</code><code>"stdout"</code> <code>class</code><code>=</code><code>"ch.qos.logback.core.consoleappender"</code><code>&gt;</code>

<code>        </code><code>&lt;!--輸出在控制台上的日志字元串編碼--&gt;</code>

<code>        </code><code>&lt;</code><code>encoding</code><code>&gt;utf-8&lt;/</code><code>encoding</code><code>&gt;</code>

<code>        </code><code>&lt;!--輸出在控制台上的日志格式--&gt;</code>

<code>        </code><code>&lt;</code><code>layout</code> <code>class</code><code>=</code><code>"ch.qos.logback.classic.patternlayout"</code><code>&gt;</code>

<code>            </code><code>&lt;!--%d表示日期; %thread表示線程名; %-5level表示從左顯示5個字元寬度的級别名; %logger表示日志類; %msg表示日志消息; %n表示換行--&gt;</code>

<code>            </code><code>&lt;</code><code>pattern</code><code>&gt;%d{yyyy-mm-dd hh:mm:ss.sss} [%thread] %-5level %logger{60} - %msg%n&lt;/</code><code>pattern</code><code>&gt;</code>

<code>        </code><code>&lt;/</code><code>layout</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>appender</code><code>&gt;</code>

<code>    </code><code>&lt;!--檔案輸出--&gt;</code>

<code>    </code><code>&lt;</code><code>appender</code> <code>name</code><code>=</code><code>"file"</code>  <code>class</code><code>=</code><code>"ch.qos.logback.core.rolling.rollingfileappender"</code><code>&gt;</code>

<code>        </code><code>&lt;!--日志對應的檔案名--&gt;</code>

<code>        </code><code>&lt;</code><code>file</code><code>&gt;${log_home}/trace${filesuffix}.txt&lt;/</code><code>file</code><code>&gt;</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      </code> 

<code>        </code><code>&lt;!--日志檔案記錄滾動政策--&gt;</code>

<code>        </code><code>&lt;</code><code>rollingpolicy</code> <code>class</code><code>=</code><code>"ch.qos.logback.core.rolling.fixedwindowrollingpolicy"</code><code>&gt;</code>

<code>            </code><code>&lt;!--當日志檔案超過大小時就壓縮為zip包--&gt;</code>

<code>            </code><code>&lt;</code><code>filenamepattern</code><code>&gt;${log_home}/trace${filesuffix}.%i.zip&lt;/</code><code>filenamepattern</code><code>&gt;</code>

<code>            </code><code>&lt;!--最多有10個zip壓縮包,超過10就覆寫之前的--&gt;</code>

<code>            </code><code>&lt;</code><code>minindex</code><code>&gt;1&lt;/</code><code>minindex</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>maxindex</code><code>&gt;10&lt;/</code><code>maxindex</code><code>&gt;</code>

<code>        </code><code>&lt;/</code><code>rollingpolicy</code><code>&gt;  </code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 </code> 

<code>        </code><code>&lt;!--檔案超過5m就重新生成新的日志檔案,老的日志檔案壓縮成zip包--&gt;</code>

<code>        </code><code>&lt;</code><code>triggeringpolicy</code> <code>class</code><code>=</code><code>"ch.qos.logback.core.rolling.sizebasedtriggeringpolicy"</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>maxfilesize</code><code>&gt;5mb&lt;/</code><code>maxfilesize</code><code>&gt;</code>

<code>        </code><code>&lt;/</code><code>triggeringpolicy</code><code>&gt;</code>

<code>        </code><code>&lt;!--輸出在檔案上的日志格式--&gt;</code>

<code>        </code><code>&lt;</code><code>encoder</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>pattern</code><code>&gt;%d{yyyy-mm-dd hh:mm:ss.sss} [%thread] %-5level %logger{60} - %msg%n &lt;/</code><code>pattern</code><code>&gt;</code>

<code>        </code><code>&lt;/</code><code>encoder</code><code>&gt;</code>

<code>    </code><code>&lt;!--日志輸出級别--&gt;</code>

<code>    </code><code>&lt;</code><code>root</code> <code>level</code><code>=</code><code>"info"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>appender-ref</code> <code>ref</code><code>=</code><code>"stdout"</code> <code>/&gt;</code>

<code>        </code><code>&lt;</code><code>appender-ref</code> <code>ref</code><code>=</code><code>"file"</code> <code>/&gt;</code>

<code>    </code><code>&lt;/</code><code>root</code><code>&gt;</code>

<code>&lt;/</code><code>configuration</code><code>&gt;</code>

【備注】:關于這個配置檔案建議感興趣的讀者,自己親自配置一下,這樣才能體驗每個标簽的含義。

5、運作frameloggerdemo發現并沒有在指定的d:/log下輸出日志檔案。試想一下,我們都沒有告訴logback輸出政策是什麼,它怎麼又會知道呢?是以這裡需要在main()運作之前把logconfig.xml加載進來,怎麼加載?怎麼隻加載一次?方法很多,可以使用spring的單例工廠,而這裡我們使用單例類。

<code>private</code> <code>static</code> <code>frameloggerdemo instance = </code><code>new</code> <code>frameloggerdemo();</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      </code> 

<code>private</code> <code>frameloggerdemo()</code>

<code>    </code><code>string logbackcfg = </code><code>"d:\\medical\\war\\etc\\logconfig.xml"</code><code>;</code>

<code>    </code><code>iloggerfactory loggerfactory = loggerfactory.getiloggerfactory();</code>

<code>    </code><code>joranconfigurator configurator = </code><code>new</code> <code>joranconfigurator();</code>

<code>    </code><code>configurator.setcontext((loggercontext) loggerfactory);</code>

<code>    </code><code>try</code>

<code>        </code><code>configurator.doconfigure(logbackcfg);</code>

<code>    </code><code>catch</code> <code>(joranexception e)</code>

<code>        </code><code>e.printstacktrace();</code>

【鬥醫】【3】Web應用開發20天

三、鬥醫應用如何使用logback呢?

從上面的frameloggerdemo可以看到,系統在使用logger對象之前必須把配置檔案加載進來,對于web應用它由多個servlet構成,是以最好在第一個servlet啟動時加載進來,後續的使用者就直接使用即可。

1、在d:\medical\war\web-inf\web.xml中定義名稱為action的servlet

<code>&lt;</code><code>web-app</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>servlet</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>servlet-name</code><code>&gt;action&lt;/</code><code>servlet-name</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>servlet-class</code><code>&gt;com.medical.frame.framelauncher&lt;/</code><code>servlet-class</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>load-on-startup</code><code>&gt;1&lt;/</code><code>load-on-startup</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>servlet</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>servlet-mapping</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>url-pattern</code><code>&gt;*.act&lt;/</code><code>url-pattern</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>servlet-mapping</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>welcome-file-list</code><code>&gt;</code>

<code>         </code><code>&lt;</code><code>welcome-file</code><code>&gt;index.html&lt;/</code><code>welcome-file</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>welcome-file-list</code><code>&gt;</code>

<code>&lt;/</code><code>web-app</code><code>&gt;</code>

2、該servlet說明當用戶端的請求為*.act時,都使用名稱為action的servlet處理,處理器為framelauncher這個類,它必須繼承httpservlet。servlet一般由init、doget、dopost、destory方法構成,是以framelauncher也不例外,它需要重寫這4個方法。

<code>import</code> <code>javax.servlet.servletexception;</code>

<code>import</code> <code>javax.servlet.http.httpservlet;</code>

<code>import</code> <code>javax.servlet.http.httpservletrequest;</code>

<code>import</code> <code>javax.servlet.http.httpservletresponse;</code>

<code>/**</code>

<code> </code><code>* 鬥醫系統動作登陸類</code>

<code> </code><code>*/</code>

<code>public</code> <code>class</code> <code>framelauncher </code><code>extends</code> <code>httpservlet</code>

<code>    </code><code>@override</code>

<code>    </code><code>public</code> <code>void</code> <code>init() </code><code>throws</code> <code>servletexception</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                           </code> 

<code>                                                                                                                                                                                                                                                                                                                                                                                                                       </code> 

<code>    </code><code>public</code> <code>void</code> <code>doget(httpservletrequest request, httpservletresponse response) </code><code>throws</code> <code>servletexception</code>

<code>    </code><code>public</code> <code>void</code> <code>dopost(httpservletrequest request, httpservletresponse response) </code><code>throws</code> <code>servletexception</code>

<code>        </code><code>doget(request, response);</code>

<code>    </code><code>public</code> <code>void</code> <code>destroy()</code>

3、當request請求到達web容器時,它會根據請求參數判斷使用哪個servlet處理,當servlet首次被調用到時,它會反射&lt;servlet-class&gt;定義的類,再調用init()方法進行初始化。而我們的日志加載就可以在這裡處理。

定義com.medical.frame.util.frameconfigutil.java,裡面定義靜态方法initlogconfig()用于加載logback日志配置檔案。

<code>public</code> <code>class</code> <code>frameconfigutil</code>

<code>    </code><code>/**</code>

<code>     </code><code>* 加載logback日志配置檔案</code>

<code>     </code><code>*/</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>initlogconfig(servletcontext context)</code>

<code>        </code><code>// 擷取logback配置檔案</code>

<code>        </code><code>stringbuilder logconfigpath = </code><code>new</code> <code>stringbuilder(context.getrealpath(</code><code>"/"</code><code>));</code>

<code>        </code><code>logconfigpath.append(</code><code>"etc"</code><code>).append(file.separator).append(</code><code>"logconfig.xml"</code><code>);</code>

<code>                                                                                                                                                                                                                                                                                                                                     </code> 

<code>        </code><code>// 加載logback配置檔案</code>

<code>        </code><code>iloggerfactory loggerfactory = loggerfactory.getiloggerfactory();</code>

<code>        </code><code>joranconfigurator configurator = </code><code>new</code> <code>joranconfigurator();</code>

<code>        </code><code>configurator.setcontext((loggercontext) loggerfactory);</code>

<code>        </code><code>try</code>

<code>        </code><code>{</code>

<code>            </code><code>configurator.doconfigure(logconfigpath.tostring());</code>

<code>        </code><code>}</code>

<code>        </code><code>catch</code> <code>(joranexception e)</code>

<code>            </code><code>system.out.println(</code><code>"[重要] init logback config error.\n"</code> <code>+ e.getmessage());</code>

4、framelauncher.init()方法調用frameconfigutil.initlogconfig()

<code>@override</code>

<code>public</code> <code>void</code> <code>init() </code><code>throws</code> <code>servletexception</code>

<code>    </code><code>servletcontext context = getservletcontext();</code>

<code>    </code><code>frameconfigutil.initlogconfig(context);</code>

至此logback的配置檔案在action這個servlet初始化時就加載完了,那該如何測試呢?

5、修改d:\medical\war\index.html檔案内容,修改後的内容如下:

<code>&lt;</code><code>html</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>head</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>title</code><code>&gt;medical&lt;/</code><code>title</code><code>&gt;   </code>

<code>    </code><code>&lt;/</code><code>head</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>body</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>a</code> <code>href</code><code>=</code><code>"index.act"</code><code>&gt;test logback&lt;/</code><code>a</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>body</code><code>&gt;</code>

<code>&lt;/</code><code>html</code><code>&gt;</code>

6、重寫com.medical.frame.framelauncher.doget()方法

<code> </code><code>* 定義日志對象</code>

<code>private</code> <code>static</code> <code>final</code> <code>logger logger = loggerfactory.getlogger(framelauncher.</code><code>class</code><code>);</code>

<code>public</code> <code>void</code> <code>doget(httpservletrequest request, httpservletresponse response) </code><code>throws</code> <code>servletexception</code>

<code>    </code><code>logger.error(</code><code>"it's test for logback."</code><code>);</code>

【備注1】:若使用者從頭到尾地寫這個應用,有可能會發現,盡管在d:\log下生成了日志檔案,但裡面沒有内容,這有可能是在加載logconfig.xml時使用的joranconfigurator不正确。應該使用import ch.qos.logback.classic.joran.joranconfigurator,而非import ch.qos.logback.access.joran.joranconfigurator。

【備注2】:若在實際操作這個執行個體時遇到問題,可參見附件,也可評論以便及時交流