OK,上面一步咱们已经知道了日志框架的必要性,而后咱们也对比了直接不用日志框架来记录日志的种种弊端。如今咱们开始就来一步一步的实现本身的日志框架。html
大致的思路以下:java
1,实现多种日志级别,经过设值不一样的日志级别来控制项目中日志的输出等级,因此这里就要写一个等级的枚举,这个枚举就定义LinkinLogLevel好了。app
2,开始写本身的日志核心类,定义LinkinLog4j类。而后这里要初始化日志对象,而后提供一系列的输出日志的方法,好比info(),debug()等。框架
3,要控制等级,好比调用info()方法,那么就应该按照约定来输出INFO级别以上的日志,自动屏蔽掉INFO等级之下的输出。eclipse
4,每一个输出日志的方法最终都要调用一个输出日志的方法,定义log()方法,该方法将一系列的日志内容(类名,等级,日志信息等)输出到控制台上。测试
OK,大体的思路有了,咱们如今开始写代码,如今咱们写代码。ui
日志等级定义枚举代码以下:this
package test.junit4test; import java.util.HashMap; import java.util.Map; /** * @建立做者: LinkinPark * @建立时间: 2016年2月23日 * @功能描述: 日志等级枚举。 * <p> * Log4J中的全部的等级以下:all→trace→debug→info→warn→error→fatal→off * 这里本身模拟的等级以下:all→debug→info→error→off * </p> */ public enum LinkinLogLevel { ALL(Integer.MIN_VALUE, "ALL"), DEBUG(10000, "DEBUG"), INFO(20000, "INFO"), ERROR(30000, "ERROR"), OFF(Integer.MAX_VALUE, "OFF"); private final int status; private final String desc; private LinkinLogLevel(int status, String desc) { this.status = status; this.desc = desc; } public int getStatus() { return status; } public String getDesc() { return desc; } public String toString() { return status + ""; } /** * @建立时间: 2016年2月24日 * @相关参数: @return * @功能描述: 将全部的枚举封装一个Map返回 */ public static Map<Integer, LinkinLogLevel> getLevelMap() { Map<Integer, LinkinLogLevel> levelMap = new HashMap<>(5, 1); LinkinLogLevel[] values = LinkinLogLevel.values(); for (LinkinLogLevel linkinLogLevel : values) { levelMap.put(linkinLogLevel.getStatus(), linkinLogLevel); } return levelMap; } }
package test.junit4test; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Map; import java.util.Objects; public class LinkinLog4j { private static final Map<Integer, LinkinLogLevel> levelMap; static { levelMap = LinkinLogLevel.getLevelMap(); } // 定义2个属性,一个是每一个日志文件的名称,一个是每一个日志文件的等级 private final String name; private final int level; /***************** 定义一系列构造器 ***********************************/ public LinkinLog4j(Class<?> klass, LinkinLogLevel level) { this(klass.getName(), level.getStatus()); } public LinkinLog4j(Class<?> klass) { this(klass.getName(), LinkinLogLevel.ALL.getStatus()); } public LinkinLog4j(String name, int level) { this.name = name; this.level = level; } /***************** 定义一系列输出日志的方法 ***********************************/ public void info(String message) { info(message, null); } public void info(String message, Throwable cause) { log(LinkinLogLevel.INFO.getStatus(), message, cause); } public void debug(String message) { debug(message, null); } public void debug(Throwable cause) { debug(null, cause); } public void debug(String message, Throwable cause) { log(LinkinLogLevel.DEBUG.getStatus(), message, cause); } public void error(String message) { error(message, null); } public void error(String message, Throwable cause) { log(LinkinLogLevel.ERROR.getStatus(), message, cause); } /** * @建立时间: 2016年2月24日 * @相关参数: @param level * @相关参数: @param message * @相关参数: @param cause * @功能描述: 核心日志方法,输出日志内容到控制台 * <p> * 判断日志类定义的日志级别,控制一些日志方法的执行和不执行 * 依次将日志的信息一步一步的添加到StringBuilder中而后输出 * TODO * 1,这里最好使用责任链默认,上一步操做保持对下一步操做对象的封装,实现解耦 * 2,重定向输出,如今默认是控制台,未来重写writeLog方法,实现将日志输出到某一个文件下 * </p> */ private void log(int level, String message, Throwable cause) { if (isLevelEnabled(level)) { return; } StringBuilder builder = new StringBuilder(32); appendLogName2Log(builder, name); appendLevel2Log(builder, level); appendMessqge2Log(builder, message); appendCauseInfo2Log(builder, cause); writeLog(builder); } /** * @建立时间: 2016年2月24日 * @相关参数: @param level 日志类中调用的各类输出日志方法的等级 * @相关参数: @return true:忽略该输出日志方法,false:执行该输出日志方法 * @功能描述: 控制一些日志的输出仍是忽略 * <p> * 日志类本身配置的日志等级VS日志类中调用的各类输出日志方法的等级 * </p> */ private boolean isLevelEnabled(int level) { if (level < this.level) { return true; } return false; } /** * @建立时间: 2016年2月24日 * @相关参数: @param builder * @相关参数: @param level * @功能描述: 追加日志等级 */ private void appendLevel2Log(StringBuilder builder, int level) { builder.append("[").append(levelMap.get(level).getDesc()).append("]").append(" "); } /** * @建立时间: 2016年2月24日 * @相关参数: @param builder * @相关参数: @param name * @功能描述: 追加日志名字 */ private void appendLogName2Log(StringBuilder builder, String name) { builder.append("[").append(name).append("]-"); } /** * @建立时间: 2016年2月24日 * @相关参数: @param builder * @相关参数: @param message * @功能描述: 追加日志内容信息 */ private void appendMessqge2Log(StringBuilder builder, String message) { builder.append(message); } /** * @建立时间: 2016年2月24日 * @相关参数: @param builder * @相关参数: @param cause * @功能描述: 追加日志异常 */ private void appendCauseInfo2Log(StringBuilder builder, Throwable cause) { if (Objects.nonNull(cause)) { builder.append("<"); builder.append(cause.getMessage()); builder.append(">"); builder.append(System.getProperty("line.separator")); StringWriter writer = new StringWriter(); PrintWriter printer = new PrintWriter(writer); cause.printStackTrace(printer); printer.close(); builder.append(writer.toString()); } } /** * @建立时间: 2016年2月24日 * @相关参数: @param str 全部的日志输出的内容 * @功能描述: 控制台输出日志 */ public void writeLog(StringBuilder str) { System.out.println(str.toString()); } }
1,如今咱们测试正常输出日志状况,LinkinLog4jTest代码以下:spa
package test.junit4test; import org.junit.Test; public class LinkinLog4jTest { LinkinLog4j log = new LinkinLog4j(LinkinLog4jTest.class, LinkinLogLevel.INFO); @Test public void testLog() { log.debug("debug()。。。"); log.info("info()。。。"); log.error("error()。。。"); } }看下junit控制台输出:
看下eclipse控制台输出:debug
[test.junit4test.LinkinLog4jTest]-[INFO] info()。。。 [test.junit4test.LinkinLog4jTest]-[ERROR] error()。。。
2,如今咱们测试代码异常而后输出日志的状况。LinkinLog4jTest1代码以下:
package test.junit4test; import org.junit.Test; public class LinkinLog4jTest1 { LinkinLog4j log = new LinkinLog4j(LinkinLog4jTest1.class); @Test public void testLog() { try { throw new NullPointerException("测试异常日志空指针了呢"); } catch (Exception e) { log.debug("testLog()方法抛出异常:" + e.getMessage()); } log.debug("debug()。。。"); log.info("info()。。。"); log.error("error()。。。"); } }junit绿条,而后控制台输出以下:
[test.junit4test.LinkinLog4jTest1]-[DEBUG] testLog()方法抛出异常:测试异常日志空指针了呢 [test.junit4test.LinkinLog4jTest1]-[DEBUG] debug()。。。 [test.junit4test.LinkinLog4jTest1]-[INFO] info()。。。 [test.junit4test.LinkinLog4jTest1]-[ERROR] error()。。。
OK,大体的日志框架咱们都已经写完了,也实现了基本的功能,可是仍是有好多的问题的。
1,这里咱们初始化咱们的每一个日志类的时候就都要输出日志级别,若是不输出的话就默认最低,如何才能将初始化日志类的等级配置和代码解耦放到一个统一的地方来配置呢?使用XML统一配置就OK,也就是新增咱们的日志配置文件来统一来配置和初始化咱们的日志框架,给用户提供默认配置。
2,如今的框架咱们都是打印日志到控制台,暂时还不支持打印日志到文件。
3,打印日志的一系列方法,最终调用同一个输出日志的方法,没有实现人为控制日志输出的功能。好比用户在打印日志的过程当中,有些日志要输出这种样式,有些日志要输出那种样式,咱们如今的框架暂时还不支持。
为了实现接口的简单性,其中一种方法就是使用配置文件记录LinkinLog4j的配置信息,LinkinLog4j则根据配置信息初始化每个LinkinLog4j实例。这些配置信息包括是否显示日志名称、时间信息;若是显示日志打印时间,其格式如何;默认的日志级别是什么;支持单独配置一些日志名称的日志级别;若是将日志打印到日志文件,则日志文件的名称和目录在哪里等信息。下一篇博客我将实现这里说的这些功能。