1. 程式人生 > 資料庫 >【轉載】 利用p6spy攔截並檢視資料庫執行操作

【轉載】 利用p6spy攔截並檢視資料庫執行操作

https://blog.csdn.net/fanxiaobin577328725/article/details/71601760

一、簡介

  • 專案首 頁:
  • GitHub下載介紹頁面:
  • GitHub託管地址:
  • 幫助文件地址:
  • 幫助文件(pdf/epub)下載地址:
  • p6spy-3.0.0.zip下載地址:

  P6Spy is a framework that enables database data to be seamlessly(無縫地) intercepted(截獲) and logged with no code changes to existing application. The P6Spy distribution includes P6Log, an application which logs all JDBC transactions for any Java application.

  P6Spy 是針對資料庫訪問操作的動態監測框架(開源專案)它使得資料庫資料可無縫擷取和操縱,而不必對現有應用程式的程式碼作任何修改。P6Spy 分發包包括P6Log,它是一 個可記錄任何 Java 應用程式的所有JDBC事務的應用程式。其配置完成使用時,可以進行資料訪問效能的監測。
  我們最需要的功能,檢視sql語句,不是預編譯的帶問號的,而是真正的資料庫執行的sql,更直觀,更簡單。

  P6Spy是一個可以用來在應用程式中攔截和修改資料操作語句的開源框架。 通過P6Spy我們可以對SQL語句進行攔截,相當於一個SQL語句的記錄器,這樣我們可以用它來作相關的分析,比如效能分析。
P6SPY提供瞭如下幾個功能:

  • 記錄SQL語句的執行時間戳。
  • 記錄SQL語句型別
  • 記錄SQL填入引數的和沒有填入引數的SQL語句
  • 根據配置的時間控制SQL語句的執行時間,對超出時間的SQL語句輸出到日誌檔案中

  p6spy.(zip/tar.gz) - This is our distribution artifact containing everything that you need to use P6Spy. you would normally download this artifact if you are installing without any code changes.
  p6spy.(zip/tar.gz) -這個檔案包含了你使用P6Spy所需要的任何檔案。

  p6spy.jar - This is the primary artifact for p6spy. If you are integrating p6spy into your application, this is the only artifact that you need.
  p6spy.jar - 這個檔案是p6spy的核心檔案,如果你要將p6spy整合到你的應用中,你只需要這一個檔案就可以了。(當然還需要一個spy.properties配置檔案,我的理解就是依賴檔案只需要此jar包即可)

注意:以上兩個檔案在Maven倉庫中已經存在,所以可以直接從Maven倉庫中獲取到。

Maven的POM配置:

  1.   <!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
  2.   <dependency>
  3.   <groupId>p6spy</groupId>
  4.   <artifactId>p6spy</artifactId>
  5.   <version>3.0.0</version>
  6.   </dependency>

二、實戰

<1> 下載p6spy-3.0.0.zip檔案,下載地址為:

<2> 配置Maven依賴,如果不使用Maven可以直接將p6spy-3.0.0.zip檔案中的p6spy.jar新增到lib目錄中,然後Build Path -> Add To Buil Path

<3> 新增spy.properties到專案src的根目錄下,該檔案在p6spy-3.0.0.zip檔案中

<4> 在spy.properties檔案中配置真正的JDBC驅動的類名稱

driverlist=com.mysql.jdbc.Driver

<5> 修改真實的JDBC的URL和驅動類,如下:

  1.   driverClassName="com.p6spy.engine.spy.P6SpyDriver"
  2.   url="jdbc:p6spy:mysql://<hostname>:<port>/<database>"

