开发者社区> sp42> 正文

使用 slf4j + Java.util.logger

简介: 使用 slf4j 实际上我对日志服务的要求不高,大多数情况下能够打印信息就可以了,例如 ActionScript 的 trace(); 这么的基本的函数我就觉得足够了,包括在网页调试中 alert() 大法更是屡试不爽。
+关注继续查看

使用 slf4j

实际上我对日志服务的要求不高,大多数情况下能够打印信息就可以了,例如 ActionScript 的 trace(); 这么的基本的函数我就觉得足够了,包括在网页调试中 alert() 大法更是屡试不爽。好了,来到 Java 世界,仍沿用这一思想,所以 sysout 快捷键下生成的 System.out/err.println("xxx"); 也没觉得有什么障碍。但长久以来 sysout 总感觉不妥,尤其运维的兄弟看到你这一堆打印的东东,而他又不是十分明白那是啥就有意见了,可想而知更严重的是,对你的程序的质量也会从心里觉得不太靠谱。于是综上所述,搞一个妥妥的日志系统还是有必要的,可以让随时关掉输出在生产服务器上,或者保存到文件。最开始我的初衷是 java.util.Logger 已经够用,后来渐渐接触一些开源使用了 slf4j 和 apache common logging,而不说 logback 的那些了,开源世界就是丰富多彩啊,单纯一个日志系统都可以搞那么多。再看看 slf4j 及其文档资料,好一个详细,被分解得如此精巧细致。

使用 slf4j,简单的 Hello world:

import com.ajaxjs.framework.model.BaseModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Catalog extends BaseModel {
	public static void main(String[] args) {
		Logger logger = LoggerFactory.getLogger(Catalog.class);
		logger.info("Hello World");
	}

}

调用 logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol); 等于调用 if (logger.isDebugEnabled()) {     logger.debug("Processing trade with id: " + id + " symbol: " + symbol); },非常方便。

对于配搭 Java.util.logger,slf4j 还需要一个 JAR 包:slf4j-jdk14-1.7.17.jar。

配置 Java.util.logger

看完 slf4j 之后,我想,能不能自己实现一个简单的?嗯这就是造轮子,——我手痒了。

首先,Java 自带的日志系统结构是这样的,我们先了解一下。

我扩展的 LogHelper 主要提供 log() 和 warning() 这两个方法,而且还重载了以方便实现 info("Processing trade with id: {} and symbol : {} ", id, symbol) 多个参数的传入。

完整的代码如下:

package com.ajaxjs.util;

import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.logging.FileHandler;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 * 自定义日志工具类
 * @author frank
 *
 */
public class LogHelper {
	/**
	 * 
	 * @param clazz
	 */
	public LogHelper(Class<?> clazz) {
		packageName = clazz.getPackage().getName();
		logger = Logger.getLogger(packageName);
		logger.addHandler(initFileHandler());
		logger.setFilter(filter);
	}
	
	// 所在的包名
	private String packageName;
	
	// 包装这个 logger
	private Logger logger;

	// 缓存
	private static Map<String, LogHelper> cache = new HashMap<>();

	/**
	 * 打印一个日志
	 * 
	 * @param msg
	 *            日志信息
	 */
	public void log(String msg) {
		logger.logp(Level.INFO, packageName, getMethodName(), msg);
	}

	/**
	 * 打印一个日志
	 * 
	 * @param tpl
	 *            信息语句之模板
	 * @param params
	 *            信息参数
	 */
	public void log(String tpl, Object... params) {
		logger.logp(Level.INFO, packageName, getMethodName(), tpl, params);

	}

	/**
	 * 打印一个日志(警告级别)
	 * 
	 * @param msg
	 *            警告信息
	 */
	public void warning(String msg) {
		logger.logp(Level.WARNING, packageName, getMethodName(), msg);
	}

	/**
	 * 打印一个日志(警告级别)
	 * 
	 * @param tpl
	 *            信息语句之模板
	 * @param params
	 *            信息参数
	 */
	public void warning(String tpl, Object... params) {
		logger.logp(Level.WARNING, packageName, getMethodName(), tpl, params);

	}

	/**
	 * 记录一个异常
	 * 
	 * @param ex
	 */
	public void warning(Throwable ex) {
		logger.logp(Level.WARNING, packageName, getMethodName(), ex.getMessage(), ex);
	}

	/**
	 * 获取所在的方法,调用时候
	 * 
	 * @return
	 */
	private String getMethodName() {
		StackTraceElement[] st = Thread.currentThread().getStackTrace();
		StackTraceElement frame = st[2];
		String method = String.format((Locale) null, "%s(%s:%s)", frame.getMethodName(), frame.getFileName(),
				frame.getLineNumber());

		return method;
	}

	/**
	 * 获取自定义的 logger
	 * 
	 * @param clazz
	 * @return
	 */
	public static LogHelper getLog(Class<?> clazz) {
		String key = clazz == null ? "root" : clazz.getPackage().getName();

		if (cache.containsKey(key)) {
			return cache.get(key);
		} else {
			LogHelper logger = new LogHelper(clazz);
			cache.put(key, logger);
			return logger;
		}
	}

