【Springboot】——整合p6spy格式化SQL日志

r囧r小猫 2022-04-17 02:14 1016阅读 0赞

项目现状介绍

  1. 项目使用Springboot+Mybatis作为基础框架,项目中日志框架采用logback+Slf4J,关于日志中SQL部分日志的打印采用的mybatis本身打印格式,sql语句正常输出,参数的位置使用占位符“?”替代。这样造成问题就是在处理项目中的sql问题,sql格式打印不规范,从sql日志查看,到sql日志运行判断都会花费一些重复的时间,既然这样那就想办法节省这一部分的时间吧。在Java项目中格式化sql日志,推荐使用辅助jar包,p6spy

何为p6spy?

  1. p6spy是针对数据库访问操作的动态监测框架(开源项目)它使得数据库数据可无缝截取和操纵,而不必对现有应用程序的代码作任何修改。P6Spy 分发包包括P6Log,它是一 个可记录任何 Java 应用程序的所有JDBC事务的应用程序。其配置完成使用时,可以进行数据访问性能的监测。

  p6spy是一个可以用来在应用程序中拦截和修改数据操作语句的开源框架。 通过p6spy我们可以对SQL语句进行拦截,相当于一个SQL语句的记录器,这样我们可以用它来作相关的分析,比如性能分析等我们最需要的功能,查看sql语句,不是预编译的带问号的,而是真正的数据库执行的sql,更直观,更简单。
p6spy相关材料的获取途径
项目首页:http://p6spy.github.io/p6spy/
GitHub下载介绍页面:https://github.com/p6spy/p6spy/wiki/Download
GitHub托管地址:https://github.com/p6spy/p6spy/tree/master
帮助文档地址:http://p6spy.readthedocs.io/en/latest/

