JFinal 配置druid进行监控及打印sql语句

前一段时间看了一篇打印sql语句的分享,如下:

http://www.jfinal.com/share/324

该文章中使用额外的jar包进行sql语句打印,

<dependency>
    <groupId>com.googlecode.log4jdbc</groupId>
    <artifactId>log4jdbc</artifactId>
    <version>1.2</version>
</dependency>

但是因为日志框架的问题无法使用,谁知柳暗花明又一村,让我发现了druid,druid能做的事情更多,除了打印sql外还能监控,统计,文档地址如下:

https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98

本文整理了druid的sql语句打印,监控统计功能的配置

  1. sql语句打印

    druid使用了拦截过滤器模式,只要配置相应Filter的即可,代码如下

/**
 * @return 配置完善的DruidPlugin
 */
public static DruidPlugin createDruidPlugin() {
   // 从配置文件中读取数据库url,username,password
   String name = Const.devProfile.getName();
   String url = prop.get(name + ".db.url");
   String username = prop.get(name + ".db.username");
   String password = prop.get(name + ".db.password").trim();
   DruidPlugin dp = new DruidPlugin(url, username, password);
   // 添加数据库插件
   // 1.统计信息插件
   StatFilter statFilter = new StatFilter();
   statFilter.setMergeSql(true);
   statFilter.setLogSlowSql(true);
   // 慢查询目前设置为1s,随着优化一步步进行慢慢更改
   statFilter.setSlowSqlMillis(Duration.ofMillis(1000).toMillis());
   dp.addFilter(statFilter);
   // 2.日志插件
   // 保存DruidDataSource的监控记录,设置打印日志周期,默认使用DruidDataSourceStatLoggerImpl
   // DruidPlugin未暴露setTimeBetweenLogStatsMillis(),只能使用properties方法设置
   dp.setConnectionProperties("druid.timeBetweenLogStatsMillis="
         + Duration.ofHours(24).toMillis());
   Slf4jLogFilter slf4jLogFilter = new Slf4jLogFilter();
   slf4jLogFilter.setDataSourceLogEnabled(false);
   slf4jLogFilter.setConnectionLogEnabled(false);
   slf4jLogFilter.setConnectionLogErrorEnabled(true);
   slf4jLogFilter.setStatementLogEnabled(false);
   slf4jLogFilter.setStatementLogErrorEnabled(true);
   slf4jLogFilter.setResultSetLogEnabled(false);
   slf4jLogFilter.setResultSetLogErrorEnabled(true);
   slf4jLogFilter.setConnectionConnectBeforeLogEnabled(false);
   slf4jLogFilter.setConnectionConnectAfterLogEnabled(false);
   slf4jLogFilter.setConnectionCommitAfterLogEnabled(false);
   slf4jLogFilter.setConnectionRollbackAfterLogEnabled(true);
   slf4jLogFilter.setConnectionCloseAfterLogEnabled(false);
   slf4jLogFilter.setStatementCreateAfterLogEnabled(false);
   slf4jLogFilter.setStatementPrepareAfterLogEnabled(false);
   slf4jLogFilter.setStatementPrepareCallAfterLogEnabled(false);
   slf4jLogFilter.setStatementExecuteAfterLogEnabled(false);
   slf4jLogFilter.setStatementExecuteQueryAfterLogEnabled(false);
   slf4jLogFilter.setStatementExecuteUpdateAfterLogEnabled(false);
   slf4jLogFilter.setStatementExecuteBatchAfterLogEnabled(false);
   slf4jLogFilter.setStatementCloseAfterLogEnabled(false);
   slf4jLogFilter.setStatementParameterSetLogEnabled(false);
   slf4jLogFilter.setResultSetNextAfterLogEnabled(false);
   slf4jLogFilter.setResultSetOpenAfterLogEnabled(false);
   slf4jLogFilter.setResultSetCloseAfterLogEnabled(false);
   if (Const.devProfile == Const.DevProfile.LOCAL_DEV
         || Const.devProfile == Const.DevProfile.LOCAL_TEST) {
      slf4jLogFilter.setStatementExecutableSqlLogEnable(true);
   }
   dp.addFilter(slf4jLogFilter);
   // 3.防注入插件
   WallFilter wall = new WallFilter();
   wall.setDbType("mysql");
   dp.addFilter(wall);
   return dp;
}

配置了3个Filter,就是配置Slf4jLogFilter时需要注意哪些需要打印,哪些不需要

此外,还要在log4j2中配置日志过滤,我的Logger配置如下