<6> 配置SQL的輸出路徑(以輸出到控制檯為例),在spy.properties檔案中將appender=com.p6spy.engine.spy.appender.StdoutLogger前面的井號(#)去掉

<7> 正常執行程式,然後就可以看到預期的SQL語句了

三、配置檔案詳解

3.1 driverlist

  This is a comma separated list of JDBC driver classes to load and register with DriverManager. You should list the classname(s) of the JDBC driver(s) that you want to proxy with P6Spy if any of the following conditions are met.

  這個屬性配置的是載入和註冊DriverManager的真實的JDBC驅動類。你需要在此列出你想讓P6Spy 代理的JDBC驅動的類名,而且JDBC驅動必須滿足如下兩個條件中任意一個。(有疑問)

  1. The JDBC driver does not implement the JDBC 4.0 API
  2. You are using a JNDI Data Source - Some application servers will prevent the automatic registration feature from working.

3.2 autoflush

  For standard development, set the autoflush value to true. When set to true, every time a statement is intercepted(攔截), it is immediately(立即) written to the log file. In some cases,however, instant feedback(反應) on every statement is not a requirement.In those cases, the system performs(執行) slightly(稍微) faster with autoflush set to false.

3.3 dateformat

  Setting a value for dateformat changes the date format value printed in the log file. No value prints the current time in milliseconds (unix time), a useful feature for parsing the log. The date format engine is Java’s SimpleDateFormat class. Refer to the SimpleDateFormat class in the JavaDocs for information on setting this value. An example follows:

dateformat=MM-dd-yy HH:mm:ss:SS

注意:如果沒有指定此屬性,則預設輸出的是時間戳,也就是顯示最前面的那一串數字字串。對時間戳不瞭解的可以參考:《》

3.4 stacktrace

  If stacktrace is set, the log prints out the stack trace for each SQL statement logged.

3.5 stacktraceclass

  Limits the stack traces printed to those that contain the value set in stacktraceclass. For example, specifying stacktraceclass=com.mycompany.myclass limits the printing of stack traces to the specified class value. The stack trace is converted to a String and string.indexOf(stacktraceclass) is performed.

3.6 reloadproperties and reloadpropertiesinterval

  If reloadproperties is set to true, the property file is reloaded every n seconds, where n is defined by the value set by reloadpropertiesinterval. For example, if reloadproperties=true and reloadpropertiesinterval=10, the system checks the File.lastModified() property of the property file every 10 seconds, and if the file has been modified, it will be reloaded.
  If you set append=true, the log will be suddenly truncated when you change your properties. This is because using reloadproperties is intended to be the equivalent of restarting your application server. Restarting your application server truncates your log file.
  reloadproperties will not reload any driver information (such as realdriver, realdriver2, and realdriver3) and will not change the modules that are in memory.

3.7 appender

  Appenders allow you to specify(指定) where and how log information is output. Appenders are a flexible(靈活的) architecture(結構) allowing anyone to write their own output class for P6Spy. To use an appender, specify the classname of the appender to use. The current release comes with three options which are slf4j, stdout, and logging to a file (default). Please note, that all of these output in the CSV format (where separator is: “|”).

<1> Using the File output: Uncomment(取消註釋)  the FileLogger appender and specify a logfile and whether or not to append to the file or to clear the file each time:

  1.   #appender=com.p6spy.engine.spy.appender.Slf4JLogger
  2.   #appender=com.p6spy.engine.spy.appender.StdoutLogger
  3.   appender=com.p6spy.engine.spy.appender.FileLogger
  4.   # name of logfile to use, note Windows users should make sure to use forward slashes in their pathname (e:/test/spy.log)
  5.   # (used for com.p6spy.engine.spy.appender.FileLogger only)
  6.   # (default is spy.log)
  7.   #logfile = spy.log
  8.   # append to the p6spy log file. if this is set to false the
  9.   # log file is

注意:檔案的預設儲存路徑是專案的根目錄下,與src是平級的。
<2> Using StdOut: Uncomment the StdoutLogger as follows:

  1.   #appender=com.p6spy.engine.spy.appender.Slf4JLogger
  2.   appender=com.p6spy.engine.spy.appender.StdoutLogger
  3.   #appender=com.p6spy.engine.spy.appender.FileLogger

<3> Using SLF4J: Uncomment the Slf4JLogger as follows:

  1.   appender=com.p6spy.engine.spy.appender.Slf4JLogger
  2.   #appender=com.p6spy.engine.spy.appender.StdoutLogger
  3.   #appender=com.p6spy.engine.spy.appender.FileLogger

  In general you need to slf4j-api and the appropriate bridge to the actual logging implementation as well as the logging implementation itself on your classpath. To simplify setup for those not having any of the additional dependencies already on classpath following *-nodep.jar bundles are provided as part of p6spy distribution:

  • p6spy-<version>-log4j-nodep.jar - having log4j included
  • p6spy-<version>-log4j2-nodep.jar - having log4j2 included and
  • p6spy-<version>-logback-nodep.jar - having logback included.

 

Mapping to SLF4J levels is provided in the following way:
  Internally is Slf4j Logger is retrieved for the: p6spy, keep this in mind when configuring your logging implementation.So for example for the log4j following could be used to restrict the p6spy logging (if using xml-based configuration) to INFO level only:

  1.   <category name="p6spy">
  2.   <priority value="INFO" />
  3.   </category>

For further instructions on configuring SLF4J, see the 

3.8 logMessageFormat

  The log message format is selected by specifying(指定的) the class to use to format the log messages. The following classes are available with P6Spy.

com.p6spy.engine.spy.appender.SingleLineFormat which results in log messages in format:

current time|execution time|category|connection id|statement SQL String|effective SQL string

解析:當前時間(預設是時間戳)|執行時間|執行的操作型別|connection id|statement SQL String|真實執行的SQL字串

注意:你可能在看控制檯輸出的時候發現還是有問號呢?其實你看的是statement SQL String,再往後才是真實執行的SQL。

com.p6spy.engine.spy.appender.MultiLineFormat, which results in log messages in format:

  1.   current time|execution time|category|connection id|statement SQL String
  2.   effective SQL string

解析:這種格式從大體上與上面沒什麼區別,只是顯示的細節上有所不同,首先其行首會出現一個井號(#),執行時間前面會有一個took,並帶有ms單位,而且其真實執行的SQL會換行進行顯示,這一點也是我想要的。總的來說,我偏向喜歡這種格式,更加清晰明瞭。

  • current time - the current time is obtained through System.getCurrentTimeMillis() and represents the number of milliseconds that have passed since January 1, 1970 00:00:00.000 GMT. (Refer to the J2SE documentation for further details on System.getCurrentTimeMillis().) To change the format, use the dateformat property described in Common Property File Settings.
  • execution time - the time it takes in milliseconds for a particular method to execute. (This is not the total cost for the SQL statement.) For example, a statement SELECT * FROM MYTABLE WHERE THISCOL =? might be executed as a prepared statement, in which the .execute() function will be measured. This is recorded as the statement category. Further, as you call .next() on the ResultSet, each .next() call is recorded in the result category.
  • category - You can manage your log by including and excluding categories, which is described in Common Property File Settings.
  • connection id - Indicates the connection on which the activity was logged. The connection id is a sequentially generated identifier.
  • statement SQL string - This is the SQL string passed to the statement object. If it is a prepared statement,it is the prepared statement that existed prior to the parameters being set. To see the complete statement,refer to effective SQL string.
  • effective SQL string - If you are not using a prepared statement, this contains no value. Otherwise,it fills in the values of the Prepared Statement so you can see the effective SQL statement that is passed to the database. Of course, the database still sees the prepared statement, but this string is a convenient way to see the actual values being sent to the database.

  The com.p6spy.engine.spy.appender.MultiLineFormat might be better from a readability perspective.Because it will place the effective SQL statement on a separate line. However, the SingleLineFormat might be better if you have a need to parse the log messages. The default is com.p6spy.engine.spy.appender.SingleLineFormat for backward compatibility. 

  You can also supply your own log message formatter to customize the format. Simply create a class which implements the com.p6spy.engine.spy.appender.MessageFormattingStrategy interface and place it on the classpath.

3.9 filter, include, exclude

  P6Spy allows you to filter SQL queries by specific strings to be present (includes property value) or not present (excludes property value). As a precondition, setting filter=true has to be provided. P6Spy will perform string matching on each statement to determine if it should be written to the log file. include accepts a comma-delimited
list of expressions which is required to appear in a statement before it can appear in the log. exclude accepts a comma-delimited list to exclude. Exclusion overrides inclusion, so that a statement matching both an include string and an exclude string is excluded.

Please note that matching mode used in the underlying regex is (achieved via prefix (?mis)):

  • ,
  • and
  • .

  An example showing capture of all statements having select, except those having order follow:

  1.   filter = true
  2.   # comma separated list of strings to include
  3.   include = select
  4.   # comma separated list of strings to exclude
  5.   exclude = order

Please note, that internally following regex would be used for particular expression matching: (?mis)^(?!.*(order).*)(.*(select).*)$
  An example showing only capture statements having any of: order_details, price, and price_history follows:

  1.   filter = true
  2.   # comma separated list of strings to include
  3.   include = order,order_details,price,price_history
  4.   # comma separated list of strings to exclude
  5.   exclude =

Please note, that internally following regex would be used for particular expression matching: (?mis)^(.*(order|order_details|price|price_history).*)$
  An example showing the capture of all statements, except statements order string in them follows:

  1.   filter = false
  2.   # comma separated list of strings to include
  3.   include =
  4.   # comma separated list of strings to exclude
  5.   exclude = order

Please note, that internally following regex would be used for particular expression matching: (?mis)^(?!.*(order).*)(.*)$
  As you can use full regex syntax, capture of statements having: pri[cz]e follows:

  1.   filter = true
  2.   # comma separated list of strings to include
  3.   include = pri[cz]e
  4.   # comma separated list of strings to exclude
  5.   exclude =

Please note, that internally following regex would be used for particular expression matching: (?mis)^(.*(pri[cz]e).*)$
  Moreover, please note, that special characters escaping (used in java) has to be done for the provided regular expression.As an example, matching for:

from\scustomers

  would mean, that following should be specified (please note doubled backslash):

  1.   filter=true
  2.   include=from\\scustomers

3.10 filter, sqlexpression

  If you need more control over regular expression for matching, SQL string property sqlexpression is to be used as an alternative to exclude and include. An example follows:

  1.   filter = true
  2.   sqlexpression = your expression

  If your expression matches the SQL string, it is logged. If the expression does not match, it is not logged. Please note you can use sqlexpression together with include/exclude, where both would be evaluated.
  Moreover, please note, that special characters escaping (used in java) has to be done for the provided regular expression.As an example, matching for:

^(.*(from\scustomers).*)$

  would mean, that following should be specified (please note doubled backslash):

  1.   filter=true
  2.   sqlexpression=^(.*(from\\scustomers).*)$

3.11 excludecategories

  The log includes category information that describes the type of statement. This property excludes the listed categories.Valid options include the following:

  • error includes P6Spy errors. (It is recommended that you include this category.)
  • info includes driver startup information and property file information.
  • debug is only intended for use when you cannot get your driver to work properly, because it writes everything.
  • statement includes Statements, PreparedStatements, and CallableStatements.
  • batch includes calls made to the addBatch() JDBC API.
  • commit includes calls made to the commit() JDBC API.
  • rollback includes calls made to the rollback() JDBC API.
  • result includes statements generated by ResultSet.

  Enter a comma-delimited list of categories to exclude from your log file. See filter, include, exclude for more details on how this process works.

3.12 outagedetection

  This feature detects long-running statements that may be indicative of a database outage problem. When enabled, it logs any statement that surpasses the configurable time boundary during its execution. No other statements are logged except the long-running statements.

3.13 outagedetectioninterval

  The interval property is the boundary time set in seconds. For example, if set to 2, any statement requiring at least 2 seconds is logged. The same statement will continue to be logged for as long as it executes. So, if the interval is set to 2 and a query takes 11 seconds, it is logged 5 times (at the 2, 4, 6, 8, 10-second intervals).

3.14 jmxPrefix

  If set to true, the execution time will be measured in nanoseconds as opposed to milliseconds.
參考資料: