OK,在开始研究Log4j的源码以前,咱们先来本身模拟一个日志工具,名字就叫linkinlog4j好了。html
在软件开发过程当中,出现bug老是在所不免;事实上,以我我的经验,即便在实际开发阶段,fix bug时间要远超过写代码的时间。在开发阶段,比较有效的fix bug的方法固然是调试,然而若是代码比较复杂,并且开始对代码不是很熟悉,那么咱们很容易在方法调用之间迷失方向;若是bug出如今多线程环境中,那么不少时候调试就无能为力了;另外当代码部署到服务器上运行时,不论是在UAT测试环境仍是Production环境,此时要调试不少时候是不可能。相信有过几年工做经验的码农在和运维,测试交流的时候常常说起的日志级别了。java
为了解决这些问题,咱们能够在开发过程当中事先在一些关键点上打印出一些日志信息(log),以利于在出问题后能知道当时运行的环境信息,特别是在production上,咱们能够分析log以肯定问题所在,固然前提log信息足够多。不过加入日志信息后,不难想象,它会对程序的运行性能产生必定的影响,并且若是log过多会致使有用的信息不容易找到,若是过少又不具有很大的参考价值,这样的日志除了下降程序的性能,貌似对其余方面没有帮助。服务器
关于性能,Log4J号称在这面作了不少优化,可是听说logback作的更好(logback的源码还没来得及看,只是简单的用过,因此还不是很了解);而关于如何写log、在哪里写log、要把那些信息写入log中,我的感受这是一门很大的学问,并且也是根据不一样项目而不一样,我以菜鸟身份在这里也就很少作赘述了。多线程
package test.junit4test; import org.junit.Test; public class LinkinLogTest { @Test public void testLog() { System.out.println("测试方法开始。。。"); try { throw new NullPointerException("这里人工抛出一个异常!"); } catch (Exception e) { System.out.println("人工捕获一个异常" + e.getMessage()); e.printStackTrace(System.out); } System.out.println("测试方法结束。。。"); } }
测试方法开始。。。 人工捕获一个异常这里人工抛出一个异常! java.lang.NullPointerException: 这里人工抛出一个异常! at test.junit4test.LinkinTest.testLog(LinkinTest.java:14) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) 测试方法结束。。。OK,没有问题。可是这段代码有这很明显的缺陷: 1,咱们这些日志只能打印在控制台。那么若是咱们发布了咱们的项目到咱们的服务器,咱们须要查看的是日志文件而不是控制台了。 2,这里咱们本身写的这个日志类没有提现等级,若是我想控制不一样的级别来控制某些的内容的输出和不输出,即便咱们这里加了一个flag旗标,也没有从根本上解决问题。 3,如今咱们的日志的内容都是比较简单,若是咱们想要在控制台显示咱们的日志的类名,方法运行的时间,所在的线程等等这些内容时,就要不停的重复的写一堆代码。 4,不少的时候咱们但愿咱们的日志按照某种约定来生成,好比说按照日期,按照文件的大小存档,这个时候咱们上面的那段代码根本实现不了。 OK,问题不少,那咱们来一步一步的解决吧。