使用 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;
	}
}


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
28天前
|
设计模式 算法 安全
Java (3)
Java (3)
10 0
|
3月前
|
机器学习/深度学习 Java
L1-6 福到了(Java)
L1-6 福到了(Java)
33 0
|
8月前
|
IDE 小程序 Java
初识Java 2
初识Java
40 0
|
10月前
|
Java
Java之“珠穆朗玛峰”
Java之“珠穆朗玛峰”
50 0
|
编解码 监控 负载均衡
JAVA问答7
JAVA问答7
88 0
|
Java
R7-24 369寝室(JAVA)
R7-24 369寝室(JAVA)
219 0
|
开发框架 安全 网络协议
java me
Java 是一门面向对象编程语言,不仅吸收了 C++ 语言的各种优点,还摒弃了 C++ 里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。 Java 语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。 01 Java EE 是指 Java Enterprise Edition,Java 企业版,多用于企业级开发,包括 web 开发等等。也叫 J2EE。 Java SE 通常是指 Java Standard Edition,Java 标准版,就是一般 Java 程序的开发就可以(如桌面程序),可以看作
168 0
|
前端开发 Java 测试技术
关于 Java 18 你想知道的一切(中)
关于 Java 18 你想知道的一切(中)
关于 Java 18 你想知道的一切(中)
|
Java
Java - 9 个小技巧让你的 if else 看起来更优雅(二)
Java - 9 个小技巧让你的 if else 看起来更优雅(二)
188 0
|
Java 程序员 C++
java是什么
java是什么
142 0