自己寫的日志框架--linkinLog4j--實現基本的框架功能



OK,上面一步我們已經知道了日志框架的必要性,然后我們也對比了直接不用日志框架來記錄日志的種種弊端。現在我們開始就來一步一步的實現自己的日志框架。

大體的思路如下:

1,實現多種日志級別,通過設值不同的日志級別來控制項目中日志的輸出等級,所以這里就要寫一個等級的枚舉,這個枚舉就定義LinkinLogLevel好了。

2,開始寫自己的日志核心類,定義LinkinLog4j類。然后這里要初始化日志對象,然后提供一系列的輸出日志的方法,比如info(),debug()等。

3,要控制等級,比如調用info()方法,那么就應該按照約定來輸出INFO級別以上的日志,自動屏蔽掉INFO等級之下的輸出。

4,每個輸出日志的方法最終都要調用一個輸出日志的方法,定義log()方法,該方法將一系列的日志內容(類名,等級,日志信息等)輸出到控制台上。


OK,大致的思路有了,我們現在開始寫代碼,現在我們寫代碼。

日志等級定義枚舉代碼如下:

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;
}

}

日志框架核心類LinkinLog4j的代碼如下:

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());
}

}

OK,現在代碼寫完了,我們自己寫2個測試類來測試下我們的日志框架有沒問題。

1,現在我們測試正常輸出日志情況,LinkinLog4jTest代碼如下:

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控制台輸出:

[test.junit4test.LinkinLog4jTest]-[INFO] info()。。。
[test.junit4test.LinkinLog4jTest]-[ERROR] error()。。。

OK,上面我配置了LinkinLog4jTest的日志級別是INFO級別,所以自動忽略掉了debug()方法的日志輸出。當然,我在初始化日志的時候也可以不傳入日志等級,這樣子的話就默認最低的等級,也就是輸出所有的日志。


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實例。這些配置信息包括是否顯示日志名稱、時間信息;如果顯示日志打印時間,其格式如何;默認的日志級別是什么;支持單獨配置一些日志名稱的日志級別;如果將日志打印到日志文件,則日志文件的名稱和目錄在哪里等信息。下一篇博客我將實現這里說的這些功能。



注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com