<Loggers>
    <!--打印无用日志较多的设置level为info-->
    <!--<Logger name="net.sf.ehcache" level="WARN"/>-->
    <Logger name="org.apache.shiro.web.servlet.SimpleCookie" level="info"/>
    <Logger name="org.apache.shiro" level="info"/>

    <!--设置druid日志level为debug-->
    <Logger name="druid" level="debug" additivity="false">
        <AppenderRef ref="sql"/>
        <AppenderRef ref="Console"/>
    </Logger>

    <Root level="debug">
        <AppenderRef ref="Console"/>
        <AppenderRef ref="debug"/>
        <AppenderRef ref="info"/>
        <AppenderRef ref="warn"/>
        <AppenderRef ref="error"/>
    </Root>
</Loggers>


2.druid的监控功能

druid的监控功能是通过配置servlet和filter实现的,配置如下:

<!--配置Druid的WebStatFilter-->
<filter>
    <filter-name>DruidWebStatFilter</filter-name>
    <filter-class>com.app.vocalist.filter.druid.MyWebStatFilter</filter-class>
    <init-param>
        <param-name>exclusions</param-name>
        <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
    </init-param>
    <init-param>
        <!--开启session监控-->
        <param-name>sessionStatEnable</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <!--最大监控session数-->
        <param-name>sessionStatMaxCount</param-name>
        <param-value>2000</param-value>
    </init-param>
    <init-param>
        <!--principal在session中的属性名-->
        <param-name>principalSessionName</param-name>
        <param-value>user</param-value>
    </init-param>
    <init-param>
        <!--单条url分析-->
        <param-name>profileEnable</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<!--Druid内置监控配置-->
<servlet>
    <servlet-name>DruidStatView</servlet-name>
    <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
    <init-param>
        <!-- 用户名 -->
        <param-name>loginUsername</param-name>
        <param-value>root</param-value>
    </init-param>
    <init-param>
        <!-- 密码 -->
        <param-name>loginPassword</param-name>
        <param-value>root</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>DruidStatView</servlet-name>
    <url-pattern>/druid/*</url-pattern>
</servlet-mapping>

注意:

  1. com.app.vocalist.filter.druid.MyWebStatFilter是我写的继承com.alibaba.druid.support.http.WebStatFilter的一个类,重写了getPrincipal(httpRequest)

  2. JFinal框架是一个Filter,处理request后不会将其交给后续的servlet,而是直接返回(其实是使用了boolean[] isHandled = {false}作为标志,但是在ActionHandler中会将其设置为true,doFilter()中判断为true就直接返回了)

    可以在JFinal中设置com.jfinal.plugin.druid.DruidStatViewHandler进行处理,我所有的url格式为/api/*,所以将JFinal

<filter-mapping>
    <filter-name>jfinal</filter-name>
    <url-pattern>/api/*</url-pattern>
</filter-mapping>

即可

评论区

dadu

2017-11-24 00:19

如梦写的这个好像也可以。http://www.dreamlu.net/druid/jfinal/2017/09/22/Print-executable-SQL-using-the-Druid-LogFilter.html

pfjia

2017-11-26 14:08

@dadu 嗯嗯,他使用的是log4j打印,我使用的是slf4j,而且我把很多有默认值的打印选项也设置了下,没有他的那么简洁

a772856947

2017-12-22 11:01

@dadu 试了下 他那个好像也没有输出可执行的sql 了

a772856947

2017-12-22 11:10

@pfjia 还是木有弄好,就像个疙瘩 没有解决 很烦恼

pfjia

2017-12-22 19:01

@a772856947 你是用的log4j2框架吗?会提示什么错误吗?

a772856947

2017-12-27 10:04

没报错@pfjia 试的一楼说的如梦的 没报错 照常打印sql 不过没打印参数出来 可以看下你这个demo打印的效果是怎样的不 是用的log4j 不过没配置你说的Logger

pfjia

2017-12-28 14:24

@a772856947
控制台打印日志如下:
14:23:26.201 [http-nio-80-exec-8] DEBUG druid.sql.Statement - {conn-10020, pstmt-20055} executed. select *
from version
where available = 1
and unix_timestamp(`begin`) <= unix_timestamp(CURRENT_TIMESTAMP)
and unix_timestamp(`end`) > unix_timestamp(CURRENT_TIMESTAMP)
and os_type = 1
and version > '2.0.3'
order by gmt_create desc

a772856947

2018-01-03 09:55

@pfjia 方便加下qq么,请假下 (#^.^#)

fangdengfu

2018-01-08 11:15

请问:Const对象是哪个包里面的。。我没找到。。麻烦了。谢谢

pfjia

2018-01-18 15:29

@fangdengfu 你好,代码中出现的Const类虽然与com.jfinal.core.Const类名一致,但是是自己写的,仅为方便项目中区分不同的开发环境