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<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,是以在并發應用中使用它将不會遇到任何問題。