Springboot+Mybatis整合p6spy

  1. #引入jar包
  2. <dependency>
  3. <groupId>p6spy</groupId>
  4. <artifactId>p6spy</artifactId>
  5. <version>3.7.0</version>
  6. </dependency>
  7. ###引入配置文件spy.properties
  8. ###
  9. # #%L
  10. # P6Spy
  11. # %%
  12. # Copyright (C) 2013 P6Spy
  13. # %%
  14. # Licensed under the Apache License, Version 2.0 (the "License");
  15. # you may not use this file except in compliance with the License.
  16. # You may obtain a copy of the License at
  17. #
  18. # http://www.apache.org/licenses/LICENSE-2.0
  19. #
  20. # Unless required by applicable law or agreed to in writing, software
  21. # distributed under the License is distributed on an "AS IS" BASIS,
  22. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  23. # See the License for the specific language governing permissions and
  24. # limitations under the License.
  25. # #L%
  26. ###
  27. #################################################################
  28. # P6Spy Options File #
  29. # See documentation for detailed instructions #
  30. # http://p6spy.github.io/p6spy/2.0/configandusage.html #
  31. #################################################################
  32. #################################################################
  33. # MODULES #
  34. # #
  35. # Module list adapts the modular functionality of P6Spy. #
  36. # Only modules listed are active. #
  37. # (default is com.p6spy.engine.logging.P6LogFactory and #
  38. # com.p6spy.engine.spy.P6SpyFactory) #
  39. # Please note that the core module (P6SpyFactory) can't be #
  40. # deactivated. #
  41. # Unlike the other properties, activation of the changes on #
  42. # this one requires reload. #
  43. #################################################################
  44. #modulelist=com.p6spy.engine.spy.P6SpyFactory,com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
  45. ################################################################
  46. # CORE (P6SPY) PROPERTIES #
  47. ################################################################
  48. # A comma separated list of JDBC drivers to load and register.
  49. # (default is empty)
  50. #
  51. # Note: This is normally only needed when using P6Spy in an
  52. # application server environment with a JNDI data source or when
  53. # using a JDBC driver that does not implement the JDBC 4.0 API
  54. # (specifically automatic registration).
  55. #driverlist=
  56. #####配置数据库驱动,根据自己的数据库选择
  57. driverlist=com.mysql.jdbc.Driver
  58. # for flushing per statement
  59. # (default is false)
  60. #autoflush = false
  61. # sets the date format using Java's SimpleDateFormat routine.
  62. # In case property is not set, miliseconds since 1.1.1970 (unix time) is used (default is empty)
  63. #dateformat=
  64. # prints a stack trace for every statement logged
  65. #stacktrace=false
  66. # if stacktrace=true, specifies the stack trace to print
  67. #stacktraceclass=
  68. # determines if property file should be reloaded
  69. # Please note: reload means forgetting all the previously set
  70. # settings (even those set during runtime - via JMX)
  71. # and starting with the clean table
  72. # (default is false)
  73. #reloadproperties=false
  74. reloadproperties=true
  75. # determines how often should be reloaded in seconds
  76. # (default is 60)
  77. #reloadpropertiesinterval=60
  78. # specifies the appender to use for logging
  79. # Please note: reload means forgetting all the previously set
  80. # settings (even those set during runtime - via JMX)
  81. # and starting with the clean table
  82. # (only the properties read from the configuration file)
  83. # (default is com.p6spy.engine.spy.appender.FileLogger)
  84. ###选择sql日志输出的问题 slf4jLogger输出到控制台,fileLogger输出到文件中
  85. appender=com.p6spy.engine.spy.appender.Slf4JLogger
  86. #appender=com.p6spy.engine.spy.appender.StdoutLogger
  87. #appender=com.p6spy.engine.spy.appender.FileLogger
  88. # name of logfile to use, note Windows users should make sure to use forward slashes in their pathname (e:/test/spy.log)
  89. # (used for com.p6spy.engine.spy.appender.FileLogger only)
  90. # (default is spy.log)
  91. ###如果选择输出到文件中自定义文件的输出路径
  92. logfile = /data/www/logs/spy.log
  93. # append to the p6spy log file. if this is set to false the
  94. # log file is truncated every time. (file logger only)
  95. # (default is true)
  96. #append=true
  97. # class to use for formatting log messages (default is: com.p6spy.engine.spy.appender.SingleLineFormat)
  98. #logMessageFormat=com.p6spy.engine.spy.appender.MultiLineFormat
  99. #自定义日志格式,在类中定义,因为原生的p6spy日志格式虽然取代了“?”但是全部输出到一行,可读性差,还是建议自定义一下
  100. logMessageFormat=com.xxx.xxxx.common.datasource.P6Spy
  101. #logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat
  102. # format that is used for logging of the date/time/... (has to be compatible with java.text.SimpleDateFormat)
  103. # (default is dd-MMM-yy)
  104. #databaseDialectDateFormat=dd-MMM-yy
  105. ##输出时间格式
  106. databaseDialectDateFormat=yyyy-MM-dd HH:mm:ss
  107. # whether to expose options via JMX or not
  108. # (default is true)
  109. #jmx=true
  110. # if exposing options via jmx (see option: jmx), what should be the prefix used?
  111. # jmx naming pattern constructed is: com.p6spy(.<jmxPrefix>)?:name=<optionsClassName>
  112. # please note, if there is already such a name in use it would be unregistered first (the last registered wins)
  113. # (default is none)
  114. #jmxPrefix=
  115. #################################################################
  116. # DataSource replacement #
  117. # #
  118. # Replace the real DataSource class in your application server #
  119. # configuration with the name com.p6spy.engine.spy.P6DataSource #
  120. # (that provides also connection pooling and xa support). #
  121. # then add the JNDI name and class name of the real #
  122. # DataSource here #
  123. # #
  124. # Values set in this item cannot be reloaded using the #
  125. # reloadproperties variable. Once it is loaded, it remains #
  126. # in memory until the application is restarted. #
  127. # #
  128. #################################################################
  129. #realdatasource=/RealMySqlDS
  130. #realdatasourceclass=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
  131. #################################################################
  132. # DataSource properties #
  133. # #
  134. # If you are using the DataSource support to intercept calls #
  135. # to a DataSource that requires properties for proper setup, #
  136. # define those properties here. Use name value pairs, separate #
  137. # the name and value with a semicolon, and separate the #
  138. # pairs with commas. #
  139. # #
  140. # The example shown here is for mysql #
  141. # #
  142. #################################################################
  143. #realdatasourceproperties=port;3306,serverName;myhost,databaseName;jbossdb,foo;bar
  144. #################################################################
  145. # JNDI DataSource lookup #
  146. # #
  147. # If you are using the DataSource support outside of an app #
  148. # server, you will probably need to define the JNDI Context #
  149. # environment. #
  150. # #
  151. # If the P6Spy code will be executing inside an app server then #
  152. # do not use these properties, and the DataSource lookup will #
  153. # use the naming context defined by the app server. #
  154. # #
  155. # The two standard elements of the naming environment are #
  156. # jndicontextfactory and jndicontextproviderurl. If you need #
  157. # additional elements, use the jndicontextcustom property. #
  158. # You can define multiple properties in jndicontextcustom, #
  159. # in name value pairs. Separate the name and value with a #
  160. # semicolon, and separate the pairs with commas. #
  161. # #
  162. # The example shown here is for a standalone program running on #
  163. # a machine that is also running JBoss, so the JDNI context #
  164. # is configured for JBoss (3.0.4). #
  165. # #
  166. # (by default all these are empty) #
  167. #################################################################
  168. #jndicontextfactory=org.jnp.interfaces.NamingContextFactory
  169. #jndicontextproviderurl=localhost:1099
  170. #jndicontextcustom=java.naming.factory.url.pkgs;org.jboss.nameing:org.jnp.interfaces
  171. #jndicontextfactory=com.ibm.websphere.naming.WsnInitialContextFactory
  172. #jndicontextproviderurl=iiop://localhost:900
  173. ################################################################
  174. # P6 LOGGING SPECIFIC PROPERTIES #
  175. ################################################################
  176. # filter what is logged
  177. # please note this is a precondition for usage of: include/exclude/sqlexpression
  178. # (default is false)
  179. #filter=true
  180. # comma separated list of strings to include
  181. # please note that special characters escaping (used in java) has to be done for the provided regular expression
  182. # (default is empty)
  183. #include=select
  184. # comma separated list of strings to exclude
  185. # (default is empty)
  186. #exclude =
  187. # sql expression to evaluate if using regex
  188. # please note that special characters escaping (used in java) has to be done for the provided regular expression
  189. # (default is empty)
  190. #sqlexpression =
  191. #list of categories to exclude: error, info, batch, debug, statement,
  192. #commit, rollback and result are valid values
  193. # (default is info,debug,result,resultset,batch)
  194. #excludecategories=info,debug,result,resultset,batch
  195. #excludecategories=error,info,debug,result,resultset
  196. #excludecategories=info
  197. # Execution threshold applies to the standard logging of P6Spy.
  198. # While the standard logging logs out every statement
  199. # regardless of its execution time, this feature puts a time
  200. # condition on that logging. Only statements that have taken
  201. # longer than the time specified (in milliseconds) will be
  202. # logged. This way it is possible to see only statements that
  203. # have exceeded some high water mark.
  204. # This time is reloadable.
  205. #
  206. # executionThreshold=integer time (milliseconds)
  207. # (default is 0)
  208. #executionThreshold=
  209. ################################################################
  210. # P6 OUTAGE SPECIFIC PROPERTIES #
  211. ################################################################
  212. # Outage Detection
  213. #
  214. # This feature detects long-running statements that may be indicative of
  215. # a database outage problem. If this feature is turned on, it will log any
  216. # statement that surpasses the configurable time boundary during its execution.
  217. # When this feature is enabled, no other statements are logged except the long
  218. # running statements. The interval property is the boundary time set in seconds.
  219. # For example, if this is set to 2, then any statement requiring at least 2
  220. # seconds will be logged. Note that the same statement will continue to be logged
  221. # for as long as it executes. So if the interval is set to 2, and the query takes
  222. # 11 seconds, it will be logged 5 times (at the 2, 4, 6, 8, 10 second intervals).
  223. #
  224. # outagedetection=true|false
  225. # outagedetectioninterval=integer time (seconds)
  226. #
  227. # (default is false)
  228. #outagedetection=false
  229. # (default is 60)
  230. #outagedetectioninterval=30

