天天看點

測試并發應用(五) 編寫有效的日志

log工具提供了允許你寫資訊到一個或者多個目的地的機制。一個logger是由以下這些組成:

一個或多個處理者: 一個處理者将決定目的地和log資訊的格式。你可以把日志資訊寫入操控台,文檔,或者資料庫。

名字: 通常logger使用的名字是基于類名或者它的包名。

等級: 日志資訊有等級來表明它的重要性。logger也有個等級是用來決定寫什麼樣的資訊。它隻會寫和這個等級一樣重要的,或者更重要的資訊。

為了以下2個主要目的,你應該使用log 系統:

當異常被捕捉,寫盡可能多的資訊。這個會幫助你定位錯誤并解決它。

寫關于程式正在執行的類和方法的資訊。

在這個指南,你将學習如何使用 java.util.logging 包提供的類來添加一個log系統到你的并發應用。

準備

指南中的例子是使用eclipse ide 來實作的。如果你使用eclipse 或者其他的ide,例如netbeans, 打開并建立一個新的java項目。

怎麼做呢…

按照這些步驟來實作下面的例子:

<code>001</code>

<code>//1.    建立一個類,名為myformatter ,擴充 java.util.logging.formatter 類。實作抽象方法 format()。它接收一個 logrecord 對象作為參數,并傳回一個有着日志資訊 string 對象。</code>

<code>002</code>

