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实例。这些配置信息包括是否显示日志名称、时间信息;如果显示日志打印时间,其格式如何;默认的日志级别是什么;支持单独配置一些日志名称的日志级别;如果将日志打印到日志文件,则日志文件的名称和目录在哪里等信息。下一篇博客我将实现这里说的这些功能。

自己写的日志框架--linkinLog4j--实现基本的框架功能的更多相关文章

  1. 自己写的日志框架--linkinLog4j--框架可配置+提性能

    OK,上一篇博客我们已经实现了日志框架的基本的功能,但是还有一个最大的问题就是日志输出地不能重定向,然后一些输出也不可控.那现在我们来实现一个比较完整的日志框架. 设计思路如下: 1,定义一堆常量Li ...

  2. Spring AOP 实现写事件日志功能

    什么是AOP?AOP使用场景?AOP相关概念?Spring AOP组件?如何使用Spring AOP?等等这些问题请参考博文:Spring AOP 实现原理 下面重点介绍如何写事件日志功能,把日志保存 ...

  3. C#写文本日志帮助类(支持多线程)改进版(不适用于ASP.NET程序)

    由于iis的自动回收机制,不适用于ASP.NET程序 代码: using System; using System.Collections.Concurrent; using System.Confi ...

  4. SQLite 预写式日志

    SQLite在3.7.0版本引入了WAL (Write-Ahead-Logging),WAL的全称是Write Ahead Logging,它是很多数据库中用于实现原子事务的一种机制,引入WAL机制之 ...

  5. C#写文本日志帮助类(支持多线程)

    代码: using System; using System.Configuration; using System.IO; using System.Threading.Tasks; namespa ...

  6. 预写式日志WAL

    Chapter 25. 预写式日志(Write-Ahead Logging (WAL)) Table of Contents 25.1. WAL 的好处 25.2. WAL 配置 25.3. 内部 预 ...

  7. 【我们一起写框架】MVVM的WPF框架(五)—完结篇

    前言 这篇文章是WPF框架系列的最后一篇,在这里我想阐述一下我对框架设计的理解. 我对框架设计的理解是这样的: 框架设计不应该局限于任何一种设计模式,我们在设计框架时,应该将设计模式揉碎,再重组:这样 ...

  8. 【我们一起写框架】MVVM的WPF框架(一)—序篇

    前言 我想,有一部分程序员应该是在二三线城市的,虽然不知道占比,但想来应该不在少数. 我是这部分人群中的一份子. 我们这群人,面对的客户,大多是国内中小企业,或者政府的小部门.这类客户的特点是,资金有 ...

  9. 【我们一起写框架】MVVM的WPF框架(二)—绑定

    MVVM的特点之一是实现数据同步,即,前台页面修改了数据,后台的数据会同步更新. 上一篇我们已经一起编写了框架的基础结构,并且实现了ViewModel反向控制Xaml窗体. 那么现在就要开始实现数据同 ...

  10. 【我们一起写框架】MVVM的WPF框架(三)—数据控件

    这世上,没人能一次性写出完美无缺的框架:因为,任何一个框架都需要项目的淬炼,然后才能升华,趋近完美. 所以,框架是个反复修改的东西,最终形成的东西. 如果你学了一点技术,觉得自己可以写出框架了,觉得自 ...

随机推荐

  1. QuickChm 制作chm文档 chm文档脚本错误,乱码

    最近学习bootstrap,到网上找了一些chm格式的文档,发现有些老旧,于是决定自己制作一个 用过以下这些chm制造器,包括easychm,chm精灵,chmeditor,chmfactory等等, ...

  2. Java JSON数据处理

    比方说要处理这么 {"data":[{"salt":"","plaintext":"xiaoxu", ...

  3. Jerry的UI5框架代码自学教程

    SAP UI5对View元素基于jQuery的操作方式,使得其学习曲线相对Angular/React来说比较平缓,至少对于我个人而言是这样.对于已经有jQuery经验的前端开发人员来说很容易上手. 使 ...

  4. 5分钟了解MySQL5.7的undo log在线收缩新特性

    Part1:写在最前 在MysQL5.6版本中,可以把undo log 回滚日志分离到一个单独的表空间里:其缺点是不能回收空间大小,until MysQL5.7,but MariadDB10.1暂不支 ...

  5. Spring Boot实战:集成Swagger2

    一.Swagger简介 上一篇文章中我们介绍了Spring Boot对Restful的支持,这篇文章我们继续讨论这个话题,不过,我们这里不再讨论Restful API如何实现,而是讨论Restful ...

  6. metasploit魔鬼训练营 (安全漏洞生命周期)

    1,前期 渗透测试工程师在挖掘0day的出来的 2,前中期 在一个密封的团体里,互相交流,利用漏洞带来的价值和,金钱 3,中期 安全漏洞被披露!!黑客们快速掌握该漏洞 4,中后期 漏洞被制作成更为简单 ...

  7. bzoj:1700: [Usaco2007 Jan]Problem Solving 解题

    Description 过去的日子里,农夫John的牛没有任何题目. 可是现在他们有题目,有很多的题目. 精确地说,他们有P (1 <= P <= 300) 道题目要做. 他们还离开了农场 ...

  8. bzoj 2946

    Description          给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l        读入单词 l        计算最长公共子串的长度 l        输 ...

  9. Codeforces Round #426 (Div. 2)【A.枚举,B.思维,C,二分+数学】

    A. The Useless Toy time limit per test:1 second memory limit per test:256 megabytes input:standard i ...

  10. hdu_1033(我怎么找到的这么水的题,只为保存代码。。。)

    #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> us ...