自己写的日志框架--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实例。这些配置信息包括是否显示日志名称、时间信息;如果显示日志打印时间,其格式如何;默认的日志级别是什么;支持单独配置一些日志名称的日志级别;如果将日志打印到日志文件,则日志文件的名称和目录在哪里等信息。下一篇博客我将实现这里说的这些功能。
自己写的日志框架--linkinLog4j--实现基本的框架功能的更多相关文章
- 自己写的日志框架--linkinLog4j--框架可配置+提性能
OK,上一篇博客我们已经实现了日志框架的基本的功能,但是还有一个最大的问题就是日志输出地不能重定向,然后一些输出也不可控.那现在我们来实现一个比较完整的日志框架. 设计思路如下: 1,定义一堆常量Li ...
- Spring AOP 实现写事件日志功能
什么是AOP?AOP使用场景?AOP相关概念?Spring AOP组件?如何使用Spring AOP?等等这些问题请参考博文:Spring AOP 实现原理 下面重点介绍如何写事件日志功能,把日志保存 ...
- C#写文本日志帮助类(支持多线程)改进版(不适用于ASP.NET程序)
由于iis的自动回收机制,不适用于ASP.NET程序 代码: using System; using System.Collections.Concurrent; using System.Confi ...
- SQLite 预写式日志
SQLite在3.7.0版本引入了WAL (Write-Ahead-Logging),WAL的全称是Write Ahead Logging,它是很多数据库中用于实现原子事务的一种机制,引入WAL机制之 ...
- C#写文本日志帮助类(支持多线程)
代码: using System; using System.Configuration; using System.IO; using System.Threading.Tasks; namespa ...
- 预写式日志WAL
Chapter 25. 预写式日志(Write-Ahead Logging (WAL)) Table of Contents 25.1. WAL 的好处 25.2. WAL 配置 25.3. 内部 预 ...
- 【我们一起写框架】MVVM的WPF框架(五)—完结篇
前言 这篇文章是WPF框架系列的最后一篇,在这里我想阐述一下我对框架设计的理解. 我对框架设计的理解是这样的: 框架设计不应该局限于任何一种设计模式,我们在设计框架时,应该将设计模式揉碎,再重组:这样 ...
- 【我们一起写框架】MVVM的WPF框架(一)—序篇
前言 我想,有一部分程序员应该是在二三线城市的,虽然不知道占比,但想来应该不在少数. 我是这部分人群中的一份子. 我们这群人,面对的客户,大多是国内中小企业,或者政府的小部门.这类客户的特点是,资金有 ...
- 【我们一起写框架】MVVM的WPF框架(二)—绑定
MVVM的特点之一是实现数据同步,即,前台页面修改了数据,后台的数据会同步更新. 上一篇我们已经一起编写了框架的基础结构,并且实现了ViewModel反向控制Xaml窗体. 那么现在就要开始实现数据同 ...
- 【我们一起写框架】MVVM的WPF框架(三)—数据控件
这世上,没人能一次性写出完美无缺的框架:因为,任何一个框架都需要项目的淬炼,然后才能升华,趋近完美. 所以,框架是个反复修改的东西,最终形成的东西. 如果你学了一点技术,觉得自己可以写出框架了,觉得自 ...
随机推荐
- iOS 动画篇 (二) CAShapeLayer与CoreAnimation结合使用
接上一篇博客 iOS 动画篇(一) Core Animation CAShapeLayer是CALayer的一个子类,使用这个类能够很轻易实现曲线的动画. 先来一个折线动画效果: 示例代码: //1. ...
- ionic serve 突然报错 node-sass
正常打开项目,并开启浏览器测试模式: 执行命令: ionic serve: 结果报错: 解决方法: 你可以按照 里面的提示: 直接执行命令: npm rebuild node-sass 然后再重新执 ...
- Lottie的使用
一.简介 Lottie是Airbnb开源的一个面向IOS.Android.React Native的动画库,能分析Adobe After Effects导出的动画,并且能让原生App像使用静态素材一样 ...
- 什么是CDN加速?(转载)
随着互联网的发展,用户在使用网络时对网站的浏览速度和效果愈加重视,但由于网民数量激增,网络访问路径过长,从 而使用户的访问质量受到严重影响.特别是当用户与网站之间的链路被突发的大流量数据拥塞时,对于异 ...
- 13、ABPZero系列教程之拼多多卖家工具 微信公众号开发前的准备
因为是开发阶段,我需要在本地调试,而微信开发需要配置域名,这样natapp.cn就有了用武之地,应该说natapp就是为此而生的. natapp.cn是什么 这是一个内网映射的网站,支持微信公众号.小 ...
- 基本命令行操作1(java编译)
1. 设置环境变量,具体:https://www.cnblogs.com/shinge/p/5500002.html "cd + 文件名" 可进入指定文件,"cd..&q ...
- mysql一些函数的记录
今天瞎搞还是弄不出报名程序,偶尔记住了几个mysql函数 mysql_connect()连接数据库 mysql_error()输出文本报错 mysql_select_db()函数设置活动的MySQL ...
- bzoj 1415: [Noi2005]聪聪和可可
直接上记忆化搜索 #include<queue> #include<cstdio> #include<algorithm> using namespace std; ...
- 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He…【字符串+模拟】
P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He… 题目描述 众所周知,在每一个彗星后都有一只UFO.这些UFO时常来收集地球上的忠诚支持者.不幸的是,他们的飞碟每次出行都 ...
- CodeForces798-B. Mike and strings-string中的find()函数
好久好久好久之前的一个题,今天翻cf,发现这个题没过,补一下. B. Mike and strings time limit per test 2 seconds memory limit per t ...