<code>public</code> <code>class</code> <code>myformatter</code><code>extends</code> <code>formatter {</code>

<code>003</code>

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

<code>004</code>

<code>    </code><code>public</code> <code>string format(logrecord record) {</code>

<code>005</code>

<code>006</code>

<code>        </code><code>stringbuilder sb=</code><code>new</code> <code>stringbuilder();</code>

<code>007</code>

<code>        </code><code>sb.append(</code><code>"["</code><code>+record.getlevel()+</code><code>"] - "</code><code>);</code>

<code>008</code>

<code>        </code><code>sb.append(</code><code>new</code> <code>date(record.getmillis())+</code><code>" : "</code><code>);</code>

<code>009</code>

<code>        </code><code>sb.append(record.getsourceclassname()+</code><code>"."</code><code>+record.getsourcemethodname()+</code><code>" : "</code><code>);</code>

<code>010</code>

<code>        </code><code>sb.append(record.getmessage()+</code><code>"\n"</code><code>);</code>

<code>011</code>

<code>012</code>

<code>        </code><code>return</code> <code>sb.tostring();</code>

<code>013</code>

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

<code>014</code>

<code>015</code>

<code>//2.   建立一個類,名為 mylogger。</code>

<code>016</code>

<code>public</code> <code>class</code> <code>mylogger {</code>

<code>017</code>

<code>018</code>

<code>//3.   聲明一個私有 static handler 屬性,名為 handler。</code>

<code>019</code>

<code>private</code> <code>static</code> <code>handler handler;</code>

<code>020</code>

<code>021</code>

<code>//4.   實作公共 static 方法 getlogger() 來建立 logger 對象,你将要使用它來寫日志資訊。它接收一個string 參數,名為 name。</code>

<code>022</code>

<code>public</code> <code>static</code> <code>logger getlogger(string name){</code>

<code>023</code>

<code>024</code>

<code>//5.   使用 logger 類的getlogger() 方法,擷取與 java.util.logging.logger 相關聯的 name 作為參數。</code>

<code>025</code>

<code>logger logger=logger.getlogger(name);</code>

<code>026</code>

<code>027</code>

<code>//6.   使用 setlevel() 方法,确立用來寫入全部資訊的log級别。</code>

<code>028</code>

<code>logger.setlevel(level.all);</code>

<code>029</code>

<code>030</code>

<code>//7.    如果處理者屬性為null值,建立一個新的 filehandler 對象在 recipe8.log 檔案内寫日志資訊。使用 setformatter()對象給處理者配置設定一個 myformatter  對象作為格式。</code>

<code>031</code>

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

<code>032</code>

<code>    </code><code>if</code> <code>(handler==</code><code>null</code><code>) {</code>

<code>033</code>

<code>        </code><code>handler=</code><code>new</code> <code>filehandler(</code><code>"recipe8.log"</code><code>);</code>

<code>034</code>

<code>        </code><code>formatter format=</code><code>new</code> <code>myformatter();</code>

<code>035</code>

<code>        </code><code>handler.setformatter(format);</code>

<code>036</code>

<code>037</code>

<code>038</code>

<code>//8.   if the 如果 logger 對象還有一個與之相關聯的處理者,使用 addhandler() 方法配置設定一個處理者。</code>

<code>039</code>

<code>    </code><code>if</code> <code>(logger.gethandlers().length==</code><code>0</code><code>) {</code>

<code>040</code>

<code>        </code><code>logger.addhandler(handler);</code>

<code>041</code>

<code>042</code>

<code>}</code><code>catch</code> <code>(securityexception e) {</code>

<code>043</code>

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

<code>044</code>

<code>}</code><code>catch</code> <code>(ioexception e) {</code>

<code>045</code>

<code>046</code>

<code>}</code>

<code>047</code>

<code>048</code>

<code>//9.   傳回建立的 logger 對象。</code>

<code>049</code>

<code>return</code> <code>logger;</code>

<code>050</code>

<code>051</code>

<code>052</code>

<code>//10. 建立一個類,名為task,它實作runnable 接口。它将是用來測試你的logger對象的任務。</code>

<code>053</code>

<code>public</code> <code>class</code> <code>task</code><code>implements</code> <code>runnable {</code>

<code>054</code>

<code>055</code>

<code>//11. 實作 run() 方法。</code>

<code>056</code>

<code>@override</code>

<code>057</code>

<code>public</code> <code>void</code> <code>run() {</code>

<code>058</code>

<code>059</code>

<code>//12. 首先,聲明一個 logger 對象,名為 logger。使用 mylogger 類的 getlogger() 方法傳遞這個類的名字為參數來初始它。</code>

<code>060</code>

<code>logger logger= mylogger.getlogger(</code><code>this</code><code>.getclass().getname());</code>

<code>061</code>

<code>062</code>

<code>//13. 使用 entering() 方法寫日志資訊表明執行開始。</code>

<code>063</code>

<code>logger.entering(thread.currentthread().getname(),</code><code>"run()"</code><code>);</code>

<code>064</code>

<code>//休眠2秒。</code>

<code>065</code>

<code>066</code>

<code>    </code><code>timeunit.seconds.sleep(</code><code>2</code><code>);</code>

<code>067</code>

<code>}</code><code>catch</code> <code>(interruptedexception e) {</code>

<code>068</code>

<code>069</code>

<code>070</code>

<code>071</code>

<code>//14.使用 exiting() 方法寫日志資訊表明執行結束</code>

<code>072</code>

<code>logger.exiting(thread.currentthread().getname(),</code><code>"run()"</code><code>,thread.currentthread());</code>

<code>073</code>

<code>074</code>

<code>075</code>

<code>//15. 建立例子的主類通過建立一個類,名為 main 并添加 main()方法。</code>

<code>076</code>

<code>public</code> <code>class</code> <code>main {</code>

<code>077</code>

<code>078</code>

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

<code>079</code>

<code>080</code>

<code>//16. 聲明一個 logger 對象,名為 logger。使用 mylogger 類的 getlogger() 方法傳遞字元串 core 作為參數來初始它</code>

<code>081</code>

<code>logger logger=mylogger.getlogger(</code><code>"core"</code><code>);</code>

<code>082</code>

<code>083</code>

<code>//17. 使用 entering() 方法寫日志資訊表明主程式開始執行。</code>

<code>084</code>

<code>logger.entering(</code><code>"core"</code><code>,</code><code>"main()"</code><code>,args);</code>

<code>085</code>

<code>086</code>

<code>//18. 建立 thread array 來儲存5個線程。</code>

<code>087</code>

<code>thread threads[]=</code><code>new</code> <code>thread[</code><code>5</code><code>];</code>

<code>088</code>

<code>089</code>

<code>//19. 建立5個task對象和5個執行他們的線程。寫日志資訊表明,你将運作一個新的線程和表明你已經建立了線程。</code>

<code>090</code>

<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i&lt;threads.length; i++) {</code>

<code>091</code>

<code>    </code><code>logger.log(level.info,</code><code>"launching thread: "</code><code>+i);</code>

<code>092</code>

<code>    </code><code>task task=</code><code>new</code> <code>task();</code>

<code>093</code>

<code>    </code><code>threads[i]=</code><code>new</code> <code>thread(task);</code>

<code>094</code>

<code>    </code><code>logger.log(level.info,</code><code>"thread created: "</code><code>+ threads[i]. getname());</code>

<code>095</code>

<code>    </code><code>threads[i].start();</code>

<code>096</code>

<code>097</code>

<code>098</code>

<code>//20. 寫日志資訊表明你已經建立了線程。</code>

<code>099</code>

<code>logger.log(level.info,</code><code>"ten threads created."</code><code>+</code><code>"waiting for its finalization"</code><code>);</code>

<code>100</code>

<code>101</code>

<code>//21. 使用 join() 方法等待5個線程的終結。在每個線程終結之後,寫日志資訊表明線程已經結束。</code>

<code>102</code>

<code>103</code>

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

<code>104</code>

<code>        </code><code>threads[i].join();</code>

<code>105</code>

<code>        </code><code>logger.log(level.info,</code><code>"thread has finished its execution"</code><code>,threads[i]);</code>

<code>106</code>

<code>    </code><code>}</code><code>catch</code> <code>(interruptedexception e) {</code>

<code>107</code>

<code>        </code><code>logger.log(level.severe,</code><code>"exception"</code><code>, e);</code>

<code>108</code>

<code>109</code>

<code>110</code>

<code>111</code>

<code>//22. 使用 exiting() 方法寫一個日志資訊表明主程式運作結束。</code>

<code>112</code>

<code>logger.exiting(</code><code>"core"</code><code>,</code><code>"main()"</code><code>);</code>

<code>113</code>

它是如何工作的…

在這個指南裡,你已經使用 java logging api 提供的logger類 在并發應用中寫日志資訊。首先,你實作了 myformatter 類來給日志資訊一個格式。這個類擴充 formatter 類,聲明了抽象方法 format()。此方法接收 logrecord 對象的全部日志消息資訊,并傳回一條格式化的日志資訊。在你的類裡使用了 logrecord類的以下這些方法來擷取日志資訊:

getlevel(): 傳回的資訊的級别。

getmillis():傳回日期,當一條資訊被發送給 logger 對象。

getsourceclassname(): 傳回發送資訊給logger的類的名字。

getsourcemessagename():傳回發送資訊給logger的方法的名字

getmessage() 傳回日志資訊。mylogger 類實作了靜态方法 getlogger(), 建立 logger 對象并配置設定 handler 對象使用myformatter的格式在recipe8.log 檔案中寫入日志資訊。你可以使用這個類的靜态方法 getlogger() 建立對象。此方法傳回每個不同的對象作為參數傳遞的名字。 你隻要建立一個handler對象,全部的logger對象都會使用它在同一個檔案中寫日志資訊。你還配置了logger寫全部的日志資訊,無論資訊級别。

最後,你實作了 task 對象和一個主程式在logfile寫入不同的日志資訊。你使用了以下的方法:

entering():寫 finer 等級的資訊,表明方法開始運作

exiting(): 寫 finer 等級的資訊,表明方法結束運作

log(): 寫特定級别的資訊

更多…

當你使用log類時,你必須考慮2個重要點:

寫必需的資訊:如果你寫過少的資訊,那麼logger就沒有滿足它的目的變的不是特别有作用。如果你寫過多的資訊,那麼就會産生很大的日志檔案,就不好管理且很難擷取必要資訊。

對資訊使用适當的級别:如果你用進階别寫入消息資訊(information messages),或者使用低級别來寫入報錯資訊,那麼你就會讓看logfiles的人很困惑。就會變得很難了解到底發生了什麼錯誤,或者有過多的資訊來分析錯誤的主要原因。

還有很多其他庫比 java.util. logging 包提供了更完整的log系統,例如 log4j 或者 slf4j libraries。但是 java.util.logging 是java api 的一部分,而且它的全部方法都是 multi-thread safe,是以在并發應用中使用它将不會遇到任何問題。