充分利用 PHP 日誌文件:實用指南
已發表: 2022-03-11可以正確地說,日誌是自由 php 開發人員可以使用的最被低估和未充分利用的工具之一。 儘管他們可以提供豐富的信息,但日誌是開發人員在嘗試解決問題時最後查看的地方並不少見。
事實上,在許多情況下,PHP 日誌文件應該是在出現問題時首先尋找線索的地方。 通常,它們包含的信息可以顯著減少拔頭髮試圖追踪一個粗糙的蟲子所花費的時間。
但也許更重要的是,只要有一點創造力和遠見,您的日誌文件就可以用作使用信息和分析的寶貴來源。 創造性地使用日誌文件可以幫助回答以下問題:哪些瀏覽器最常用於訪問我的站點? 我的服務器的平均響應時間是多少? 對站點根目錄的請求百分比是多少? 自我們部署最新更新以來,使用情況發生了怎樣的變化? 還有很多很多。
本文提供了一些關於如何配置日誌文件以及如何處理它們包含的信息的提示,以最大限度地發揮它們提供的優勢。
儘管本文在技術上側重於 PHP 開發人員的日誌記錄,但本文提供的大部分信息都與技術無關,並且也與其他語言和技術堆棧相關。
注意:本文假定您基本熟悉 Unix shell。 對於那些缺乏這方面知識的人,提供了一個附錄,其中介紹了在 Unix 系統上訪問和讀取日誌文件所需的一些命令。
我們的 PHP 日誌文件示例項目
作為本文討論目的的示例項目,我們將 Symfony Standard 作為一個工作項目,我們將在 Debian 7 Wheezy 上使用rsyslogd 、 nginx和PHP-FPM設置它。
composer create-project symfony/framework-standard-edition my "2.6.*"這很快為我們提供了一個帶有漂亮 UI 的工作測試項目。
配置日誌文件的提示
以下是有關如何配置日誌文件以幫助最大化其價值的一些提示。
錯誤日誌配置
錯誤日誌代表了最基本的日誌形式; 即,在出現問題時捕獲額外的信息和細節。 所以,在一個理想的世界裡,你會希望沒有錯誤並且你的錯誤日誌是空的。 但是,當問題確實發生時(因為它們總是如此),您的錯誤日誌應該是您在調試過程中所做的第一站。
錯誤日誌通常很容易配置。
一方面,所有錯誤和崩潰消息都可以以完全相同的格式記錄在錯誤日誌中,否則它們將呈現給用戶。 通過一些簡單的配置,最終用戶將永遠不需要在您的站點上看到那些醜陋的錯誤痕跡,而 devops 仍然能夠監控系統並查看這些錯誤消息的所有細節。 以下是在 PHP 中設置這種日誌記錄的方法:
log_errors = On error_reporting = E_ALL error_log = /path/to/my/error/log另外兩行很重要,要包含在實時站點的日誌文件中,以防止向用戶呈現血腥級別的錯誤詳細信息,它們是:
display_errors = Off display_startup_errors = Off 系統日誌 ( syslog ) 配置
在開源世界中, syslog守護程序有許多普遍兼容的實現,包括:
-
syslogd和sysklogd最常見於 BSD 系列系統、CentOS、Mac OS X 等 syslog-ng– 現代 Gentoo 和 SuSE 構建的默認值rsyslogd– 廣泛用於 Debian 和 Fedora 系列操作系統
(注意:在本文中,我們將使用rsyslogd作為示例。)
基本的 syslog 配置通常足以在系統範圍的日誌文件中捕獲日誌消息(通常是/var/log/syslog ;也可能是/var/log/messages或/var/log/system.log取決於發行版你正在使用)。
系統日誌提供了幾個日誌設施,其中八個( LOG_LOCAL0到LOG_LOCAL7 )是為用戶部署的項目保留的。 例如,這裡是您如何設置LOG_LOCAL0以根據日誌記錄級別(即錯誤、警告、信息、調試)寫入 4 個單獨的日誌文件:
# /etc/rsyslog.d/my.conf local0.err /var/log/my/err.log local0.warning /var/log/my/warning.log local0.info -/var/log/my/info.log local0.debug -/var/log/my/debug.log 現在,每當您將日誌消息寫入LOG_LOCAL0設施時,錯誤消息將轉到/var/log/my/err.log ,警告消息將轉到/var/log/my/warning.log ,依此類推。 但請注意,syslog 守護程序根據“此級別和更高級別”的規則過濾每個文件的消息。 因此,在上面的示例中,所有錯誤消息都將出現在所有四個配置文件中,警告消息將出現在除錯誤日誌之外的所有文件中,信息消息將出現在 info 和 debug 日誌中,而調試消息只會出現在debug.log .
還有一個重要的注意事項; 上述配置文件示例中的 info 和 debug 級別文件之前的-符號表示應該異步執行對這些文件的寫入(因為這些操作是非阻塞的)。 對於信息和調試日誌,這通常很好(甚至在大多數情況下推薦),但最好是同步寫入錯誤日誌(並且最可能是警告日誌)。
為了關閉不太重要的日誌級別(例如,在生產服務器上),您可以簡單地將相關消息重定向到/dev/null (即,無處):
local0.debug /dev/null # -/var/log/my/debug.log 一種有用的特定定制,尤其是支持我們將在本文後面討論的某些 PHP 日誌文件解析,是使用製表符作為日誌消息中的分隔符。 這可以通過在/etc/rsyslog.d中添加以下文件來輕鬆完成:
# /etc/rsyslog.d/fixtab.conf $EscapeControlCharactersOnReceive off最後,不要忘記在進行任何配置更改後重新啟動 syslog 守護程序以使它們生效:
service rsyslog restart服務器日誌配置
與您可以寫入的應用程序日誌和錯誤日誌不同,服務器日誌由相應的服務器守護進程(例如,Web 服務器、數據庫服務器等)在每個請求上專門寫入。 您對這些日誌的唯一“控制”是服務器允許您配置其日誌記錄功能。 儘管在這些文件中可能有很多需要篩選的內容,但它們通常是清楚了解服務器“幕後”發生了什麼的唯一方法。
讓我們在帶有 MySQL 存儲後端的 nginx 環境中部署我們的 Symfony Standard 示例應用程序。 這是我們將使用的 nginx 主機配置:
server { server_name my.log-sandbox; root /var/www/my/web; location / { # try to serve file directly, fallback to app.php try_files $uri /app.php$is_args$args; } # DEV # This rule should only be placed on your development environment # In production, don't include this and don't deploy app_dev.php or config.php location ~ ^/(app_dev|config)\.php(/|$) { fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param HTTPS off; } # PROD location ~ ^/app\.php(/|$) { fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param HTTPS off; # Prevents URIs that include the front controller. This will 404: # http://domain.tld/app.php/some-path # Remove the internal directive to allow URIs like this internal; } error_log /var/log/nginx/my_error.log; access_log /var/log/nginx/my_access.log; } 關於上面的最後兩個指令: access_log表示一般請求日誌,而error_log表示錯誤,並且與應用程序錯誤日誌一樣,值得設置額外的監控以提醒問題,以便您可以快速做出反應。
注意:這是一個故意過度簡化的 nginx 配置文件,僅用於示例目的。 它幾乎不關注安全性和性能,不應該在任何“真實”環境中按原樣使用。
這是我們在瀏覽器中輸入http://my.log-sandbox/app_dev.php/ Enter後在/var/log/nginx/my_access.log中得到的內容。
192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /app_dev.php/ HTTP/1.1" 200 6715 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/framework/css/body.css HTTP/1.1" 200 6657 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/framework/css/structure.css HTTP/1.1" 200 1191 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/acmedemo/css/demo.css HTTP/1.1" 200 2204 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/acmedemo/images/welcome-quick-tour.gif HTTP/1.1" 200 4770 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/acmedemo/images/welcome-demo.gif HTTP/1.1" 200 4053 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/acmedemo/images/welcome-configure.gif HTTP/1.1" 200 3530 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /favicon.ico HTTP/1.1" 200 6518 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:30 +0300] "GET /app_dev.php/_wdt/e50d73 HTTP/1.1" 200 13265 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36"這表明,為了提供一個頁面,瀏覽器實際上執行了 9 次 HTTP 調用。 然而,其中 7 個請求是對靜態內容的請求,這些請求是簡單而輕量級的。 但是,它們仍然佔用網絡資源,這可以通過使用各種精靈和縮小技術來優化。
雖然這些優化將在另一篇文章中討論,但這里相關的是我們可以通過使用另一個location指令分別記錄對靜態內容的請求:
location ~ \.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js)$ { access_log /var/log/nginx/my_access-static.log; } 請記住,nginx location執行簡單的正則表達式匹配,因此您可以包含您希望在您的站點上調度的盡可能多的靜態內容擴展。
解析此類日誌與解析應用程序日誌沒有什麼不同。
其他值得一提的日誌
另外兩個值得一提的 PHP 日誌是調試日誌和數據存儲日誌。
調試日誌
關於 nginx 日誌的另一個方便之處是調試日誌。 我們可以通過將配置的error_log行替換為以下內容來打開它(需要安裝 nginx 調試模塊):
error_log /var/log/nginx/my_error.log debug;相同的設置適用於 Apache 或您使用的任何其他網絡服務器。
順便說一句,調試日誌與錯誤日誌無關,即使它們是在error_log指令中配置的。
雖然調試日誌確實可以很冗長(例如單個 nginx 請求,生成了 127KB 的日誌數據!),但它仍然非常有用。 翻閱日誌文件可能很麻煩和乏味,但它通常可以快速提供線索和信息,極大地幫助加速調試過程。
特別是調試日誌可以真正幫助調試 nginx 配置,尤其是最複雜的部分,比如location匹配和rewrite鏈。
當然,調試日誌不應該在生產環境中啟用。 它們使用的空間量和存儲的信息量也意味著服務器上的大量 I/O 負載,這會顯著降低整個系統的性能。
數據存儲日誌
另一種類型的服務器日誌(用於調試)是數據存儲日誌。 在 MySQL 中,您可以通過添加以下行來打開它們:
[mysqld] general_log = 1 general_log_file = /var/log/mysql/query.log這些日誌僅包含系統在按時間順序為數據庫請求提供服務時運行的查詢列表,這有助於各種調試和跟踪需求。 但是,它們不應在生產系統上保持啟用狀態,因為它們會產生額外的不必要的 I/O 負載,從而影響性能。
寫入您的日誌文件
PHP 本身提供了用於打開、寫入和關閉日誌文件的函數(分別為openlog() 、 syslog()和closelog() )。
PHP 開發人員還有許多日誌庫,例如 Monolog(在 Symfony 和 Laravel 用戶中很流行),以及各種特定於框架的實現,例如集成到 CakePHP 中的日誌功能。 一般來說,像 Monolog 這樣的庫不僅包裝syslog()調用,還允許使用其他後端功能和工具。

