Slf4j+logback实现日志打印-获取调用者类及方法行数信息

为什么要用slf4j实现日志打印,在阿里的开发规范中有这么一段:

【强制】应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架
SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。
大概意思就是说 SLF4J 是一个日志抽象层,允许你使用任何一个日志系统,并且可以随时切换还不需要动到已经写好的程序。
但是我们在使用slf4j的时候一般会像下面这样:

public class MySlf4j {
     private static final Logger logger = LoggerFactory.getLogger("MySlf4j");
     public static void info(String msg) {
           logger.info(msg);
     }
}

在每个类里定义Logger,这就是重复的劳动,必须对logger进行封装,编写封装类需要注意,slf4j我们如果不做处理我们调用方法记录日志,像 类名、方法名、行数都变成Logger这个类的信息,这样就无法准确定位日志来源。这样的问题是无法忍受的。
所有我们对Mylf4j工具类做了如下修改:
1.通过Throwable获取堆栈信息。
2.通过SharedSecrets.getJavaLangAccess(),我们访问堆栈信息通过stackDepth定位到调用者类信息。
具体的代码如下:

public class MySlf4j {

	/**空数组*/
	private static final Object[] EMPTY_ARRAY = new Object[] {};
	/**全类名*/
	private static final String FQCN = MySlf4j.class.getName();

	/**
	 * 获取栈中类信息
	 * @param stackDepth 栈深(下标) 2:调用者类信息
	 * @return org.slf4j.spi.LocationAwareLogger
	 * @author wxt
	 * @date 2019/7/10 8:40
	 */
	private static LocationAwareLogger getLocationAwareLogger(final int stackDepth) {
		/**通过堆栈信息获取调用当前方法的类名和方法名*/
		JavaLangAccess access = SharedSecrets.getJavaLangAccess();
		Throwable throwable = new Throwable();
		StackTraceElement frame = access.getStackTraceElement(throwable, stackDepth);
		return (LocationAwareLogger) LoggerFactory.getLogger(frame.getClassName());
	}

	/**
	 * 封装Debug级别日志
	 * @param msg
	 * @param arguments
	 * 作者:wxt
	 * 日期:2019年3月27日下午5:38:01
	 */
	public static void textDebug(String msg, Object... arguments) {
		if (arguments != null && arguments.length > 0) {
			MessageFormat temp = new MessageFormat(msg);
			msg = temp.format(arguments);
		}
		getLocationAwareLogger(2).log(null, FQCN, LocationAwareLogger.DEBUG_INT, msg, EMPTY_ARRAY, null);
	}

	/**
	 * 封装Info级别日志
	 * @param msg
	 * @param arguments
	 * 作者:wxt
	 * 日期:2019年3月27日下午5:37:42
	 */
	public static void textInfo(String msg, Object... arguments) {
		if (arguments != null && arguments.length > 0) {
			MessageFormat temp = new MessageFormat(msg);
			msg = temp.format(arguments);
		}
		getLocationAwareLogger(2).log(null, FQCN, LocationAwareLogger.INFO_INT, msg, EMPTY_ARRAY, null);
	}

	/**
	 * 封装Warn级别日志
	 * @param msg
	 * @param arguments
	 * 作者:wxt
	 * 日期:2019年3月27日下午5:37:30
	 */
	public static void textWarn(String msg, Object... arguments) {
		if (arguments != null && arguments.length > 0) {
			MessageFormat temp = new MessageFormat(msg);
			msg = temp.format(arguments);
		}
		getLocationAwareLogger(2).log(null, FQCN, LocationAwareLogger.WARN_INT, msg, EMPTY_ARRAY, null);
	}

	/**
	 * 封装Error级别日志
	 * @param msg
	 * @param arguments
	 * 作者:wxt
	 * 日期:2019年3月27日下午5:37:14
	 */
	public static void textError(String msg, Object... arguments) {
		if (arguments != null && arguments.length > 0) {
			MessageFormat temp = new MessageFormat(msg);
			msg = temp.format(arguments);
		}
		getLocationAwareLogger(2).log(null, FQCN, LocationAwareLogger.ERROR_INT, msg, EMPTY_ARRAY, null);
	}

	/**
	 * 异常堆栈转字符串
	 * @param e
	 * @return
	 * 作者:wxt
	 * 日期:2019年3月27日下午5:37:08
	 */
	public static String ExceptionToString(Exception e) {
		StringWriter sw = null;
		PrintWriter pw = null;
		try {
			if (e == null) {
				return "无具体异常信息";
			}
			sw = new StringWriter();
			pw = new PrintWriter(sw);
			e.printStackTrace(pw);
			return sw.toString();
		} catch (Exception ex) {
			MySlf4j.textError("异常堆栈转字符串异常", ex);
			return "";
		} finally {
			sw.flush();
			pw.flush();
			pw.close();
		}

	}
}

loback.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>

<configuration scan="true">
	<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="APP_NAME" value="wxt-provider" />
    <property name="LOG_HOME" value="/usr/local/logs/${APP_NAME}" />
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [${APP_NAME}] [%thread] [%X{traceId}] %logger{50} - [%class : %method : %line] - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender" additivity="false">
		<File>${LOG_HOME}/${APP_NAME}.log</File>
		<encoder>
			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [${APP_NAME}] [%thread] [%X{traceId}] %logger{50} - [%class : %method : %line] - %msg%n</pattern>
		</encoder>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${LOG_HOME}/${APP_NAME}.%d{yyyy-MM-dd}.log</fileNamePattern>
		</rollingPolicy>
	</appender>

	<root level="INFO">
		<appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
	</root>
</configuration>

这样我们通过调用该工具类就可以打印出调用者的类相关信息。


欢迎关注公众号: 编码是个技术活

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值