前一篇博文也介绍了如何打印sql语句,这个打印sql语句的开关一般用日志级别的方式可以配置的,所以动态调整日志的级别就变得有意义了,不仅仅sql日志可以开启,我们可以对所有日志的级别更改,便于线上找出问题所在。
本博文参考了spring boot的做法https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/htmlsingle/#production-ready-logger-configuration,利用JMX对log级别动态调整,为了方便接口的调用,引入了Jolokia,而且spring boot 也支持这个框架的https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/htmlsingle/#production-ready-jolokia,如果你使用的是spring boot,可以不用继续阅读博文,博文对与spring boot 和非spring boot做了区别。
根据前一篇文章,为了调整打印sql的日志级别,我们需要一个MXBean:
package com.sdcuike.spring.jmx;
import ch.qos.logback.classic.Level;
import org.jolokia.jmx.JsonMBean;
import org.springframework.stereotype.Component;
/**
* @author sdcuike
* @date 2018/8/4
* @since 2018/8/4
*/
@Component
@JsonMBean
public class LogSqlMXBean implements ILogSqlMXBean {
private volatile boolean logSql = false;
private static final String LOGSQL_LOGGERNAME = "com.sdcuike.spring.log.db";
@Override
public boolean isLogSql() {
return logSql;
}
@Override
public void setLogSql(boolean logSql) {
this.logSql = logSql;
if (logSql) {
LoggerLevelUtils.setLogLevel(LOGSQL_LOGGERNAME, Level.DEBUG);
} else {
LoggerLevelUtils.setLogLevel(LOGSQL_LOGGERNAME, Level.INFO);
}
}
}
其接口:
package com.sdcuike.spring.jmx;
/**
* @author sdcuike
* @date 2018/8/4
* @since 2018/8/4
*/
public interface ILogSqlMXBean {
void setLogSql(boolean b);
boolean isLogSql();
}
这个MXBean主要对打印sql的log级别做调整,日志级别调整工具类:
package com.sdcuike.spring.jmx;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.impl.StaticLoggerBinder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.security.CodeSource;
import java.security.ProtectionDomain;
/**
* @author sdcuike
* @date 2018/8/4
* @since 2018/8/4
*/
class LoggerLevelUtils {
private static final String ROOT_LOGGER_NAME = "ROOT";
private LoggerLevelUtils() {
}
/**
* 动态设置log级别
*
* 代码来源于:https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LoggersEndpoint.java
*
* @param loggerName
* @param level
*/
static void setLogLevel(String loggerName, Level level) {
ch.qos.logback.classic.Logger logger = getLogger(loggerName);
if (logger != null) {
logger.setLevel(level);
}
}
private static ch.qos.logback.classic.Logger getLogger(String name) {
LoggerContext factory = getLoggerContext();
if (StringUtils.isEmpty(name) || ROOT_LOGGER_NAME.equals(name)) {
name = Logger.ROOT_LOGGER_NAME;
}
return factory.getLogger(name);
}
private static LoggerContext getLoggerContext() {
ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
Assert.isInstanceOf(LoggerContext.class, factory,
String.format(
"LoggerFactory is not a Logback LoggerContext but Logback is on "
+ "the classpath. Either remove Logback or the competing "
+ "implementation (%s loaded from %s). If you are using "
+ "WebLogic you will need to add 'org.slf4j' to "
+ "prefer-application-packages in WEB-INF/weblogic.xml",
factory.getClass(), getLocation(factory)));
return (LoggerContext) factory;
}
private static Object getLocation(ILoggerFactory factory) {
try {
ProtectionDomain protectionDomain = factory.getClass().getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
if (codeSource != null) {
return codeSource.getLocation();
}
} catch (SecurityException ex) {
// Unable to determine location
}
return "unknown location";
}
}
代码来源于spring boot,日志框架支持对其设置级别的喔(和版本有关系吗,目前没调研)。
现在配置一下jolokia,暴露JMX:
整个代码见:https://github.com/sdcuike/all_learning_201806/tree/blog_2018-08-05/spring-recipes/src/main/java/com/sdcuike/spring。
暴露了JMX,我们启动一下spring boot,测试一下:
curl -X POST \
http://localhost:8788/jolokia/ \
-H 'Cache-Control: no-cache' \
-H 'Postman-Token: c6d2266b-24aa-4d8e-933f-2226d16a38eb' \
-d '{
"type" : "search",
"mbean" : "*:*"
}'
返回的内容看看是不是有我们的MXBean:
现在我们查询一下打印sql的log级别:
curl -X POST \
http://localhost:8788/jolokia/ \
-H 'Cache-Control: no-cache' \
-H 'Postman-Token: 56b13fc4-907f-49e1-90b4-e8b6db8aed96' \
-d '{
"type" : "read",
"mbean" : "com.sdcuike.spring.jmx:name=logSqlMXBean,type=LogSqlMXBean",
"attribute" : "LogSql"
}'
把日志级别调整到debug:
curl -X POST \
http://localhost:8788/jolokia/ \
-H 'Cache-Control: no-cache' \
-H 'Postman-Token: d5a96f61-d531-4910-8574-a0137c58280c' \
-d '{
"type" : "write",
"mbean" : "com.sdcuike.spring.jmx:name=logSqlMXBean,type=LogSqlMXBean",
"attribute" : "LogSql",
"value":"true"
}'
查询一下是不是我们的日志级别变了,而且测试sql打印成功。
如果是非spring boot应该,而是一般的应用而且使用了spring框架,可以看一下测试用例,描述了如何配置:
https://github.com/sdcuike/all_learning_201806/blob/blog_2018-08-05/spring-recipes/src/test/java/com/sdcuike/spring/JMXTest.java
。这里就不再复述了。