zoukankan      html  css  js  c++  java
  • 自己写的日志框架--linkinLog4j--框架可配置+提性能


    OK,上一篇博客我们已经实现了日志框架的基本的功能,但是还有一个最大的问题就是日志输出地不能重定向,然后一些输出也不可控。那现在我们来实现一个比较完整的日志框架。


    设计思路如下:

    1,定义一堆常量LinkinLog4jConstants,这些常量用于框架中日志输出的配置项,为了简单和方便,这里不一定非要依赖配置文件,所以在这些常量里面都要付初始值。

    2,新增配置文件,LinkinLog4j.propertites。这些配置文件中可以配置上面常量项的任何一项用来覆盖框架默认。

    3,框架核心类,LinkinLog4j新增shortName,ThreadName属性,用于日志输出类名简称,线程名字。

    4,LinkinLog4j定义几个新的静态常量,在static块中统一初始化。比较重要的是:要有一个输出流,用来重定向Appender,要有一个日志+等级映射的map,用来控制各种子类的等级

    5,重写log(),按照配置文件中的各种配置来控制日志某些内容的输出与不输出。

    6,框架辅助类,LinkinLog4jHelper。框架日志工厂类,LinkinLog4jFactory,用来生成单例LinkinLog4j。


    OK,现在我先贴出代码,然后一边自己写个测试来试一下这个框架看下效果。我自测通过。

    框架核心类,LinkinLog4j:

    package linkinframe.linkinLog4j;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintStream;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Map;
    import java.util.Objects;
    import java.util.Properties;
    import java.util.TreeMap;
    
    public class LinkinLog4j
    {
    	private static final String PROP_LOG_PREFIX; // 配置文件key值前缀
    	private static boolean SHOW_LOG_NAME; // 是否输出类名全名旗标
    	private static boolean SHOW_SHORT_NAME; // 是否输出类名简称旗标
    	private static boolean SHOW_THREAD_NAME; // 是否输出线程名旗标
    	private static boolean SHOW_DATE_INFO; // 是否输出时间旗标
    	private static boolean FLUSH; // 是否刷新输出流旗标
    	private static final Map<String, LinkinLogLevel> LEVELDESCMAP; // 所有等级枚举 key值数字
    	private static final Map<Integer, LinkinLogLevel> LEVELSTATUSMAP; // 所有等级枚举 key值英语
    	private static Map<String, Integer> CONFIGLEVELS; // 封装配置文件中的每个类的日志级别
    	private static SimpleDateFormat DATEFORMAT; // 时间格式化器
    	private static int ROOT_LEVEL; // 默认等级,如果没有配置就使用该等级
    	private static PrintStream OUT; // 输出流
    
    	static
    	{
    		LEVELDESCMAP = LinkinLogLevel.getLevelDescMap();
    		LEVELSTATUSMAP = LinkinLogLevel.getLevelStatusMap();
    		InputStream in = LinkinLog4jHelper.getConfigInputStream(LinkinLog4jConstants.PROP_NAME);
    		Properties props = new Properties();
    		if (in != null)
    		{
    			try
    			{
    				props.load(in);
    				in.close();
    			}
    			catch (IOException e)
    			{
    			}
    		}
    		// 添加系统的所有的常量
    		props.putAll(System.getProperties());
    		PROP_LOG_PREFIX = LinkinLog4jConstants.PROP_LOG_PREFIX;
    		SHOW_LOG_NAME = LinkinLog4jHelper.getBooleanProperty(props, LinkinLog4jConstants.SHOW_LOG_NAME, LinkinLog4jConstants.DEFAULT_SHOW_LOG_NAME);
    		SHOW_SHORT_NAME = LinkinLog4jHelper.getBooleanProperty(props, LinkinLog4jConstants.SHOW_SHORT_NAME, LinkinLog4jConstants.DEFAULT_SHOW_SHORT_NAME);
    		SHOW_THREAD_NAME = LinkinLog4jHelper.getBooleanProperty(props, LinkinLog4jConstants.SHOW_THREAD_NAME, LinkinLog4jConstants.DEFAULT_SHOW_THREAD_NAME);
    		SHOW_DATE_INFO = LinkinLog4jHelper.getBooleanProperty(props, LinkinLog4jConstants.SHOW_DATE_INFO, LinkinLog4jConstants.DEFAULT_SHOW_DATE_INFO);
    		FLUSH = LinkinLog4jHelper.getBooleanProperty(props, LinkinLog4jConstants.FLUSH, LinkinLog4jConstants.DEFAULT_FLUSH);
    		ROOT_LEVEL = toIntegerLevel(LinkinLog4jHelper.getProperty(props, LinkinLog4jConstants.ROOT_LEVEL, LinkinLog4jConstants.DEFAULT_LEVEL));
    		// 初始化配置文件中的所有的日志文件+等级
    		CONFIGLEVELS = parseConfigLevels(props);
    		// 初始化时间格式。
    		String dateFormatStr = LinkinLog4jHelper.getProperty(props, LinkinLog4jConstants.DATE_FORMAT, LinkinLog4jConstants.DEFAULT_DATA_FORMAT);
    		DATEFORMAT = new SimpleDateFormat(dateFormatStr);
    		// 初始化输出流,如果没有找见则重定向为控制台输出
    		String logFile = LinkinLog4jHelper.getProperty(props, LinkinLog4jConstants.LOG_FILE, LinkinLog4jConstants.DEFAULT_LOG_FILE);
    		boolean append = LinkinLog4jHelper.getBooleanProperty(props, LinkinLog4jConstants.LOG_FILE_APPEND, LinkinLog4jConstants.DEFAULT_LOG_FILE_APPEND);
    		OUT = getPrintStream(logFile, append);
    
    		// jvm中增加一个关闭的钩子
    		Runtime runtime = Runtime.getRuntime();
    		runtime.addShutdownHook(new Thread()
    		{
    			@Override
    			public void run()
    			{
    				try
    				{
    					shutdown();
    				}
    				catch (Exception e)
    				{
    					System.err.println("框架shutdown出错!!!");
    					e.printStackTrace(System.err);
    				}
    			}
    
    			private void shutdown()
    			{
    			}
    
    		});
    	}
    
    	private final String name; // 每个日志文件的名称
    	private String shortName; // 日志文件简称
    	private String threadName; // 线程名字
    	private final int level; // 每个日志文件的等级
    
    	public LinkinLog4j(String name)
    	{
    		this.name = name;
    		this.level = getLogLevel(name);
    	}
    
    	/**
    	 * @创建时间: 2016年2月24日
    	 * @相关参数: @param props
    	 * @相关参数: @return
    	 * @功能描述: 封装日志名+日志等级进一个map
    	 */
    	private static Map<String, Integer> parseConfigLevels(Properties props)
    	{
    		Map<String, Integer> map = new TreeMap<String, Integer>();
    		for (String key : props.stringPropertyNames())
    		{
    			if (key != null && key.startsWith(PROP_LOG_PREFIX))
    			{
    				String logLevelValue = props.getProperty(key);
    				String logName = parseLogName(key);
    				map.put(logName, toIntegerLevel(logLevelValue));
    			}
    		}
    		return map;
    	}
    
    	/**
    	 * @创建时间: 2016年2月24日
    	 * @相关参数: @param logNameKey
    	 * @相关参数: @return
    	 * @功能描述: 获取日志名称,配置文件中去掉配置前缀
    	 */
    	private static String parseLogName(String logNameKey)
    	{
    		return logNameKey.substring(PROP_LOG_PREFIX.length());
    	}
    
    	/**
    	 * @创建时间: 2016年2月24日
    	 * @相关参数: @param property
    	 * @相关参数: @return
    	 * @功能描述: 通过desc获取等级枚举中的status
    	 */
    	private static int toIntegerLevel(String desc)
    	{
    		return LEVELDESCMAP.get(desc).getStatus();
    	}
    
    	/**
    	 * @创建时间: 2016年2月24日
    	 * @相关参数: @param logFile
    	 * @相关参数: @param append
    	 * @相关参数: @return
    	 * @功能描述: 获取输出地appender
    	 */
    	private static PrintStream getPrintStream(String logFile, boolean append)
    	{
    		PrintStream out = null;
    		try
    		{
    			LinkinLog4jHelper.createNewFileIfNotExists(logFile);
    			out = new PrintStream(new FileOutputStream(logFile, append));
    		}
    		catch (Exception e)
    		{
    			System.err.println("未找到输出日志路径,默认使用控制台输出!");
    			return System.out;
    		}
    		return out;
    	}
    
    	/**
    	 * @创建时间: 2016年2月24日
    	 * @相关参数: @param logName
    	 * @相关参数: @return
    	 * @功能描述: 获取日志类的输出日志等级
    	 */
    	private int getLogLevel(String logName)
    	{
    		// 1、如果没有配置,使用框架默认等级
    		if (CONFIGLEVELS == null || CONFIGLEVELS.isEmpty())
    		{
    			return ROOT_LEVEL;
    		}
    		// 2、如果配置了,使用配置文件中的等级
    		int logLevel = -1;
    		// {level=10000, level.test.junit4test=2147483647}
    		// test.junit4test.LinkinLog4jTest
    		for (String name : CONFIGLEVELS.keySet())
    		{
    			if (logName.startsWith(name))
    			{
    				logLevel = CONFIGLEVELS.get(name);
    			}
    		}
    		if (logLevel == -1)
    		{
    			logLevel = ROOT_LEVEL;
    		}
    		return logLevel;
    	}
    
    	/***************** 定义一系列输出日志的方法 ***********************************/
    	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
    	 * @功能描述: 核心日志方法,输出日志内容到appender
    	 * <p>
    	 * 判断日志类定义的日志级别,控制一些日志方法的执行和不执行
    	 * 依次将日志的信息一步一步的添加到StringBuilder中然后输出
    	 * </p>
    	 */
    	private void log(int level, String message, Throwable cause)
    	{
    		if (isLevelEnabled(level))
    		{
    			return;
    		}
    		StringBuilder builder = new StringBuilder(128);
    		appendDateInfo2Log(builder);
    		appendLogName2Log(builder, name);
    		appendThreadName2Log(builder);
    		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(LEVELSTATUSMAP.get(level).getDesc()).append("]").append(" ");
    	}
    
    	/**
    	 * @创建时间: 2016年2月24日
    	 * @相关参数: @param builder
    	 * @功能描述: 追加时间信息,取值当前时间
    	 * 注意:这里要加锁
    	 */
    	private void appendDateInfo2Log(StringBuilder builder)
    	{
    		if (SHOW_DATE_INFO)
    		{
    			Date date = new Date();
    			String dateStr = "";
    			synchronized (DATEFORMAT)
    			{
    				dateStr = DATEFORMAT.format(date);
    			}
    			builder.append(dateStr).append(LinkinLog4jConstants.LOG_SEPARATOR);
    		}
    	}
    
    	/**
    	 * @创建时间: 2016年2月24日
    	 * @相关参数: @param builder
    	 * @相关参数: @param name
    	 * @功能描述: 追加日志名字
    	 */
    	private void appendLogName2Log(StringBuilder builder, String name)
    	{
    		if (SHOW_SHORT_NAME)
    		{
    			builder.append(getShortName(name));
    		}
    		else if (SHOW_LOG_NAME)
    		{
    			builder.append(name);
    		}
    		builder.append(LinkinLog4jConstants.LOG_SEPARATOR);
    	}
    
    	private void appendThreadName2Log(StringBuilder builder)
    	{
    		if (SHOW_THREAD_NAME)
    		{
    			builder.append(getThreadName());
    		}
    		builder.append(LinkinLog4jConstants.LOG_SEPARATOR);
    	}
    
    	/**
    	 * @创建时间: 2016年2月24日
    	 * @相关参数: @param name
    	 * @相关参数: @return
    	 * @功能描述: 根据类名获取简称
    	 * <p>
    	 * linkin.package.className→className
    	 * </p>
    	 */
    	protected Object getShortName(String name)
    	{
    		if (shortName == null)
    		{
    			if (name == null)
    			{
    				shortName = "null";
    				return shortName;
    			}
    			int idx = name.lastIndexOf(".");
    			if (idx < 0)
    			{
    				shortName = name;
    			}
    			else
    			{
    				shortName = name.substring(idx + 1);
    			}
    		}
    		return shortName;
    	}
    
    	/**
    	 * @创建时间: 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 synchronized void writeLog(StringBuilder str)
    	{
    		OUT.println(str.toString());
    		if (FLUSH)
    		{
    			OUT.flush();
    		}
    	}
    
    	/**
    	 * @创建时间: 2016年2月24日
    	 * @相关参数: @return
    	 * @功能描述: 获取当前线程
    	 */
    	public synchronized String getThreadName()
    	{
    		if (threadName == null)
    		{
    			threadName = Thread.currentThread().getName();
    		}
    		return threadName;
    	}
    
    }
    

    框架常量类,LinkinLog4jConstants:

    package linkinframe.linkinLog4j;
    
    /**
     * @创建作者: LinkinPark
     * @创建时间: 2016年2月24日
     * @功能描述: 框架的默认值,配置文件中相关配置覆盖这份默认值
     */
    public class LinkinLog4jConstants
    {
    	// 配置文件名称
    	public static final String PROP_NAME = "linkinLog4j.properties";
    	// 配置文件key值前缀
    	public static final String PROP_PREFIX = "linkinLog4j.";
    	// 配置日志文件名字+等级的映射
    	public static final String PROP_LOG_PREFIX = PROP_PREFIX + "log.level.";
    	// 默认时间格式化器
    	public static final String DATE_FORMAT = PROP_PREFIX + "dateFormat";
    	public static final String DEFAULT_DATA_FORMAT = "HH:mm:ss";
    	// 默认日志级别
    	public static final String ROOT_LEVEL = PROP_PREFIX + "root.level";
    	public static final String DEFAULT_LEVEL = LinkinLogLevel.ALL.getDesc();
    	// 默认显示日志日志所在类名简称
    	public static final String SHOW_LOG_NAME = PROP_PREFIX + "showLogName";
    	public static boolean DEFAULT_SHOW_LOG_NAME = true;
    	// 默认显示线程
    	public static final String SHOW_THREAD_NAME = PROP_PREFIX + "showThreadName";
    	public static boolean DEFAULT_SHOW_THREAD_NAME = true;
    	// 默认不显示日志所在类名全称
    	public static final String SHOW_SHORT_NAME = PROP_PREFIX + "showShortName";
    	public static boolean DEFAULT_SHOW_SHORT_NAME = false;
    	// 默认不显示时间
    	public static final String SHOW_DATE_INFO = PROP_PREFIX + "showDateInfo";
    	public static boolean DEFAULT_SHOW_DATE_INFO = true;
    	// 重定向日志输出路径
    	public static final String LOG_FILE = PROP_PREFIX + "logFile";
    	public static final String DEFAULT_LOG_FILE = null;
    	// 默认日志刷缓冲
    	public static final String FLUSH = PROP_PREFIX + "logFile.flush";
    	public static boolean DEFAULT_FLUSH = true;
    	// 是否追加日志到一个文件尾部
    	public static final String LOG_FILE_APPEND = PROP_PREFIX + "logFile.append";
    	public static final boolean DEFAULT_LOG_FILE_APPEND = true;
    	// 日志的分割符
    	public static final String LOG_SEPARATOR = "-";
    
    }
    

    框架日志等级枚举,LinkinLogLevel:

    package linkinframe.linkinLog4j;
    
    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;
    	}
    
    	/*************** 提供2个map,分别封装所有的枚举 ***************/
    	public static Map<String, LinkinLogLevel> getLevelDescMap()
    	{
    		Map<String, LinkinLogLevel> levelMap = new HashMap<>(5, 1);
    		LinkinLogLevel[] values = LinkinLogLevel.values();
    		for (LinkinLogLevel linkinLogLevel : values)
    		{
    			levelMap.put(linkinLogLevel.getDesc(), linkinLogLevel);
    		}
    		return levelMap;
    	}
    
    	public static Map<Integer, LinkinLogLevel> getLevelStatusMap()
    	{
    		Map<Integer, LinkinLogLevel> levelMap = new HashMap<>(5, 1);
    		LinkinLogLevel[] values = LinkinLogLevel.values();
    		for (LinkinLogLevel linkinLogLevel : values)
    		{
    			levelMap.put(linkinLogLevel.getStatus(), linkinLogLevel);
    		}
    		return levelMap;
    	}
    
    }
    

    框架帮助类,LinkinLog4jHelper类:

    package linkinframe.linkinLog4j;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;
    
    /**
     * @创建作者: LinkinPark
     * @创建时间: 2016年2月24日
     * @功能描述: 工具类
     */
    public class LinkinLog4jHelper
    {
    
    	public static String getProperty(Properties props, String key)
    	{
    		return getProperty(props, key, "");
    	}
    
    	public static String getProperty(Properties props, String key, String defaultValue)
    	{
    		return props.getProperty(key, defaultValue);
    	}
    
    	public static boolean getBooleanProperty(Properties props, String key)
    	{
    		return getBooleanProperty(props, key, false);
    	}
    
    	public static boolean getBooleanProperty(Properties props, String key, boolean defaultValue)
    	{
    		String property = props.getProperty(key);
    		if (property == null || property == "" || property == "null")
    		{
    			return defaultValue;
    		}
    		return new Boolean(property).booleanValue();
    	}
    
    	public static InputStream getConfigInputStream(String configName)
    	{
    		ClassLoader classLoader = getContextClassLoader();
    		InputStream in = classLoader.getResourceAsStream(configName);
    		if (in == null)
    		{
    			in = LinkinLog4j.class.getClassLoader().getResourceAsStream(configName);
    		}
    		if (in == null)
    		{
    			in = LinkinLog4j.class.getResourceAsStream(configName);
    		}
    		return in;
    	}
    
    	public static ClassLoader getContextClassLoader()
    	{
    		return Thread.currentThread().getContextClassLoader();
    	}
    
    	/**
    	 * @创建时间: 2016年2月25日
    	 * @相关参数: @param logFile
    	 * @功能描述: 文件不存在,则创建一个
    	 */
    	public static void createNewFileIfNotExists(String logFile)
    	{
    		File file = new File(logFile);
    		File file1 = new File(file.getParent());
    		if (!file.exists())
    		{
    			file1.mkdirs();
    			try
    			{
    				file.createNewFile();
    			}
    			catch (IOException e)
    			{
    				System.err.println("创建日志出错!!!");
    			}
    		}
    	}
    
    }
    

    框架工厂类,LinkinLog4jFactory:

    package linkinframe.linkinLog4j;
    
    import java.util.Hashtable;
    
    public class LinkinLog4jFactory
    {
    	// 字符串包装key值,加快hashTable查找效率
    	static Hashtable<CategoryKey, Object> hashTable = new Hashtable<>(10, 1);
    
    	public static LinkinLog4j getLogger(Class<?> klass)
    	{
    		return getLogger(klass.getName());
    	}
    
    	public static LinkinLog4j getLogger(String name)
    	{
    		CategoryKey key = new CategoryKey(name);
    		LinkinLog4j logger;
    		synchronized (hashTable)
    		{
    			Object o = hashTable.get(key);
    			if (o == null)
    			{
    				logger = makeNewLoggerInstance(name);
    				hashTable.put(key, logger);
    				return logger;
    			}
    			else if (o instanceof LinkinLog4j)
    			{
    				return (LinkinLog4j) o;
    			}
    			else
    			{
    				return null;
    			}
    		}
    	}
    
    	public static LinkinLog4j makeNewLoggerInstance(String name)
    	{
    		return new LinkinLog4j(name);
    	}
    
    }
    

    还有一个key值封装类,该类用来封装一个字符串加一个hashCode值的缓冲,然后重写hashCode方法,加快工厂效率。

    package linkinframe.linkinLog4j;
    
    /**
     * @创建作者: LinkinPark
     * @创建时间: 2016年2月23日
     * @功能描述: 字符串包装,加快hashTable查找效率
     */
    public class CategoryKey
    {
    	String name;
    	int hashCache;
    
    	CategoryKey(String name)
    	{
    		this.name = name;
    		hashCache = name.hashCode();
    	}
    
    	final public int hashCode()
    	{
    		return hashCache;
    	}
    
    	final public boolean equals(Object rArg)
    	{
    		if (this == rArg)
    		{
    			return true;
    		}
    
    		if (rArg != null && CategoryKey.class == rArg.getClass())
    		{
    			return name.equals(((CategoryKey) rArg).name);
    		}
    		else
    		{
    			return false;
    		}
    	}
    }
    

    OK,上面的代码写完了,我们写测试之前,先提供一份完整的linkinLog4j.propertites配置文件。

    #################################
    #该配置文件可省,框架默认向控制台输出日志#
    ##[ALL→DEBUG→INFO→ERROR→OFF]###
    #################################
    #是否输出类的简称,只有一个类名。默认false
    linkinLog4j.showShortName=false
    #是否输出线程名字,默认true
    #linkinLog4j.showThreadName=false
    #是否输出时间信息,取值当前时间,默认true
    linkinLog4j.showDateInfo=true
    #时间格式化器,默认HH:mm:ss
    linkinLog4j.dateFormat=yyyy-MM-dd HH:mm:ss
    #配置全局日志级别,默认ALL
    linkinLog4j.root.level=DEBUG
    #配置日志重定向文件appender,默认控制台
    linkinLog4j.logFile=huhu/log/linkinLog4j.log
    #是否追加日志到文件尾部,默认true
    linkinLog4j.logFile.append=false
    #是否刷新日志在输出流中的缓冲,默认true
    linkinLog4j.logFile.flush=true
    #################################
    #支持配置包,配置类,覆盖root默认,覆盖包#
    #################################
    #linkinLog4j.log.level.test.junit4test=DEBUG
    linkinLog4j.log.level.test.junit4test.LinkinLog4jTest1=INFO
    #################################
    #TODO:多个线程在日志中线程名字一样,我去
    #################################


    当然上面的配置文件可以省略,这一套日志框架都有赋初始值的,如果没有配置该配置文件,则使用控制台做appender,然后按照默认约定格式来输出默认内容。典型的我们来写几个测试,看下输出情况。

    1,不用配置文件,直接使用框架默认打印日志。直接修改常量,使得框架找不到该配置文件就OK。

    测试类代码如下:

    package test.junit4test;
    
    import org.junit.Test;
    
    import linkinframe.linkinLog4j.LinkinLog4j;
    import linkinframe.linkinLog4j.LinkinLog4jFactory;
    
    public class LinkinLog4jTest
    {
    	LinkinLog4j log = LinkinLog4jFactory.getLogger(LinkinLog4jTest.class);
    
    	@Test
    	public void testLog()
    	{
    		log.debug("debug()。。。");
    		log.info("info()。。。");
    		log.error("error()。。。");
    	}
    
    }
    
    OK,没有问题。junit控制台绿条,然后控制台输出结果如下:

    未找到输出日志路径,默认使用控制台输出!
    10:08:41-test.junit4test.LinkinLog4jTest-main-[DEBUG] debug()。。。
    10:08:41-test.junit4test.LinkinLog4jTest-main-[INFO] info()。。。
    10:08:41-test.junit4test.LinkinLog4jTest-main-[ERROR] error()。。。
    

    2,使用配置文件,设置一些常量来控制日志的输出,包括重定向日志文件,包括输出内容等等。

    OK,没有问题,junit绿条,然后日志内容被重定向了我们制定的路径文件下。



    3,重新设置子类文件的日志级别,可以使用包配置,也可以使用类名配置。我们现在修改linkinLog4j.propertites文件,定义日志等级:

    #################################
    #支持配置包,配置类,覆盖root默认,覆盖包#
    #################################
    #linkinLog4j.log.level.test.junit4test=DEBUG
    linkinLog4j.log.level.test.junit4test.LinkinLog4jTest=INFO

    运行上面的测试我们打开huhu/log/linkinLog4j.log,日志内容重新刷新且只输出了INFO级别以上的日志:

    2016-02-25 10:12:56-test.junit4test.LinkinLog4jTest-main-[INFO] info()。。。
    2016-02-25 10:12:56-test.junit4test.LinkinLog4jTest-main-[ERROR] error()。。。
    



    总结:

    虽然日志功能在应用程序开发中是一个非常重要的部件,有些时候日志信息的好坏可以直接影响程序开发的进度。

    但是日志本身不涉及到任何业务逻辑,因而需要尽量减少它的侵入性,也就说它提供的接口应该尽量的简单。为了实现接口的简单性,其中一种方法就是使用配置文件记录LinkinLog4j的配置信息,LinkinLog4j则根据配置信息初始化每一个日志核心实例。

    这些配置信息包括:

    1,是否显示日志名称、时间信息;如果显示日志打印时间,其格式如何;

    2,默认的日志级别是什么,默认的日志追加方式是什么,如果没有配置文件要给与一种约定。

    3,支持单独配置一些日志名称的日志级别;可以覆盖包,可以覆盖类,实现文件日志配置继承。

    4,如果将日志打印到日志文件,则日志文件的名称和目录在哪里等信息。


    OK,现在日志框架基本可以使用了,功能也比较完善了,这里核心代码我有借鉴Log4j源码,剩下的一些不足就是一些异常捕获情况,一些性能上面的提升,一些功能和方法的抽象,比如我们这里有输出地Appender,日志格式化器Layout。我们都应该抽象成接口或者抽象类,然后衍生多个子类来嫁入我们的框架输出日志。框架没有完美,我们阅读源码,最大的用处就是看懂它的设计,来吸收一些好的技巧和设计模式。接下来,我会写几篇关于Log4j源码解析的博客。先这样吧。


  • 相关阅读:
    批量修改图片尺寸
    批量修改文件名
    C++ 字符串的编码
    Hanoi问题
    农夫过河问题
    遍历文件夹中所有图片
    仿射变换和透射变换
    程序局部性原理
    14年年底的学习计划
    linux之Vim使用
  • 原文地址:https://www.cnblogs.com/LinkinPark/p/5232844.html
Copyright © 2011-2022 走看看