代码方式配置Log4j并实现线程级日志管理 第二部分

一 设计类结构

  第一部分说了两件事儿:

  1. 如何根据配置文件分析有用的信息以便对底层结构进行挖掘
  2. 如何查看源码的类结构以便调研需求完成后续开发设计

  在了解我们的终极目标之后,不忙写代码,之前的工作仅仅是调研,为的是确认需求能否实现,以及实现过程中可能触及的技术风险。接下来我们需要根据需求及之前所做的调研,对我们即将要开发的功能做一个初步设计,将实现的框架搭建起来。

  我习惯先定义一个类,比如说LogUtil.java:

package com.bubbling;

/**
 * 1.实现代码方式配置Log4j <br>
 * 2.实现线程级日志对象管理 <br>
 * 3.实现日志的异步输出模式 <br>
 * 4.实现按日志文件大小及日期进行文件备份
 * 
 * @author 胡楠
 *
 */
public class LogUtil
{
	
}

  这个类将作为一个日志工具类使用,对外提供相关方法供调用者进行日志属性配置及日志内容输出。

二 成员设计

  接下来是成员设计,成员设计可以说是最重要的,它直接影响类的整体设计思路。先说日志配置中有哪些常见的、使用率高的、必要的属性:

  1. 文件名
  2. 文件存储路径
  3. 单一文件最大存储容量
  4. 备份文件最大保留数量
  5. 是否开启异步模式
  6. 日志输出目的地:文件/控制台/其他

  因LogUtil是一个工具类,其成员设计初步拟定为类级成员,作为可被外部配置的公有信息,初步的成员设计如下:

public class LogUtil
{
	private static String fileName;
	private static String filePath;
	private static String fileSize;
	private static int maxBackupIndex;
	private static boolean isAsynchronous;
	private static int logTarget;
	
	public static void setFileName(String value)
	{
		fileName = value;
	}
	public static String getFileName()
	{
		return fileName;
	}
	……
}

  因为需要明确日志输出的目的地,那么我们需要对目的地的定义进行规范设计,像这种对同一类型且仅区分内容的属性设计,通常使用静态不可变成员,或者枚举类型来定义,这里我使用枚举类型来定义它,这样会使得封装性更强:

public class LogUtil
{
	private static String fileName;
	private static String filePath;
	private static String fileSize;
	private static int maxBackupIndex;
	private static boolean isAsynchronous;
	private static LogTarget logTarget;

	public enum LogTarget
	{
		Console, File, Socket
	}

	public static void setFileName(String value)
	{
		fileName = value;
	}

	public static String getFileName()
	{
		return fileName;
	}
	……	
}

三 方法设计

  再来就是方法设计,它体现了一个类的内在或外向的行为方式,是与外界进行交互的窗口。按需求来说,我们需要一个方法给我们返回一个Logger对象,并使用Logger对象进行日志输出,需要明确的是,返回的Logger其属性配置因该从LogUtil的类级成员处取得,并且由方法参数来确定日志的输出目标,不同输出目标的Logger其属性配置亦不相同:

public static Logger getLogger(LogTarget target)
	{
		Logger logger = null;

		if (LogTarget.Console == target)
		{
			logger = getConsoleLogger();
		}
		else if (LogTarget.File == target)
		{
			logger = getFileLogger();
		}
		else if (LogTarget.Socket == target)
		{
			logger = getSocketLogger();
		}

		return logger;
	}

	private static Logger getSocketLogger()
	{
		// TODO Auto-generated method stub
		return null;
	}
	……

四 实现Logger对象实例化方法

  已经定义好了大体框架,接下来就是实现各种get***Logger()方法了,这里以输出到文件使用RollingFileAppender为例,做一个简单的实现样例,其他Logger对象的实现大同小异,仅在属性上有所区别:

	public static Logger getLogger(LogTarget target, String loggerName)
	{
		Logger logger = null;

		if (LogTarget.Console == target)
		{
			logger = getConsoleLogger(loggerName);
		}
		else if (LogTarget.File == target)
		{
			logger = getFileLogger(loggerName);
		}
		else if (LogTarget.Socket == target)
		{
			logger = getSocketLogger(loggerName);
		}

		return logger;
	}

	private static Logger getSocketLogger(String loggerName)
	{
		// TODO Auto-generated method stub
		return null;
	}

	private static Logger getFileLogger(String loggerName)
	{
		// 初始化一个RollingFileAppender对象
		RollingFileAppender appender = new RollingFileAppender();
		// 设置日志内容追加到文件内容末尾
		appender.setAppend(true);
		// 设置日志文件的存储位置
		appender.setFile(getFilePath() + File.separator + getFileName());
		// 不开启异步模式
		appender.setBufferedIO(false);
		// 仅开启异步模式,缓存大小才有意义
		appender.setBufferSize(0);
		// 下面的方法是对上面四个属性设置的一个封装
		// appender.setFile("", true, false, 0);
		// 需要激活Appender对象的配置,这样属性设置才会生效
		appender.activateOptions();
		// 注意这里需要是指Logger对象名,后续设计会对此处进行重构,目前以调用类的SimpleName作为Logger对象的name属性值
		Logger logger = Logger.getLogger(loggerName);
		// 为Logger对象添加Appender成员
		logger.addAppender(appender);
		// 设置Logger对象不继承上层节点属性配置,仅向文件中输出内容
		logger.setAdditivity(false);
		// 为什么设置日志输出级别为Trace,因为后续我们需要通过LogUtil公开的方法对日志级别进行动态控制,所以此处暂时设置为最低级别
		logger.setLevel(Level.TRACE);
		return logger;
	}
	……

  此时已经算是成功了一半了,应用程序通过LogUtil的getLogger()方法会得到一个可用的Logger对象,调用Logger对象提供的日志输出方法即可实现日志的输出。

  那么有人问我,我是怎么知道Logger、Appender有那些属性需要设置的?答:官方文档/源码查阅。这里我不贴官方文档了,用Eclipse给大家看看我是怎么知道这些属性设置方法的:

Appender属性设置方法概览

  如果需要查看更详细的说明,跟到源码的方法定义处,看看人家设计师当初是怎么对方法进行设计的即可,这依然是通用的、常规的操作(看源码很牛逼?瞎说八道)。

  最后再总结下,目前尚未完成的设计:

  1. 线程级日志对象控制
  2. 按处理线程动态设置日志输出界别
  3. 日志的异步输出模式

  这些内容会在后续的博客里逐步介绍。可能会有人觉得我很傻逼,写的罗里吧嗦,但请理解我的初衷,我只是想分享我当时做设计的时候,是如何一步步完成需求的,或许对于高手而言不值一提,但我更希望这里的一些思路能够对某些朋友有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬睡客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值