自定义日志输出格式代码

  1. import com.p6spy.engine.spy.appender.MessageFormattingStrategy;
  2. import java.text.SimpleDateFormat;
  3. import java.util.Date;
  4. public class P6Spy implements MessageFormattingStrategy {
  5. private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
  6. @Override
  7. public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql) {
  8. return !"".equals(sql.trim()) ? this.format.format(new Date()) + " | took " + elapsed + "ms | " + category + " | connection " + connectionId + "\n " + sql + ";" : "";
  9. }
  10. }

修改数据源,因为项目中配置了动态链接数据源,所以在代码中将返回Datasource的地方替换成P6Datasource

  1. public DataSource createDataSource(DataSourceConfig dataSourceConfig, Map<String, Object> propertyMap) {
  2. Class<? extends DataSource> dataSourceType;
  3. try {
  4. dataSourceType = (Class<? extends DataSource>) Class.forName((String) "org.apache.tomcat.jdbc.pool.DataSource");
  5. DataSourceBuilder factory = DataSourceBuilder.create().driverClassName("com.mysql.jdbc.Driver").url(dataSourceConfig.getUrl())
  6. .username(dataSourceConfig.getUserName()).password(dataSourceConfig.getPassWord()).type(dataSourceType);
  7. ConversionService conversionService = new DefaultConversionService();
  8. DataSource dataSource = factory.build();
  9. RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
  10. dataBinder.setConversionService(conversionService);
  11. dataBinder.setIgnoreNestedProperties(false);//false
  12. dataBinder.setIgnoreInvalidFields(false);//false
  13. dataBinder.setIgnoreUnknownFields(true);//true
  14. PropertyValues dataSourcePropertyValues = new MutablePropertyValues(propertyMap);
  15. dataBinder.bind(dataSourcePropertyValues);
  16. return new P6DataSource(dataSource);
  17. } catch (ClassNotFoundException e) {
  18. LOGGER.error("创建数据源失败", e);
  19. throw new RuntimeException(e);
  20. }
  21. }