這是一個如何寫入日誌的簡單示例:
<?php openlog(uniqid(), LOG_ODELAY, LOG_LOCAL0); syslog(LOG_INFO, 'It works!'); 我們在這裡調用openlog :
- 將 PHP 配置為在腳本的生命週期內為每個系統日誌消息添加一個唯一標識符
- 將其設置為延遲打開 syslog 連接,直到發生第一個
syslog()調用 - 將
LOG_LOCAL0設置為默認日誌記錄工具
以下是運行上述代碼後日誌文件的內容:
# cat /var/log/my/info.log Mar 2 00:23:29 log-sandbox 54f39161a2e55: It works!最大化 PHP 日誌文件的價值
現在我們都熟悉了理論和基礎知識,讓我們看看通過對示例 Symfony 標準項目進行盡可能少的更改的日誌可以得到多少。
首先,讓我們創建腳本src/log-begin.php (正確打開和配置我們的日誌)和src/log-end.php (記錄有關成功完成的信息)。 請注意,為簡單起見,我們只會將所有消息寫入信息日誌。
# src/log-begin.php <?php define('START_TIME', microtime(true)); openlog(uniqid(), LOG_ODELAY, LOG_LOCAL0); syslog(LOG_INFO, 'BEGIN'); syslog(LOG_INFO, "URI\t{$_SERVER['REQUEST_URI']}"); $browserHash = substr(md5($_SERVER['HTTP_USER_AGENT']), 0, 7); syslog(LOG_INFO, "CLIENT\t{$_SERVER['REMOTE_ADDR']}\t{$browserHash}"); <br /> # src/log-end.php <?php syslog(LOG_INFO, "DISPATCH TIME\t" . round(microtime(true) - START_TIME, 2)); syslog(LOG_INFO, 'END'); 讓我們在app.php使用這些腳本:
<?php require_once(dirname(__DIR__) . '/src/log-begin.php'); syslog(LOG_INFO, "MODE\tPROD"); # original app.php contents require_once(dirname(__DIR__) . '/src/log-end.php'); 對於開發環境,我們希望在app_dev.php中也需要這些腳本。 這樣做的代碼與上面的代碼相同,除了我們將MODE設置為DEV而不是PROD 。
我們還想跟踪正在調用哪些控制器,所以讓我們在Acme\DemoBundle\EventListener\ControllerListener中添加一行,就在ControllerListener::onKernelController()方法的開頭:
syslog(LOG_INFO, "CONTROLLER\t" . get_class($event->getController()[0]));請注意,這些更改總共只增加了 15 行代碼,但可以共同產生大量信息。
分析日誌文件中的數據
首先,讓我們看看服務每個頁面加載需要多少 HTTP 請求。
根據我們配置日誌記錄的方式,這是一個請求的日誌中的信息:
Mar 3 12:04:20 log-sandbox 54f58724b1ccc: BEGIN Mar 3 12:04:20 log-sandbox 54f58724b1ccc: URI /app_dev.php/ Mar 3 12:04:20 log-sandbox 54f58724b1ccc: CLIENT 192.168.56.1 1b101cd Mar 3 12:04:20 log-sandbox 54f58724b1ccc: MODE DEV Mar 3 12:04:23 log-sandbox 54f58724b1ccc: CONTROLLER Acme\DemoBundle\Controller\WelcomeController Mar 3 12:04:25 log-sandbox 54f58724b1ccc: DISPATCH TIME 4.51 Mar 3 12:04:25 log-sandbox 54f58724b1ccc: END Mar 3 12:04:25 log-sandbox 54f5872967dea: BEGIN Mar 3 12:04:25 log-sandbox 54f5872967dea: URI /app_dev.php/_wdt/59b8b6 Mar 3 12:04:25 log-sandbox 54f5872967dea: CLIENT 192.168.56.1 1b101cd Mar 3 12:04:25 log-sandbox 54f5872967dea: MODE DEV Mar 3 12:04:28 log-sandbox 54f5872967dea: CONTROLLER Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController Mar 3 12:04:29 log-sandbox 54f5872967dea: DISPATCH TIME 4.17 Mar 3 12:04:29 log-sandbox 54f5872967dea: END所以現在我們知道每個頁面加載實際上是由兩個 HTTP 請求提供的。
其實這裡有兩點值得一提。 首先,每個頁面加載的兩個請求是為了在開發模式下使用 Symfony(我在整篇文章中都這樣做了)。 您可以通過搜索/app-dev.php/ URL 塊來識別開發模式調用。 其次,假設每個頁面加載都由對 Symfony 應用程序的兩個後續請求提供服務。 正如我們之前在 nginx 訪問日誌中看到的,實際上還有更多的 HTTP 調用,其中一些是針對靜態內容的。
好的,現在讓我們在演示站點上瀏覽一下(在日誌文件中建立數據),看看我們還能從這些日誌中學到什麼。
自日誌文件開始以來總共服務了多少個請求?
# grep -c BEGIN info.log 10他們中的任何一個都失敗了(腳本是否在沒有到達結尾的情況下關閉了)?
# grep -c END info.log 10 我們看到BEGIN和END記錄的數量匹配,所以這告訴我們所有的調用都成功了。 (如果 PHP 腳本沒有成功完成,它就不會執行src/log-end.php腳本。)
對站點根目錄的請求百分比是多少?
# `grep -cE "\s/app_dev.php/$" info.log` 2這告訴我們站點根目錄有 2 個頁面加載。 由於我們之前了解到 (a) 每次頁面加載對應用程序有 2 個請求,並且 (b) 總共有 10 個 HTTP 請求,因此對站點根目錄的請求百分比為 40%(即 2x2/10)。
哪個控制器類負責為站點根目錄提供請求?
# grep -E "\s/$|\s/app_dev.php/$" info.log | head -n1 Mar 3 12:04:20 log-sandbox 54f58724b1ccc: URI /app_dev.php/ # grep 54f58724b1ccc info.log | grep CONTROLLER Mar 3 12:04:23 log-sandbox 54f58724b1ccc: CONTROLLER Acme\DemoBundle\Controller\WelcomeController 在這裡,我們使用請求的唯一 ID 來檢查與該單個請求相關的所有日誌消息。 因此,我們能夠確定負責向站點根目錄提供請求的控制器類是Acme\DemoBundle\Controller\WelcomeController 。
哪些 IP 為
192.168.0.0/16子網的客戶端訪問了該站點?
# grep CLIENT info.log | cut -d":" -f4 | cut -f2 | sort | uniq 192.168.56.1正如在這個簡單的測試案例中所預期的那樣,只有我的主機訪問了該站點。 這當然是一個非常簡單的例子,但它展示的能力(能夠分析您網站的流量來源)顯然非常強大和重要。
我的網站有多少流量來自 FireFox?
將1b101cd作為我的 Firefox User-Agent 的哈希值,我可以如下回答這個問題:
# grep -c 1b101cd info.log 8 # grep -c CLIENT info.log 10答案:80%(即 8/10)
產生緩慢響應的請求的百分比是多少?
出於本示例的目的,我們將“慢”定義為需要 5 秒以上才能提供響應。 因此:
# grep "DISPATCH TIME" info.log | grep -cE "\s[0-9]{2,}\.|\s[5-9]\." 2答案:20%(即 2/10)
有沒有人提供過 GET 參數?
# grep URI info.log | grep \?不,Symfony 標準只使用 URL slug,所以這也告訴我們沒有人試圖入侵該站點。
這些只是可以創造性地利用日誌文件來產生有價值的使用信息甚至基本分析的一些相對基本的示例。
其他要記住的事情
確保安全
另一個提示是為了安全。 您可能認為記錄請求是個好主意,在大多數情況下確實如此。 但是,在將任何可能敏感的用戶信息存儲到日誌之前,請務必小心刪除任何潛在的敏感用戶信息。
對抗日誌文件膨脹
由於日誌文件是您始終向其附加信息的文本文件,因此它們會不斷增長。 由於這是一個眾所周知的問題,因此有一些相當標準的方法可以控制日誌文件的增長。
最簡單的方法是輪換日誌。 輪換日誌意味著:
- 定期用新的空文件替換日誌以供進一步寫入
- 為歷史存儲舊文件
- 刪除“老化”到足以釋放磁盤空間的文件
- 確保應用程序可以在發生這些文件更改時寫入不間斷的日誌
最常見的解決方案是logrotate ,它預裝在大多數 *nix 發行版中。 讓我們看一個用於輪換日誌的簡單配置文件:
/var/log/my/debug.log /var/log/my/info.log /var/log/my/warning.log /var/log/my/error.log { rotate 7 daily missingok notifempty delaycompress compress sharedscripts postrotate invoke-rc.d rsyslog rotate > /dev/null endscript } 另一種更高級的方法是讓rsyslogd本身將消息寫入文件,根據當前日期和時間動態創建。 這仍然需要一個自定義解決方案來刪除舊文件,但讓 devops 可以精確地管理每個日誌文件的時間範圍。 對於我們的示例:
$template DynaLocal0Err, "/var/log/my/error-%$NOW%-%$HOUR%.log" $template DynaLocal0Info, "/var/log/my/info-%$NOW%-%$HOUR%.log" $template DynaLocal0Warning, "/var/log/my/warning-%$NOW%-%$HOUR%.log" $template DynaLocal0Debug, "/var/log/my/debug-%$NOW%-%$HOUR%.log" local1.err -?DynaLocal0Err local1.info -?DynaLocal0Info local1.warning -?DynaLocal0Warning local1.debug -?DynaLocal0Debug 這樣, rsyslog將每小時創建一個單獨的日誌文件,並且不需要輪換它們並重新啟動守護程序。 以下是如何刪除超過 5 天的日誌文件以完成此解決方案:
find /var/log/my/ -mtime +5 -print0 | xargs -0 rm遠程日誌
隨著項目的發展,從日誌中解析信息變得越來越需要資源。 這不僅意味著創建額外的服務器負載; 這也意味著在解析日誌時會在 CPU 和磁盤驅動器上產生峰值負載,這會降低用戶的服務器響應時間(或者在最壞的情況下甚至會導致站點停機)。
為了解決這個問題,考慮設置一個集中的日誌服務器。 為此,您只需要另一個打開 UDP 端口 514(默認)的框。 要讓rsyslogd監聽連接,請將以下行添加到其配置文件中:
$UDPServerRun 514有了這個,設置客戶端就很簡單了:
*.* @HOSTNAME:514 (其中HOSTNAME是您的遠程日誌服務器的主機名)。
結論
雖然本文展示了日誌文件可以提供比您以前想像的更有價值的信息的一些創造性方式,但重要的是要強調我們只是觸及了可能的表面。 您可以記錄的範圍、範圍和格式幾乎是無限的。 這意味著——如果你想從日誌中提取使用或分析數據——你只需要以一種易於隨後解析和分析的方式記錄它。 此外,通常可以使用標準 Linux 命令行工具(如grep 、 sed或awk )執行該分析。
事實上,PHP 日誌文件是一個最強大的工具,可以帶來巨大的好處。
資源
GitHub 上的代碼:https://github.com/isanosyan/toptal-blog-logs-post-example
附錄:在 Unix Shell 中讀取和操作日誌文件
這裡簡要介紹了一些更常見的 *nix 命令行工具,您需要熟悉這些工具來閱讀和操作日誌文件。
cat可能是最簡單的一種。 它將整個文件打印到輸出流。 例如,以下命令會將logfile1打印到控制台:cat logfile1>字符允許用戶重定向輸出,例如到另一個文件。 以寫入模式打開目標流(這意味著擦除目標內容)。 下面是我們如何用logfile1的內容替換tmpfile的內容:cat logfile1 > tmpfile>>重定向輸出並以附加模式打開目標流。 目標文件的當前內容將被保留,新行將添加到底部。 這會將logfile1內容附加到tmpfile:cat logfile1 >> tmpfilegrep按某種模式過濾文件並僅打印匹配的行。 下面的命令將只打印包含Bingo消息的logfile1行:grep Bingo logfile1cut打印單列的內容(按從 1 開始的數字)。 默認情況下,搜索製表符作為列之間的分隔符。 例如,如果您的文件中充滿了格式YYYY-MM-DD HH:MM:SS的時間戳,這將只允許您打印年份:cut -d"-" -f1 logfile1head只顯示文件的第一行tail只顯示文件的最後幾行sort對輸出中的行進行排序uniq過濾掉重複的行wc計算字數(或與-l標誌一起使用時的行數)|(即“管道”符號)將一個命令的輸出作為輸入提供給下一個命令。 管道對於組合命令非常方便。 例如,以下是我們如何找到 2014 年發生在一組時間戳中的月份的方法:grep -E "^2014" logfile1 | cut -d"-" -f2 | sort | uniq
在這裡,我們首先將行與正則表達式“從 2014 年開始”匹配,然後減少月份。 最後,我們使用sort和uniq的組合只打印一次出現。