	private static int max_size = 1;
	private static int max_number = 10;
	private static String logFilePath = "c:\\temp\\log%g.log";

	/**
	 * 日志格式
	 */
	private final static Formatter myFormatter = new Formatter() {
		private static final String tpl = "\n%s %s : %s.%s  %s";

		@Override
		public String format(LogRecord record) {
			return String.format(tpl, DateTools.now(), record.getLevel(), record.getLoggerName(),
					record.getSourceMethodName(), record.getMessage());
		}
	};

	// 过滤器,是否要日志服务
	private final static Filter filter = new Filter() {

		@Override
		public boolean isLoggable(LogRecord record) {
			return record.getMessage().contains("no log") ? false : true;
		}

	};

	/**
	 * 初始化保存到磁盤的處理器
	 */
	private Handler initFileHandler() {
		FileHandler logHandler = null;

		try {
			logHandler = new FileHandler(logFilePath, 1024 * max_size, max_number, true);
		} catch (IOException e) {
			logger.severe("日志文件路径" + logFilePath + "错误,请检查!");
			e.printStackTrace();
		}
		logHandler.setLevel(Level.WARNING);
		logHandler.setFormatter(myFormatter);

		return logHandler;
	}

	/**
	 * 控制台支持字符串 format
	 * 
	 * @param msg
	 *            插入的内容
	 */
	public static void sysConsole(String msg) {
		System.console().format("%S", msg);
	}
}

实际上 Java 自带的日志系统不算弱的了,首先提供了若干 Level,用于描述日志信息的级别;其次, handler 是把日志信息如果保存和显示的对象,包括持久化;如果要保存的话那是什么格式的呢?这便是 Formatter 考虑的问题;再则,如果我想某些日志不保存,可不可以呢?可以呀,那就是 Filter 涉及的问题。老外的这篇文章说得不错:http://examples.javacodegeeks.com/core-java/util/logging/java-util-logging-example/

调用方法:

public class Node {
	private static final com.ajaxjs.util.LogHelper LOGGER = com.ajaxjs.util.LogHelper.getLog(Node.class);
        ……
}

这种是不用导入包名。

封装这个类的时候,遇到一个难点就是重写 warning()/info() 方法之后不能调出上下文堆栈,不能报告具体哪个方法调用。后来查阅相关文档,才知道是调出

StackTraceElement[] st = Thread.currentThread().getStackTrace();

线程的 StackTace 即可。


修正 console 超链接不能点击的问题,少了一个 .,修改后如下

/**
 * 获取所在的方法,调用时候
 * 
 * @return
 */
private String getMethodName() {
	StackTraceElement frame = null;
	// get thread by class name
	for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
        if (ste.getClassName().equals(packageName)) {
        	// 会有两个?
        	frame = ste;
            break;
        }
    }
	
	if(frame != null) {// 超链接,跳到源码所在行数
		return String.format(".%s(%s:%s)%n",  frame.getMethodName(), frame.getFileName(), frame.getLineNumber());
	}else{
		return null;
	}
}


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
java.lang.NoClassDefFoundError:org/apache/commons/logging/LogFactory
java.lang.NoClassDefFoundError:org/apache/commons/logging/LogFactory
12 0
java.lang.NoSuchMethodError: org.springframework.core.ResolvableType.clearCache
java.lang.NoSuchMethodError: org.springframework.core.ResolvableType.clearCache
92 0
java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing
java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing
69 0
【java】java自带的java.util.logging.Logger日志功能
偶然翻阅到一篇文章,注意到Java自带的Logger日志功能,特地来细细的看一看,记录一下。 1.Java自带的日志功能,默认的配置   ①Logger的默认配置,位置在JRE安装目录下lib中的logging.
2919 0
Java_异常_02_java.lang.NoClassDefFoundError: org/apache/log4j/Level
总结:解析Json时,除了要导入json-lib-2.2-jdk15.jar外,还要导入: commons-beanutils.jar, commons-httpclient.jar, commons-lang.
1115 0
Java_异常_03_ java.lang.NoClassDefFoundError: org/apache/commons/pool/KeyedObjectPoolFactory
异常信息: java.lang.NoClassDefFoundError: org/apache/commons/pool/KeyedObjectPoolFactory 原因: 我用的是commons-pool2,而此处报错明显是找不到commons-pool,换成commons-pool-1.6即可。
1214 0
java.lang.NoClassDefFoundError: org/jboss/logging/BasicLogger
原因:缺少jboss-logging的jar包 解决方法:添加jboss-logging.jar到lib下
1367 0
+关注
sp42
移动项目技术负责人。多年全栈经验,熟悉 Java 和 JS,CSDN 博客技术专家,著有《ExtJS 详解与实践》等书。
文章
问答
文章排行榜
最热
最新
相关电子书
更多
OpenCrypto Unchaining the Java
立即下载
Java Your(Next)
立即下载
低代码开发师(初级)实战教程
立即下载