動機
我們可能會因為以下幾個原因需要將信息、錯誤和調試信息寫入日志文件中:
測試、分析並驗證業務邏輯。
跟蹤並查看詳細的錯誤消息。
通過動態打開詳細日志或關閉日志來解決在產品環境中碰到的業務邏輯 bug。
通過檢查日志語句的執行時間來查明存儲過程的性能問題。
我們可以對存儲過程使用這個日志框架來滿足以下需求:
在產品環境中動態打開或關閉全局或可選擇的日志功能。
對日志文件進行歸檔,將當前日志文件維護到一個可管理的大小。
為每個存儲過程和每個 DB2 代理確定日志語句。
為每個 DB2 代理分隔或合並日志文件。
確保日志文件本身不是資源敏感的。
整體架構和設計
DB2 有自己的日志方法,可以將消息存放到 db2diag.log 文件中。DB2 還提供了一些工具來捕獲並存放詳細的跟蹤信息,這樣 DB2 開發人員就可以對所報告的問題進行隔離和解決。本文中給出的日志框架提供了將信息和調試消息捕獲到文件中進行分析的能力,這樣存儲過程的開發人員就可以對這些問題進行隔離和解決。
由於 DB2 存儲過程可以由不同的 DB2 代理並行執行,因此記錄日志的存儲過程的每個實例都必須讀取所有的日志控制信息,並與其他過程一起共享這些信息。為了實現這個目的,我們使用了共享內存,每個日志過程實例都可以訪問這些共享內存中的信息。
如何配置日志功能
要為某個存儲過程配置日志功能,就需要使用存儲過程 DB2.UPDATE_SP_CONFIG。我們可以使用這個過程來啟動、停止和控制記錄日志的功能,並設置共享內存中的參數,這樣,所有正在執行的存儲過程都可以使用相同的日志來控制信息。
為了啟用或禁用用於每個存儲過程的日志,或同時啟用或禁用用於所有存儲過程的日志,可以將這些信息保存到一個配置文件中,並在首次調用日志存儲過程時將其加載到共享內存中。在更改日志配置參數時,共享內存中的信息就會獲得更新,同時還會保存到配置文件中。(注意:這非常類似於 DB2 配置參數的更改,其中對某些參數的更改可以立即生效,有些則會等到實例或數據庫重新啟動之後才生效。在這個日志框架中,對存儲過程的日志參數進行的任何更改都會立即生效)。
這個日志框架的一個目標是可以保守地使用系統資源,這樣日志過程調用所耗費的資源都不會太多,或者不會產生很大負載。這個框架的設計目標是可以被上千個存儲過程使用,而不會對性能產生太大影響。
對於每個日志調用,都需要確定這條消息是否將發送到日志文件中。我們需要根據日志標記名使用的散列值對具有鏈表 (linked list) 的散列表進行查詢。對於那些共享相同散列值的名稱來說,我們在相同的關鍵字級別上維護了一個鏈表,以方便查找相同的日志標記名的配置參數。使用這種具有鏈表的散列表可以提供最快速的查詢功能。
如果對所有的存儲過程都啟用完整日志,那麼日志文件本身可能會增長的非常快。通過對日志文件進行歸檔,可以將日志文件移動到另外一個位置進行分析、存儲或刪除。
如何啟動日志
要在 DB2 的存儲過程中啟用日志功能,首先要使用兩個參數調用 DB2.OPEN_LOG。第一個參數是日志標記名,第二個是一個句柄。為了盡量簡單,可以使用存儲過程名作為日志標記名,也可以對一組實現特定業務功能的存儲過程使用一個通用的日志標記。例如,如果有一個嵌套過程,而該過程又調用另外 6 個 DB2 存儲過程來獲取最佳實現,那麼可以對這 7 個 DB2 存儲過程使用標記名 GBQR。DB2.OPEN_LOG 中的第二個參數是一個 CHAR 類型的句柄(48 個字節)。該句柄由兩個 C 結構組成。其中一個包含標記名的共享內存的位置及其日志操作,第二個結構包含調用 DB2.OPEN_LOG 時的時間戳。
如何記錄消息
日志框架為記錄消息提供了另外兩個存儲過程:DB2.LOGINFO 和 DB2.LOGGER。每個存儲過程都需要兩個參數。第一個參數是從 DB2.OPEN_LOG 中獲得的日志句柄,第二個是要記錄的消息。如果共享內存中存在這個標記,則 DB2.LOGINFO 會記錄一條信息消息。如果調試參數被設置為 Y,並且在共享內存中存在一個日志標記,那麼 DB2.LOGGER 就會記錄一條調試信息。
通常,我們會使用 LOGINFO 來記錄詳細錯誤和其他信息消息,並會使用 LOGGER 將調試信息截獲到日志文件中。當不想將調試消息記錄到日志文件中時,將這個日志標記的調試參數設置成 N 即可。要設置這個參數,可以使用存儲過程 DB2.UPDATE_SP_CONFIG,該過程用來實現日志管理的工作。如果希望徹底關閉 LOGINFO 消息,那麼通過將調試參數設置成 R 來永久刪除這個標記,就可以將這個日志標記從共享內存中刪除。
如何關閉日志
在這個存儲過程的末尾,可以通過調用 DB2.CLOSE_LOG 來關閉日志。CLOSE_LOG 過程需要使用日志句柄作為輸入參數。CLOSE_LOG 會在最後向日志中寫入最後一條消息,內容是從啟動日志到結束日志經過了多少時間。CLOSE_LOG 調用對於確定使用的執行時間非常有用。
日志框架的引用指南
日志標記的解釋
第一個 DB2.OPEN_LOG 調用的功能是從共享內存中保存的日志標記來構建一個如 圖 1 所示的鏈表散列表。對於每個從共享內存或初次調用使用的配置文件中讀取的日志標記,OPEN_LOG 都會使用標記名和散列表的大小來生成一個散列值。對於大小為 10 的散列表來說,get_quote、find_zip_code、verify_zip 和 put_culls 標記會共享同一個散列值 3。這個鏈表散列表包含有關共享內存日志標記指針的信息。共享內存的位置存儲在句柄中,從 OPEN_LOG 到後續的 LOGGER 和 LOGINFO 調用都會傳遞這個句柄。
圖 1. 鏈表散列表
圖片看不清楚?請點擊這裡查看原圖(大圖)。
這些日志方法都可以在 SQL-PL 存儲過程或其他外部存儲過程(COBOL、Java™ 等)中使用。如果正在使用用 C 語言編寫的外部存儲過程,那麼既可以使用日志存儲過程,也可以直接使用日志調用,後者可以跳過存儲過程。
日志存儲過程框架包含兩個部分。第一個部分是 日志管理,用來獲取或設置配置參數。第二個部分是 日志存儲過程,可以在 SQL-PL 或任何其他外部存儲過程中使用。
日志管理
用來進行日志管理的存儲過程有 3 個。DB2.UPDATE_SP_CONFIG 存儲過程用來設置並存儲日志配置參數。DB2.GET_SP_CONFIG 存儲過程將列出日志參數及其值。第三個存儲過程是 DB2.GET_SP_LOG,它可以打印日志文件的內容。
更新存儲過程的配置
DB2.UPDATE_SP_CONFIG 是用來設置配置文件和共享內存中某個標記名所使用的日志管理存儲過程。通常,我們會在命令行中使用 CLP 或 DB2 命令編輯器來執行這個存儲過程,以實現對日志參數的更新。
CALL DB2.UPDATE_SP_CONFIG(log_token_name, value);
log_token_name —— 日志標記名。此框架使用了兩類日志標記:全局標記和用於單個或一組存儲過程的私有標記。
全局標記 —— 這個框架使用了兩個全局標記。
GLOBAL_LOGGING —— 該值默認設置為 Y。在將這個參數設置為 N 時,LOGINFO 和 LOGGER 所使用的所有日志行為就會全部停止。利用這個參數,可以在運行時關閉所有存儲過程的日志。
LOG_SPLIT —— 該默認設置為 N。當將 LOG_SPLIT 設置為 N 時,日志框架就會將所有的語句全部記錄到日志文件 sp.log 中。當多個不同的 DB2 代理同時執行某個存儲過程時,可以在一個日志文件中看到所有的日志語句,但是可以通過尋找每個 DB2 代理所使用的惟一應用程序 ID 號來區分每個日志語句。當所有語句都被記錄到一個文件中時,就可以使用 grep 對這個文件進行過濾,從而查看某個特定 DB2 代理所記錄的語句。在將這個參數設置為 Y 時,日志文件就會對每個 DB2 代理進行分隔,每個文件的名稱都是以這個 DB2 代理的應用程序 ID 開頭的。
私有標記 —— 單個存儲過程或一組存儲過程可以采用某個標記名,來說明這個標記需要輸出日志。
使用 DB2.UPDATE_SP_CONFIG 存儲過程更新日志標記:
要關閉全局日志:
call DB2.UPDATE_SP_CONFIG('GLOBAL_LOGGING','N');
要啟用全局日志:
call DB2.UPDATE_SP_CONFIG('GLOBAL_LOGGING','Y');
要啟用日志文件分隔功能:
call DB2.UPDATE_SP_CONFIG('LOG_SPLIT','Y');
注意:當啟用日志分隔功能時,日志文件名就是從 db2 list applications 命令獲得的應用程序 ID 的最後部分。例如,這個命令典型的輸出如下所示:
清單 1. db2 list applications 命令的示例輸出Auth Id Application Appl. Application Id DB # of
Name Handle Name Agents
-------- -------------- ---------- ------------------------------ -------- -----
DB2INST1 db2jcc_applica 864 GA0A0A34.A905.051223231443 FALCON 1
DB2INST1 db2jcc_applica 867 GA0A0A34.A904.051223231442 FALCON 1
DB2INST1 db2jcc_applica 866 GA0A0A34.A906.051223231441 FALCON 1
DB2INST1 db2jcc_applica 862 GA0A0A34.A907.051223231440 FALCON 1
如果上面的每個 DB2 代理都正在執行相同的存儲過程,那麼就會生成 4 個日志文件,其名稱分別如下所示:
051223231443.log
051223231442.log
051223231441.log
051223231440.log
要關閉日志文件分隔功能:
call DB2.UPDATE_SP_CONFIG('LOG_SPLIT','N');
要將 Debug 設置為 Y 來創建 TESTSP 日志標記:
call DB2.UPDATE_SP_CONFIG('TEST_SP','Y');
要將 Debug 設置為 N 來更新 TESTSP 日志標記:
call DB2.UPDATE_SP_CONFIG('TEST_SP','N');
要將 TESTSP 日志標記從共享內存和配置文件中刪除:
call DB2.UPDATE_SP_CONFIG('TEST_SP','R');
DB2.UPDATE_SP_CONFIG 存儲過程對共享內存進行更新,並將所有的日志標記寫入一個配置文件中。日志目錄被命名為 splog,我們需要在 ~/sqllib 目錄中創建它。可以采用兩種方法來創建這個目錄:
如果 DB2 的用戶與實例用戶不同:
在 sqllib 中創建 splog 目錄,並將其屬性更改為 777。
在 splog 目錄中,創建一個與數據庫名稱相同的目錄。
在這個數據庫名稱目錄中創建一個 archive 目錄。
如果 DB2 fenced 用戶與實例用戶相同:
這個框架將創建必需的文件和目錄,因為日志框架具有創建這些目錄和文件所需的權限。
如果希望將 splog 文件夾與 sqllib 文件夾分開存放,可以為 splog 創建一個單獨的掛載點,並創建一個指向這個掛載點的符號鏈接 ~/sqllib/splog。
日志管理存儲過程 DB2.UPDATE_SP_CONFIG 會創建或更改 log 目錄中的 .splogrc 配置文件。例如,如果數據庫名為 “sample”,並且 DB2 fenced 用戶與實例用戶不同,那麼我們可以先創建 sqllib/splog 目錄,然後創建 sqllib/splog/sample 目錄,最後再創建 sqllib/splog/sample/archive 目錄。如果 DB2 fenced 用戶與實例用戶相同,那麼這個框架就可以為我們自動創建這些目錄。
清單 2. 配置文件db2inst1@p595 /home/db2inst1/sqllib/splog/sample=>ls -al
drwxrwsrwx 3 db2inst1 db2grp1 512 Dec 22 13:53 .
drwxrwsrwx 3 db2inst1 db2grp1 512 Dec 21 23:45 ..
-rw-r--r-- 1 db2inst1 db2grp1 6811 Dec 22 15:46 .splogrc
-rw-r--r-- 1 db2inst1 db2grp1 5192 Dec 22 13:50 051222034739.log
-rw-r--r-- 1 db2inst1 db2grp1 3904 Dec 22 15:45 051222195007.log
drwx--S--- 2 db2inst1 db2grp1 512 Feb 09 1971 archive
-rw-r--r-- 1 db2inst1 db2grp1 768700 Dec 22 15:46 sp.log
DB2.UPDATE_SP_CONFIG 存儲過程會在 ~/sqllib/splog/YourDBName 目錄中創建或更新 .splogrc 文件。
清單 3. 配置文件$ cat .splogrc
# *************************************************************************
# ** (C) COPYRIGHT International Business Machines Corp. 2000 - 2005
# ** All Rights Reserved.
# **
# ** Vikram Khatri vkhatri(at)us.ibm.com
# ** Use it at your own risk. No warranties implIEd and no support available
# *************************************************************************
global_logging=Y
log_split=N
testsp=No
直接對配置文件進行編輯應該沒有其他任何要求。即使直接編輯這個文件,也無法立即看到這些參數的效果,因為存儲過程 DB2.UPDATE_SP_CONFIG 還需要對共享內存進行更新,DB2.LOGINFO 和 DB2.LOGGER 主要從這裡獲取日志參數。
在創建日志標記(例如 TESTSP)之後,就可以在調用 DB2.OPEN_LOG 啟動日志功能時使用這個標記。在調用 OPEN_LOG 之後再調用 DB2.LOGINFO 只會檢查共享內存中是否存在 TESTSP 標記。如果存在該標記,則會記錄 LOGINFO 消息。DB2.LOGGER 會使用 TESTSP 標記檢查 DEBUG 參數,並確定它是否被設置為 Y,如果是,則記錄調試消息。
獲取存儲過程的配置
由於根本沒有必要具有訪問 .splogrc 配置文件的訪問權限,因此可以利用 DB2.GET_SP_CONFIG 存儲過程的幫助來查看日志標記的設置。
清單 4. 獲取存儲過程的日志參數db2inst1@p595 /home/db2inst1/sqllib/splog/sample=>db2 "call db2.get_sp_config()"
Result set 1
--------------
SP_NAME DEBUG_MODE
-------------------- ----------
TESTSP No
獲取存儲過程日志文件
存儲過程的日志文件保存在 ~/sqllib/splog/YourDBName 目錄中。如果已經使用 LOG_SPLIT=Y,則會為每個 DB2 代理都生成一個日志文件。如果沒有訪問 DB2 服務器的登錄訪問權限,那麼還可以通過使用 DB2.GET_SP_LOG 存儲過程來獲取日志文件的內容。這個過程需要使用日志文件名作為輸入參數。如果 LOG_SPLIT 被設置為 N,那麼可以使用 sp.log 作為日志文件名。否則,可以使用 db2 list applications 命令來確定應用程序的 ID,並使用應用程序 ID 的最後部分作為日志文件名。
清單 5. 獲取存儲過程的日志文件內容db2inst1@p595 /home/db2inst1/sqllib/splog/sample=>db2 "call db2.get_sp_log('sp.log')"
Result set 1
--------------
12-25-2005 22:29:39.839:[testsp:051226021028] Begin Stored Procedure *** [681574408]
12-25-2005 22:29:39.839:[testsp:051226021028] The database name from dbInfo is SAMPLE
12-25-2005 22:29:39.839:[testsp:051226021028] The HOME variable is /home/db2inst1
12-25-2005 22:29:39.839:[testsp:051226021028] The Application ID is *LOCAL.db2inst1.051226021028
12-25-2005 22:29:39.839:[testsp:051226021028] End Stored Procedure *** Elapsed 0.000
12-25-2005 22:36:04.048:[testsp:051226021028] Begin Stored Procedure *** [681574408]
12-25-2005 22:36:04.048:[testsp:051226021028] The database name from dbInfo is SAMPLE
12-25-2005 22:36:04.048:[testsp:051226021028] The HOME variable is /home/db2inst1
12-25-2005 22:36:04.048:[testsp:051226021028] The Application ID is *LOCAL.db2inst1.051226021028
12-25-2005 22:36:04.048:[testsp:051226021028] End Stored Procedure *** Elapsed 0.000
日志框架存儲過程
在 SQL-PL 存儲過程中可以使用 4 個日志存儲過程來記錄消息。表 1 給出了詳細的內容。
表 1. 用來記錄日志的存儲過程 存儲過程名 語法 參數 DB2.OPEN_LOG CALL DB2.OPEN_LOG (log_token_name, handle) log_token_name —— 日志標記名。如果已經使用 DB2.UPDATE_SP_CONFIG 在共享內存中設置了這個值,那麼將執行日志操作,否則將忽略它。
handle —— 在 SQL-PL 代碼中聲明的變量名。它需要聲明為 CHAR(48) FOR BIT DATA 類型。
DB2.LOGINFO CALL DB2.LOGINFO (handle, message) handle —— 調用 DB2.OPEN_LOG 時所設置的句柄。message —— 希望記錄日志的信息消息。
DB2.LOGGER CALL DB2.LOGGER (handle, message) handle —— 調用 DB2.OPEN_LOG 時所設置的句柄。message —— 希望記錄日志的信息消息。
DB2.CLOSE_LOG CALL DB2.CLOSE_LOG (handle) handle —— 調用 DB2.OPEN_LOG 時所設置的句柄。將一切准備就緒
為了構建這個日志框架,需要數據庫管理員幫我們設置存儲過程的日志目錄並創建一個用戶臨時表空間。
DBA 的操作
在 ~/sqllib 目錄中創建一個 splog 目錄。DBA 可以創建一個符號鏈接 ~/sqllib/splog,指向我們有權查看的日志文件的其他地方。
對 ~/sqllib/splog 設置適當的文件權限,這樣 DB2 fenced 用戶就可以具有對其進行操作所需的所有權限了。
在 ~/sqllib/splog 目錄中創建一個名字與數據庫名相同的目錄。
在 ~/sqllib/splog/YourDBName 目錄中創建一個 archive 目錄。
創建一個用作全局臨時表的用戶表空間。日志管理存儲過程 DB2.GET_SP_CONFIG 和 DB2.GET_SP_LOG 都會使用一個全局臨時表將結果集返回給客戶機。下面的腳本給出了一個例子,我們可以對其進行更改,從而在自己的環境中創建一個用戶臨時表空間。
清單 6. 創建用戶臨時表空間CREATE USER TEMPORARY TABLESPACE dgtt IN DATABASE
PARTITION GROUP PG0 PAGESIZE 4096 MANAGED BY SYSTEM
USING ('/stagefs/dbins17a/dgtt/dgtttbsp.tablespace')
ON DBPARTITIONNUMS (0)
EXTENTSIZE 32
PREFETCHSIZE AUTOMATIC
BUFFERPOOL IBMDEFAULTBP
DROPPED TABLE RECOVERY OFF;
開發人員的操作
這個日志框架已經在 64 位的 AIX® 5.3.1 和 64 位的 Linux™ 平台(Suse 9 和 RHEL 3)進行了測試。在這個特定的 UNIX® 32/64 位平台上,需要使用 ~/sqllib/samples/c 中的 bldrtn 和 embprep 程序,而不是使用本框架所提供的文件。特定於平台的 bldrtn 和 embprep 程序會使用正確的編譯器和鏈接選項。
表 2. 日志源文件 文件名 目的 splogger.sqc 使用 C 編寫的本地程序,以及日志方法使用的存儲過程封裝程序。 splogger.exp 將使用共享庫導出的方法名稱。 spcat DB2 catalog 腳本,用於將外部共享庫程序注冊成 DB2 模式中的存儲過程。 spalter DB2 catalog 腳本,用於對存儲過程執行 ALTER 操作,將其更改為一個不同的共享庫名,這樣當 DB2 KEEPFENCED 被設置為 YES 時,DB2 就可以將新的庫加載到內存中。 makefile 編譯源代碼使用的 Makefile。 bldrtn makefile 調用這個腳本來編譯 C 源代碼。請使用適合您的 UNIX 版的 bldrtn 文件(在 ~/sqllib/samples/c 目錄中),而不是使用此處提供的文件。 embprep bldrtn 會調用這個腳本,通過 DB2 PREP 來預處理 sqc 文件,並使用 BIND 將其綁定到數據庫上。請使用適合您的 UNIX 版的 embprep 文件(在 ~/sqllib/samples/c 目錄中),而不是使用此處提供的文件。
通過 SQL-PL 存儲過程來使用這個日志框架
下面這個 SQL-PL 存儲過程 TESTSP 的例子顯示了日志存儲過程的用法。
清單 7. SQL-PL 中的日志框架$ db2 CONNECT TO SAMPLE
$ db2 "call DB2.UPDATE_SP_CONFIG('TESTSP','Y')"
$ db2 CONNECT RESET
$ db2 -td@ -f testsp.sql
File testsp.sql
-----------------
CONNECT TO SAMPLE
@
SET CURRENT SCHEMA = 'TEST'
@
DROP PROCEDURE TESTSP
@
CREATE PROCEDURE TESTSP()
LANGUAGE SQL
BEGIN
DECLARE h CHAR(48) FOR BIT DATA;
DECLARE count INTEGER DEFAULT 0;
CALL DB2.OPEN_LOG('TESTSP',h);
SET count = count + 1;
CALL DB2.LOGINFO(h,'this is a test loginfo '||CHAR(COALESCE(count,0))||' message');
SET count = count + 1;
CALL DB2.LOGGER(h,'this is a test logger '||CHAR(COALESCE(count,0))||' message');
SET count = count + 1;
CALL DB2.LOGINFO(h,'this is a test loginfo '||CHAR(COALESCE(count,0))||' message');
SET count = count + 1;
CALL DB2.LOGGER(h,'this is a test logger '||CHAR(COALESCE(count,0))||' message');
SET count = count + 1;
CALL DB2.LOGINFO(h,'this is a test loginfo '||CHAR(COALESCE(count,0))||' message');
SET count = count + 1;
CALL DB2.LOGGER(h,'this is a test logger '||CHAR(COALESCE(count,0))||' message');
SET count = count + 1;
CALL DB2.LOGINFO(h,'this is a test loginfo '||CHAR(COALESCE(count,0))||' message');
SET count = count + 1;
CALL DB2.LOGGER(h,'this is a test logger '||CHAR(COALESCE(count,0))||' message');
CALL DB2.CLOSE_LOG(h);
END
@
CONNECT RESET
@
Call TESTSP Stored Procedure
----------------------------
$ db2 "call test.testsp(?)"
圖 2. 日志文件的輸出
圖片看不清楚?請點擊這裡查看原圖(大圖)。
通過 C 存儲過程來使用日志框架
由於日志框架程序是使用 C 語言編寫的,因此可以在 C 存儲過程中直接使用它們,而不需要再調用日志存儲過程。下面的示例 C 存儲過程使用了日志 API。要使用這些日志方法,則需要將 splogger 共享庫包含到自己的項目中。
我們需要在 C 存儲過程中包含 logger.h,它定義了日志方法的原型。
清單 8. 日志框架 API —— logging.h 頭文件#define BYTEHANDLESIZE 48
void openLog(char *name, char *handle);
void closeLog(char *handle);
void setDBName(struct sqludf_dbinfo *dbInfo);
void logger(char *handle, char *fmt, ... );
void logginfo(char *handle, char *fmt, ... );
清單 9. 顯示日志方法 API 用法的示例 C 存儲過程SQL_API_RC SQL_API_FN Snow
(
char outMessage[100],
sqlint16 *outMessageNullInd,
char sqlstate[6],
char qualName[28],
char specName[19],
char diagMsg[71],
struct sqludf_dbinfo *dbInfo
)
{
char byteHandle[BYTEHANDLESIZE];
char dbName[128] = "";
setDBName(dbInfo);
openLog("TESTSP", byteHandle);
strncpy(outMessage, (char *)(dbInfo->dbname), dbInfo->dbnamelen);
outMessage[dbInfo->dbnamelen] = '\0';
logger(byteHandle, "The database name from dbInfo is %s", outMessage);
strcat(outMessage, "::");
logger(byteHandle, "The HOME variable is %s", getenv("HOME"));
strcat(outMessage, getenv("HOME"));
strcat(outMessage, "::");
strcat(outMessage, (char *)(dbInfo->appl_id));
logger(byteHandle, "The Application ID is %s", (char *)(dbInfo->appl_id));
*outMessageNullInd = 0;
closeLog(byteHandle);
return 0;
}
除了日志 API 調用之外,還需要注意 setDBName 的用法。C 程序必須調用這個方法,因為它要從 dbInfo DB2 結構中獲取數據庫名來設置日志文件名。
請使用下面的 SQL 語句來注冊這個示例存儲過程。
清單 10. 注冊示例存儲過程CREATE PROCEDURE DB.SNOW
(
OUT MSG VARCHAR(100)
)
DYNAMIC RESULT SETS 0
DETERMINISTIC
LANGUAGE C
PARAMETER STYLE SQL
DBINFO
FENCED NOT THREADSAFE
NO SQL
PROGRAM TYPE SUB
EXTERNAL NAME 'splogger!Snow';
常見問題解答
問題:在使用 makefile 來編譯源代碼時,看到 “bldrtn not found” 錯誤,這是怎麼回事?
解答:bldrtn 文件的權限可能被更改過了,這與將文件復制到系統中的方法有關。請使用 chmod 命令來更改 bldrtn、embprep 和 spcat 的權限。例如:
$ chmod +x embprep bldrtn spcat bld spalter
問題:當編譯 splogger 時,在執行 spcat 的過程中會看到 “database connection does not exist” 錯誤,這是怎麼回事?在 spcat 中我沒有看到任何 connect 語句。連接應該在哪裡進行呢?
解答:在 spcat 中沒有 connect 語句,它假設連接數據庫是隱式的。
清單 11. 如何使用隱式連接$ db2set DB2DBDFT=<your database name>
$ db2stop force
$ db2start
$ db2 activate db <your database name>
問題:我沒有訪問 DB2 的實例訪問權限,應該如何編譯和構建 splogger 呢?
解答:請更改 makefile,並為 ALIAS、UID 和 PWD 指定適當的值。在 spcat 和 spalter 中放置一條 CONNECT 語句。
問題:bld 腳本的用途是什麼?
解答:bld 腳本用來將 splogger 編譯成一個單獨的程序,而不是一個共享庫,這樣做的目的是為了進行測試。它生成一個 splog 二進制文件,可以用該文件來執行 splogger.sqc 中的主程序。
問題:在運行 make 命令時,看到了 “make: 1254-004 The error code from the last command is 4.” 錯誤,這是怎麼回事?
解答:這是 spcat 的一個正常結果。它對 DB2 日志存儲過程執行 DROP PROCEDURE 命令,如果在數據庫中不存在日志存儲過程,就會看到上面的錯誤。下一次運行 spcat 時,就不會再看到這個錯誤。
問題:如果我對這個框架進行了一些更改,並且將在自己的 SQL-PL 存儲過程中使用這些更改,那麼應該如何使用這個日志程序呢?由於性能方面的原因,我們不能將 KEEPFENCED 設置為 NO,因為在我們的系統中還運行了其他外部 C 存儲過程。
解答:請使用 CLP 命令 UPDATE DBM CFG USING KEEPFENCED YES 來設置 DB2 實例級參數 KEEPFENCED=YES。重新啟動實例之後,這個外部存儲過程庫就會由 DB2 在首次調用存儲過程時被加載到內存中。然後這些共享庫會一直保存在內存中,直到重新啟動實例為止。如果對這個外部庫進行更改,並將這些更改復制到 ~/sqllib/function 目錄中,那麼 DB2 將不會使用它,因為原來的庫已經被加載到內存中了。這不是 DB2 的限制,而是操作系統本身就被設計成這樣。(不過在大型機上可以加載或卸載共享庫。)
顯然,我們希望在某個產品系統上將 KEEPFENCED 參數設置為 YES,如果必須對外部存儲過程(包括 splogger)進行更改,那麼有一種替代解決方案可以強迫 DB2 加載新的庫。spalter 腳本就使用了這種方法。它對此共享庫進行重命名,並使用新的庫名稱來更改存儲過程。DB2 會加載這個新的庫,但是舊的庫仍然會一直保存在內存中,直到重新啟動實例為止。
清單 12. 當 KEEPFENCED=YES 時對共享庫進行更改#! /bin/ksh
TOK=$(date +"%y%m%d%H%M%S")
SHLIBNAME=splogger$TOK
rm -f ~/sqllib/function/splogger*
cp -f splogger ~/sqllib/function/$SHLIBNAME
db2 -tv << !EOF
ALTER PROCEDURE DB2.UPDATE_SP_CONFIG EXTERNAL NAME '${SHLIBNAME}!UpdateSPConfig';
ALTER PROCEDURE DB2.GET_SP_CONFIG EXTERNAL NAME '${SHLIBNAME}!GetSPConfig';
ALTER PROCEDURE DB2.GET_SP_LOG EXTERNAL NAME '${SHLIBNAME}!GetSPLog';
ALTER PROCEDURE DB2.OPEN_LOG EXTERNAL NAME '${SHLIBNAME}!OpenLog';
ALTER PROCEDURE DB2.CLOSE_LOG EXTERNAL NAME '${SHLIBNAME}!CloseLog';
ALTER PROCEDURE DB2.LOGGER EXTERNAL NAME '${SHLIBNAME}!Logger';
ALTER PROCEDURE DB2.LOGINFO EXTERNAL NAME '${SHLIBNAME}!Loginfo';
!EOF
問題:在日志文件中,在 “Begin Stored Procedure *** [681574408]” 一行中有一個數字。這個數字是什麼?
解答:這是 splogger 庫所使用的共享內存的位置。當停止 DB2 時,就會釋放 DB2 所使用的共享內存。對於 splogger 共享庫來說,無法確定它何時會釋放這個共享內存。不過可以使用這個數字來釋放這個庫所使用的共享內存。請使用 ipcrm -m 681574408 命令來釋放這段內存。
問題:我正在使用這個框架,但卻沒有看到任何日志文件。日志文件應該在哪裡呢?
解答:這是最常見的問題。您可以在 /tmp 目錄中查找是否有日志文件。這與所使用的用戶 ID 和 ~/sqllib/splog 目錄的文件權限有關。請讓 DBA 幫助您在 ~/sqllib 目錄中創建一個 splog 目錄,並將這個 splog 的全部權限授予 DB2 fenced 用戶。您也可以在 DB2 fenced 用戶的根目錄中創建一個 splog 目錄,並在 ~/sqllib/splog 中創建一個指向這個 splog 目錄的軟鏈接 (soft link)。日志存儲過程被設計用來作為一個 FENCED 進程運行,它們都是用 DB2 fenced 用戶 ID 來運行的。
問題:這個框架中的 hardw 方法是什麼?它的用途是什麼?
解答:這個日志框架被設計用於調試存儲過程,hardw 方法用來調試這些日志程序。
問題:系統已經不再向文件中寫日志了。在日志文件中我看不到任何新消息了。到底發生了什麼?
解答:請驗證包含 splog 目錄的文件系統是否還有空間。
問題:在使用這個框架時還需要調整其他 DB2 參數嗎?
解答:不需要。
問題:DB2.GET_SP_LOG 的輸出一團糟。什麼地方出問題了?
解答:存儲過程 DB2.GET_SP_LOG 一次要從日志文件中讀取 4000 個字節。請將輸出結果重定向到某一文件中。
問題:我試圖在 Windows® 平台上編譯這個框架,卻看到很多編譯器錯誤。我應該怎樣做呢?
解答:這個框架只在 AIX 和 Linux 平台上進行了測試。我正在使用 C# 將這個框架轉換到 Windows 平台上。
問題:我試圖在 Solaris 平台上編譯這個框架,卻看到很多編譯器錯誤。我應該怎樣做呢?
解答:這個框架還沒有在 Solaris 平台上進行測試。請把錯誤告訴我,我會試著解決這些問題。
問題:當我試圖在 Linux 平台上編譯這個框架時,看到了很多編譯器錯誤。我應該怎樣做呢?
解答:將 bldrtn 和 embprep 文件從 ~/sqllib/samples/c 復制到您的目錄中,並重新再次嘗試。
問題:有任何計劃要將這個框架加入到某個 DB2 產品中嗎?
解答:到目前為止還沒有。
問題:我可以對這個框架進行更改並在自己的產品中使用嗎?
解答:當然。這是一個開放源碼的存儲過程日志器。請在每個源代碼中維護 IBM 的版權信息。
問題:當我試圖使用 ipcrm 命令來釋放共享內存時,看到一個錯誤說不允許我刪除這段共享內存。應該如何解決這個問題呢?
解答:日志器存儲過程是使用 DB2 fenced 用戶的身份來運行的。請以 DB2 fenced 用戶的身份登錄系統,並再次嘗試刪除這段共享內存。如果 DB2 fenced 用戶是 nobody,請讓系統管理員幫助您使用 ipcrm 命令來刪除這段共享內存。
問題:我已經使用這個框架,存儲過程已經進行了全面的調試,現在不需要任何日志調用了。但即使是幾毫秒的時間,我也希望節省出來,這樣可以用來改進性能。關閉日志功能最好的方法是什麼?
解答:可以通過將 global_logging 設置成 N 來全局地關閉日志功能。然而,如果希望節省存儲過程執行時的每一毫秒的時間,那麼在確信 SQL-PL 和外部存儲過程都已經准備好之後,就可以注釋掉所有的 CALL DB2. 語句。我使用下面這個簡單的 sed 腳本來注釋掉這些調用,如果以後需要,可以再重新使用它們。
清單 13. 如果全局關閉日志功能db2 "call db2.update_sp_config('global_logging','y')"
清單 14. 如果在 SQL-PL 存儲過程中注釋掉 CALL DB2. 語句#!/bin/ksh
# Comment Logger Calls
cat actsp.sql | sed 's/CALL DB2./--CALL DB2./' > sp.sql
db2 -td@ -f sp.sql
#!/bin/ksh
# Uncomment Logger Calls
cat sp.sql | sed 's/--CALL DB2./CALL DB2./' > actsp.sql
db2 -td@ -f actsp.sql
問題:我對增強這個框架的功能很感興趣。此時我應該怎樣做呢?
解答:這是一個提供 DB2 存儲過程日志功能的開放源碼框架。歡迎用戶貢獻自己的能力並提供反饋。
結束語
日志框架可以用來將日志信息和調試信息記錄到日志文件中,並進行一些性能方面的評測。可以在產品系統中使用這個框架,在系統運行時激活或關閉存儲過程的日志功能,它可以幫助我們理解和管理存儲過程。
本文示例源代碼或素材下載