添加前的SQL,添加之后的SQL日志
在这里插入图片描述
在这里插入图片描述
注意⚠️
p6spy本身配合slf4j+log4j使用,项目中需要整合logback,目前小编的项目还有一点不完美就是,sql.log日志文件不能自动自动切割,如果一定要利用logback来完成,p6spy打印的所有日志,输出到main函数所在的目录下。如果一定希望能自动切割,那就多关注一些main函数所在的包的日志切割情况。

总结

  1. 之前自己对技术的应用大部分停留到应用的层面,很片面的认为很少有机会去根据项目的需要改动现有框架的源码,所以会用就Ok了。但是这次在项目中整合p6spy,因为日志框架的问题,再加上对p6spy深入认知不够,还尚未完美的将p6spy+logback整合到一起。也让我反思对技术为什么要追求更高层次上的认知!

发表评论

表情:
评论列表 (有 0 条评论,1016人围观)

还没有评论,来说两句吧...

相关阅读

    相关 使用P6Spy格式化SQL语句

    公司最近在做一个项目,开发过程中遇到点小问题,查询结果出不来,查看自带日志打印,有点不爽,特别是想复制sql语句到数据库navicat中去调试时,还要手动复制参数值,很是麻烦,

    相关 P6Spy

     P6Spy是一个可以用来在应用程序中拦截和修改数据操作语句的开源框架。通过P6Spy我们可以对SQL语句进行拦截,相当于一个SQL语句的记录器,这样我们可以用它来作相关的分析