為了有效地管理Web伺服器,有必要獲得有關伺服器的活動和性能以及可能發生的任何問題的回饋。Apache HTTP Server提供了非常全面和靈活的日誌記錄功能。本文將介紹如何配置其日誌記錄功能,以及如何理解日誌包含的內容。
Apache HTTP Server提供了各種不同的機制,用於記錄伺服器上發生的所有事情,從初始請求到URL映射過程,再到最終的連接解決方案,包括流程中可能發生的任何錯誤。除此之外,第三方模組可以提供日誌記錄功能,或者將條目注入到現有日誌檔中,並且諸如CGI程式或PHP腳本或其他處理程式之類的應用程式可以向伺服器錯誤日誌發送消息。
在本文中,我們將討論作為http伺服器標準部分的日誌記錄模組。
安全警告
任何能夠寫入Apache httpd正在編寫日誌檔的目錄的用戶幾乎可以訪問伺服器啟動的uid,通常是root用戶。不要在不知道後果的情況下對存儲日誌的目錄進行寫訪問;。
此外,日誌檔可能包含客戶端直接提供的資訊,而不會轉義。因此,惡意客戶端可能會在日誌檔中插入控制字元,因此在處理原始日誌時必須小心。
錯誤日誌
伺服器錯誤日誌(其名稱和位置由ErrorLog
指令設置)是最重要的日誌檔。這是Apache httpd將發送診斷資訊並記錄它在處理請求時遇到的任何錯誤的地方。當啟動伺服器或伺服器操作出現問題時,它是第一個查看的地方,因為它通常包含錯誤的詳細資訊以及如何修復它。
錯誤日誌通常寫入檔(通常是Unix系統上的error_log和Windows和OS/2上的error.log
)。在Unix系統上,也可以讓伺服器向syslog
發送錯誤或將它們傳遞給程式。
錯誤日誌的格式由ErrorLogFormat
指令定義,可以使用該指令自定義記錄的值。如果未指定默認格式,則默認為格式。典型的日誌消息如下:
[Fri Sep 09 10:42:29.902022 2011] [core:error] [pid 35708:tid 4328636416] [client 72.15.99.187] File does not exist: /usr/local/apache2/htdocs/favicon.ico
日誌條目中的第一項是消息的日期和時間。接下來是生成消息的模組(在本例中為核心)以及該消息的嚴重性級別。接下來是經歷該條件的進程ID以及(如果適用的話)線程ID。接下來,我們有發出請求的客戶端地址。最後是詳細的錯誤消息,在這種情況下表示對不存在的檔的請求。
錯誤日誌中可能會出現各種各樣的不同消息。大多數看起來類似於上面的例子。錯誤日誌還將包含CGI腳本的調試輸出。通過CGI腳本寫入stderr
的任何資訊都將直接複製到錯誤日誌中。
在錯誤日誌和訪問日誌中放置%L
識別字將生成一個日誌條目ID,您可以使用該ID將錯誤日誌中的條目與訪問日誌中的條目相關聯。如果加載了mod_unique_id
,則其唯一請求ID也將用作日誌條目ID。
在測試期間,連續監視錯誤日誌以查找任何問題通常很有用。在Unix系統上,您可以使用以下方法完成此操作 -
$ tail -f error_log
按模組記錄日誌
LogLevel
指令用於基於每個模組指定日誌嚴重性級別。通過這種方式,如果您只使用一個特定模組來解決問題,則可以調高其日誌記錄量,而無需獲取不感興趣的其他模組的詳細資訊。這對於mod_proxy
或mod_rewrite
等模組特別有用。你想知道它想要做什麼的細節。
通過在LogLevel
指令中指定模組的名稱來執行此操作:
LogLevel info rewrite:trace5
這會將主LogLevel
設置為info
,但將其設置為trace5
以獲取mod_rewrite
。
訪問日誌
伺服器訪問日誌記錄伺服器處理的所有請求。訪問日誌的位置和內容由CustomLog
指令控制。LogFormat
指令可用於簡化日誌內容的選擇。本節介紹如何配置伺服器以在訪問日誌中記錄資訊。
當然,將資訊存儲在訪問日誌中只是日誌管理的開始。下一步是分析此信息以生成有用的統計資訊。一般而言,日誌分析超出了本文檔的範圍,並不是Web伺服器本身的部分工作。
各種版本的Apache httpd使用其他模組和指令來控制訪問日誌記錄,包括mod_log_referer
,mod_log_agent
和TransferLog
指令。CustomLog
指令現在包含所有舊指令的功能。
訪問日誌的格式是高度可配置的。使用格式字串指定格式,該字串看起來很像C樣式的printf(1)格式字串。
通用日誌格式
訪問日誌的典型配置可能如下所示-
LogFormat "%h %l %u %t "%r" %>s %b" common
CustomLog logs/access_log common
這定義了昵稱common
,並將其與特定的日誌格式字串相關聯。格式字串由百分比指令組成,每個指令指示伺服器記錄特定的資訊。文字字元也可以放在格式字串中,並直接複製到日誌輸出中。引號字元("
)必須通過在它前面放一個反斜杠來轉義,以防止它被解釋為格式字串的結尾。格式字串也可能包含特殊控制字元\n
表示換行符和\t
為標籤。
CustomLog
指令使用定義的昵稱設置新的日誌檔。訪問日誌的檔案名相對於ServerRoot
,除非它以斜杠開頭。
上述配置將以稱為通用日誌格式(CLF)的格式寫入日誌條目。這種標準格式可以由許多不同的Web伺服器生成,並由許多日誌分析程式讀取。CLF
中生成的日誌檔條目如下所示:
127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
組合日誌格式
另一種常用的格式字串稱為組合日誌格式,它可以如下使用。
LogFormat "%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"" combined
CustomLog log/access_log combined
此格式與通用日誌格式完全相同,另外還添加了兩個字段。每個附加字段都使用percent-directive%{header}i
,其中header
可以是任何HTTP請求標頭。此格式下的訪問日誌如下所示:
127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)"
多個訪問日誌
只需在配置檔中指定多個CustomLog
指令即可創建多個訪問日誌。例如,以下指令將創建三個訪問日誌。第一個包含基本的CLF資訊,第二個和第三個包含引用和流覽器資訊。最後兩條CustomLog
行顯示了如何模仿ReferLog
和AgentLog
指令的效果。
LogFormat "%h %l %u %t \"%r\" %>s %b" common
CustomLog logs/access_log common
CustomLog logs/referer_log "%{Referer}i -> %U"
CustomLog logs/agent_log "%{User-agent}i"
條件日誌
有時,根據客戶端請求的特徵從訪問日誌中排除某些條目是方便的。這可以通過環境變數輕鬆完成。首先,必須設置環境變數以指示請求滿足特定條件。通常通過SetEnvIf
完成。然後,CustomLog
指令的env =
子句用於包含或排除設置環境變數的請求。一些例子:
# Mark requests from the loop-back interface
SetEnvIf Remote_Addr "127\.0\.0\.1" dontlog
# Mark requests for the robots.txt file
SetEnvIf Request_URI "^/robots\.txt$" dontlog
# Log what remains
CustomLog logs/access_log common env=!dontlog
作為另一個示例,考慮將來自英語用戶的請求記錄到一個日誌檔,將非英語用戶記錄到不同的日誌檔。
SetEnvIf Accept-Language "en" english
CustomLog logs/english_log common env=english
CustomLog logs/non_english_log common env=!english
在緩存場景中,如果想知道緩存的效率。一個非常簡單的方法是:
SetEnv CACHE_MISS 1
LogFormat "%h %l %u %t "%r " %>s %b %{CACHE_MISS}e" common-cache
CustomLog logs/access_log common-cache
mod_cache
將在mod_env
之前運行,並且在成功時將在沒有它的情況下傳遞內容。在這種情況下,緩存命中將記錄 - 而緩存未命中將記錄1。
除了env =
語法之外,LogFormat還支持以HTTP回應代碼為條件的日誌記錄值:
LogFormat "%400,501{User-agent}i" browserlog
LogFormat "%!200,304,302{Referer}i" refererlog
在第一個示例中,如果HTTP狀態代碼為400或501,則將記錄用戶代理。在其他情況下,將記錄文字-
。同樣,在第二個示例中,如果HTTP狀態代碼不是200,204或302,則將記錄Referer
。
記錄輪換
即使是在中等繁忙的伺服器上,日誌檔中存儲的資訊量也非常大。訪問日誌檔通常每10,000個請求增長1MB或更多。因此,有必要通過移動或刪除現有日誌來定期輪換日誌檔。這在伺服器運行時無法完成,因為Apache httpd將繼續寫入舊的日誌檔,只要它保持檔打開即可。相反,必須在移動或刪除日誌檔後重新啟動伺服器,以便它將打開新的日誌檔。
通過使用正常重新啟動,可以指示伺服器打開新的日誌檔,而不會丟失來自客戶端的任何現有或掛起的連接。但是,為了實現此目的,伺服器必須在完成舊請求的服務時繼續寫入舊日志檔。因此,在對日誌檔進行任何處理之前,必須在重新啟動後等待一段時間。簡單地旋轉日誌並壓縮舊日志以節省空間的典型方案是:
mv access_log access_log.old
mv error_log error_log.old
apachectl graceful
sleep 600
gzip access_log.old error_log.old
管道日誌
Apache httpd能夠通過管道將錯誤和訪問日誌檔寫入另一個進程,而不是直接寫入檔。此功能可顯著提高日誌記錄的靈活性,而無需向主伺服器添加代碼。要將日誌寫入管道,只需使用管道符“|”替換檔案名,然後替換應接受其標準輸入上的日誌條目的可執行檔的名稱。伺服器啟動時伺服器將啟動管道日誌進程,如果在伺服器運行時崩潰,它將重新啟動它。
管道日誌進程由父Apache httpd進程生成,並繼承該進程的用戶標識。這意味著管道日誌程式通常以root身份運行。因此,保持程式簡單安全非常重要。
管道日誌的一個重要用途是允許日誌輪換而無需重新啟動伺服器。為此,Apache HTTP Server包含一個名為rotatelogs
的簡單程式。例如,要每24小時輪換一次日志,您可以使用:
CustomLog "|/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common
請注意,引號用於包含將為管道調用的整個命令。雖然這些示例適用於訪問日誌,但相同的技術可用於錯誤日誌。
與條件日誌記錄一樣,管道日誌是一種非常強大的工具,但是如果可以使用更簡單的解決方案(如離線後處理),則不應使用它們。
默認情況下,在不調用shell的情況下生成管道日誌進程。使用|$
代替|
使用shell生成(通常使用/bin/sh -c
):
# Invoke "rotatelogs" using a shell
CustomLog "|$/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common
這是Apache 2.2的默認行為。根據shell的具體情況,這可能會導致日誌記錄管道程式的生命週期內的額外shell進程以及重新啟動期間的信號處理問題。出於與Apache 2.2的相容性原因,符號||
也支持並等同於使用|
。
虛擬主機日誌
運行具有許多虛擬主機的伺服器時,有幾個選項可用於處理日誌檔。首先,可以使用與單主機伺服器完全相同的日誌。只需將日誌記錄指令放在主伺服器上下文中的<VirtualHost>
部分之外,就可以在同一訪問日誌和錯誤日誌中記錄所有請求。此技術不允許在單個虛擬主機上輕鬆收集統計資訊。
如果將CustomLog
或ErrorLog
指令放在<VirtualHost>
部分中,則該虛擬主機的所有請求或錯誤將僅記錄到指定的檔。任何沒有日誌記錄指令的虛擬主機仍會將其請求發送到主伺服器日誌。此技術對於少量虛擬主機非常有用,但如果主機數量非常大,則管理起來可能很複雜。此外,它通常會產生檔描述符不足的問題。
對於訪問日誌,有一個非常好的折衷方案。通過將虛擬主機上的資訊添加到日誌格式字串,可以將所有主機記錄到同一日誌中,然後將日誌拆分為單個檔。例如,請考慮以下指令。
LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost
CustomLog logs/access_log comonvhost
%v
用於記錄為請求提供服務的虛擬主機的名稱。然後,可以使用像split-logfile
這樣的程式對訪問日誌進行後處理,以便將其分成每個虛擬主機的一個檔。