天天看点

测试并发应用(五) 编写有效的日志

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,所以在并发应用中使用它将不会遇到任何问题。