# 快速入門(八):調試和日志
項目開發的時候,出現錯誤在所難免,最大的困惑在于發現問題所在,其次才是如何解決問題。因此懂得如何調試和跟蹤問題非常之關鍵,`5.0`版本提供了非常方便的調試工具和手段,讓你更容易定位和發現問題。
本篇授你`TP5`調試大法之獨門五式,稍加修煉,則可讓你的TP5調試功力獨步天下。
- - [第一式:未雨綢繆——頁面Trace](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/192760#-trace)
- [第二式:初見端倪——異常頁面](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/192760#--1)
- [第三式:撥云見日——斷點調試](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/192760#--2)
- [第四式:欲窮千里——日志分析](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/192760#--3)
- [第五式:運籌帷幄——遠程調試](http://www.hmoore.net/thinkphp/thinkphp5_quickstart/192760#--4)
## 第一式:未雨綢繆——頁面Trace
> 第一式是基本式,簡單易學,但往往容易被忽視。
> 難度系數:0
> 實用指數:6
頁面`Trace`是`ThinkPHP`經典的調試手段,`ThinkPHP5.0`繼續發揚光大,已經可以支持不依賴頁面顯示。
頁面`Trace`的主要作用包括:
- 查看運行數據;
- 查看文件加載情況;
- 查看運行流程;
- 查看當前執行SQL;
- 跟蹤調試數據;
- 查看頁面錯誤信息;
系統默認不開啟頁面`Trace`,開啟頁面`Trace`是在應用配置文件中設置下面的參數:
```
<pre class="calibre18">
```
<span class="hljs-comment">// 開啟應用Trace調試</span><span class="hljs-string">'app_trace'</span> => <span class="hljs-keyword">true</span>,
<span class="hljs-comment">// 設置Trace顯示方式</span><span class="hljs-string">'trace'</span> => [
<span class="hljs-comment">// 在當前Html頁面顯示Trace信息</span><span class="hljs-string">'type'</span> => <span class="hljs-string">'html'</span>,
],
```
```
我們用默認自帶的歡迎頁面來測試,訪問后就可以看到頁面`Trace`信息按鈕。

注意看在右下角可以看到一個`ThinkPHP`的`LOGO`和當前頁面的運行時間,  點擊會彈出詳細的`Trace`信息,如圖:

這是系統默認的`Trace`信息顯示效果,包含了基本、文件、流程、錯誤、`SQL`和調試信息。
**基本信息**一欄顯示了當前請求的運行信息,包括運行時間、吞吐率、內存開銷和文件加載等基本信息,通過這個頁面可以對當前的請求有一個直觀的了解,例如當前請求的內存開銷是否過大,查詢次數是否在合理的范圍之內等等。
**文件信息**一欄則按加載順序顯示了當前請求加載的文件列表。
**流程信息**一欄則會顯示當前請求做了哪些操作,大家可以在開發的過程中經常關注下不同頁面的請求和區別,該欄的內容開啟調試模式后可見。
**錯誤信息**一欄會顯示頁面執行過程中的相關錯誤,包括警告錯誤(由于ThinkPHP5.0默認情況下對錯誤零容忍,所以你在正常情況下基本看不到任何錯誤,因為有任何錯誤都會直接拋出異常)。
如果你當前請求使用了數據庫操作和查詢的話,并且開啟了數據庫調試模式(注意,數據庫調試模式可以單獨開啟,在數據庫配置文件中配置開啟`debug`參數)會在`SQL`一欄顯示相關的`SQL`連接和查詢信息,如圖:

對于一些性能不高的查詢尤其要引起注意,及早進行優化,如果要查看每個`SQL`查詢的`EXPLAIN`信息,可以在數據庫配置文件中設置`sql_explain`參數如下:
```
<pre class="calibre18">
```
<span class="hljs-comment">// 是否需要進行SQL性能分析</span><span class="hljs-string">'sql_explain'</span> => <span class="hljs-keyword">true</span>,
```
```
開啟后,我們就可以看到SQL語句的下面增加了`EXPLAIN`分析信息:

最后一欄是用于開發過程的調試輸出,使用`trace`方法調試輸出的信息不會在頁面直接顯示,而是在頁面`Trace`的調試一欄顯示輸出。
我們在控制器的方法中增加
```
<pre class="calibre18">
```
trace(<span class="hljs-string">'這是測試調試信息'</span>);
trace([<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>]);
```
```
然后刷新頁面會看到:

如果你不希望影響頁面的輸出效果,可以啟用瀏覽器Trace調試信息,設置如下:
```
<pre class="calibre18">
```
<span class="hljs-comment">// 開啟應用Trace調試</span><span class="hljs-string">'app_trace'</span> => <span class="hljs-keyword">true</span>,
<span class="hljs-comment">// 設置Trace顯示方式</span><span class="hljs-string">'trace'</span> => [
<span class="hljs-comment">// 使用瀏覽器console顯示頁面trace信息</span><span class="hljs-string">'type'</span> => <span class="hljs-string">'console'</span>,
],
```
```
設置后,用`Chrome`瀏覽器打開控制臺切換到`console`可以看到如下顯示:

控制臺方式依然可以查看基本、文件、流程、錯誤、SQL和調試欄的相關信息。
并且支持顏色顯示,例如:

現在你已經基本了解了頁面`Trace`的作用了,在開發過程中,開啟頁面`Trace`顯示可以讓你及早了解系統運行狀況和及時排查隱患,起到未雨綢繆的作用。
> Ajax方式請求的信息不會在頁面Trace中顯示,還包括部分頁面Trace之后執行的日志信息也無法在Trace中查看到。
## 第二式:初見端倪——異常頁面
> 第二式是臨危式,一旦遇“敵”方出此招,必須有波瀾不驚的氣勢方能駕馭自如。
> 難度系數:3
> 實用指數:8
前面已經提到,`ThinkPHP5.0`對錯誤非常嚴謹,默認情況下,任何的錯誤(包括警告錯誤)系統都會拋出異常。
為了培養實戰經驗,讓我們來和異常進行一次親密接觸吧,下面的代碼用了一個未定義索引`$_GET['name']`:
```
<pre class="calibre18">
```
<span class="hljs-keyword">namespace</span> <span class="hljs-title">app</span>\<span class="hljs-title">index</span>\<span class="hljs-title">controller</span>;
<span class="hljs-operator"><span class="hljs-keyword">class</span> <span class="hljs-title">Index</span></span>{
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span><span class="hljs-number">()</span></span>{
<span class="hljs-keyword">return</span> <span class="hljs-string">'hello,'</span>.<span class="hljs-regexp">$_GET</span>[<span class="hljs-string">'name'</span>];
}
}
```
```
訪問 `http://tp5.com` 后顯示:

如果你的系統是英文的話,會自動顯示英文提示:

異常頁面除了顯示錯誤信息之外,還有很多的額外的信息能夠幫助你快速定位和發現問題所在。
第一步是查看異常所在的文件源碼,錯誤行會用紅色標識,如果要繼續一探究竟,那么就需要查看下面的`Call Stack`信息,這個是顯示了當前異常的詳細Trace信息,帶下劃線樣式的地方,鼠標移上去會停留會顯示該文件的詳細位置

這些信息還不夠,不要急,仔細查看完整的異常頁面,其實還包含了當前請求的全局變量和常量,如圖:

遇到異常頁面后要沉著冷靜的分析錯誤信息,根據經驗,第二式可以解決80%的問題定位,調試大神一般會在這個階段把問題找出。
如若仍然無從下手的,請繼續修煉第三式。
## 第三式:撥云見日——斷點調試
> 第三式為破敵式,不斷設置斷點縮小和定位錯誤范圍,直到找到錯誤位置。雖然簡單粗暴,但對于復雜的情況下排查錯誤非常實用,并且如何設置斷點有很多的經驗。
> 難度系數:6
> 實用指數:9
系統為斷點調試提供了幾個有用的方法,訣竅就是通過調試信息不斷縮小問題的范圍以及診斷變量的變化。
### `dump` 變量調試輸出
在你需要的斷點位置,調用`dump`方法,可以輸出瀏覽器友好的變量信息,支持任何變量類型,例如:
```
<pre class="calibre18">
```
<span class="hljs-keyword">dump</span>(<span class="hljs-string">'測試'</span>);
<span class="hljs-keyword">dump</span>([<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>]);
<span class="hljs-keyword">dump</span>(User::get());
```
```
頁面會輸出類似下面的信息:

### `halt` 變量調試并中斷輸出
`halt`方法的作用和`dump`一樣,只是在輸出變量之后會中斷當前程序的執行,下面是示例代碼:
```
<pre class="calibre18">
```
<span class="hljs-keyword">dump</span>(<span class="hljs-string">'測試'</span>);
halt([<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>]);
halt(<span class="hljs-string">'這里的信息是不會輸出的'</span>);
```
```
頁面會輸出:
```
<pre class="calibre18">
```
E:\www\tp\thinkphp\<span class="hljs-keyword">library</span>\think\Debug.php:<span class="hljs-number">168</span>:<span class="hljs-keyword">string</span> <span class="hljs-string">'測試'</span> (length=<span class="hljs-number">6</span>)
E:\www\tp\thinkphp\<span class="hljs-keyword">library</span>\think\Debug.php:<span class="hljs-number">168</span>:
<span class="hljs-keyword">array</span> (size=<span class="hljs-number">3</span>)
<span class="hljs-number">0</span> => <span class="hljs-keyword">string</span> <span class="hljs-string">'a'</span> (length=<span class="hljs-number">1</span>)
<span class="hljs-number">1</span> => <span class="hljs-keyword">string</span> <span class="hljs-string">'b'</span> (length=<span class="hljs-number">1</span>)
<span class="hljs-number">2</span> => <span class="hljs-keyword">string</span> <span class="hljs-string">'c'</span> (length=<span class="hljs-number">1</span>)
```
```
第二個`halt`方法不會被執行,因為`halt`方法在執行后就中止程序了。
### `trace` 控制臺輸出
如果你不希望在頁面輸出調試信息,可以使用`trace`方法,該方法輸出的信息會在頁面`Trace`或者瀏覽器`Console`中顯示,使用方法和`dump`是一樣的。
```
<pre class="calibre18">
```
trace(<span class="hljs-string">'測試'</span>);
trace([<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>]);
trace(<span class="hljs-string">User:</span>:get());
```
```
輸出效果如圖:

利用好這三個調試方法,足以臨場殺敵,百萬“大軍”中取`BUG`首級!
## 第四式:欲窮千里——日志分析
> 第四式為診斷式,意為經常追溯日志信息,協同分析錯誤原因。
> 難度系數:6
> 實用指數:7
系統記錄的日志信息在很多時候能夠發揮意想不到的作用,默認情況下,系統使用文件方式記錄日志,并且按照日期自動分開子目錄保存,文件結構如下:
```
<pre class="calibre18">
```
runtime/<span class="hljs-number">log</span>
└─<span class="hljs-number">201608</span><span class="hljs-number">05.l</span>og
<span class="hljs-number">06.l</span>og
<span class="hljs-number">08.l</span>og
<span class="hljs-number">09.l</span>og
<span class="hljs-number">10.l</span>og
<span class="hljs-number">11.l</span>og
<span class="hljs-number">12.l</span>og
```
```
每天會生成一個當天的日志文件,日志文件的內容和頁面`Trace`信息的內容類似,但區別在于所有的請求都會記錄,因此日志信息包含了大量的信息,更加有利于我們分析問題。
ThinkPHP對系統的日志按照級別來分類,并且這個日志級別完全可以自己定義,系統內部使用的級別包括:
- **log** 常規日志,用于記錄日志
- **error** 錯誤,一般會導致程序的終止
- **notice** 警告,程序可以運行但是還不夠完美的錯誤
- **info** 信息,程序輸出信息
- **debug** 調試,用于調試信息
- **sql** SQL語句,用于SQL記錄,只在數據庫的調試模式開啟時有效
系統提供了不同日志級別的快速記錄方法,例如:
```
<pre class="calibre18">
```
<span class="hljs-operator"><span class="hljs-title1">Log</span>:<span class="hljs-string">:<span class="hljs-function">error</span>(<span class="hljs-operator">'錯誤信息'</span>)</span></span>;
<span class="hljs-operator"><span class="hljs-title1">Log</span>:<span class="hljs-string">:<span class="hljs-function">info</span>(<span class="hljs-operator">'日志信息'</span>)</span></span>;
```
```
或者使用`trace`方法記錄日志
```
<pre class="calibre18">
```
trace(<span class="hljs-string">'錯誤信息'</span>,<span class="hljs-string">'error'</span>);
trace(<span class="hljs-string">'日志信息'</span>,<span class="hljs-string">'info'</span>);
```
```
為了便于分析,還支持設置某些級別的日志信息單獨文件記錄,例如:
```
<pre class="calibre18">
```
<span class="hljs-string">'log'</span> => [
<span class="hljs-string">'type'</span> => <span class="hljs-string">'file'</span>,
<span class="hljs-comment">// error和sql日志單獨記錄</span><span class="hljs-string">'apart_level'</span> => [<span class="hljs-string">'error'</span>,<span class="hljs-string">'sql'</span>],
],
```
```
設置后,就會單獨生成`error` 和 `sql`兩個類型的日志文件,主日志文件中將不再包含這兩個級別的日志信息,日志文件結構類似于:
```
<pre class="calibre18">
```
runtime/<span class="hljs-number">log</span>
└─<span class="hljs-number">201608</span><span class="hljs-number">05.l</span>og
<span class="hljs-number">06.l</span>og
<span class="hljs-number">08.l</span>og
<span class="hljs-number">09.l</span>og
<span class="hljs-number">10.l</span>og
<span class="hljs-number">11.l</span>og
<span class="hljs-number">12.l</span>og
<span class="hljs-number">12</span>_error.<span class="hljs-number">log</span><span class="hljs-number">12</span>_sql.<span class="hljs-number">log</span>
```
```
定期查看系統的日志文件便于及時發現一些可能存在的隱患,以及給已有的問題提供更多的參考依據。
日志文件可以使用`Socket`方式記錄到遠程服務器,這就是后面要講的遠程調試功能,當然,你還可以擴展自己的日志記錄方式來滿足更多的要求。
## 第五式:運籌帷幄——遠程調試
> 第五式為獨步式,一旦修煉完成,千里之外任何錯誤盡在掌握。
> 難度系數:6
> 實用指數:8
遠程調試功能暫且不表,我們將會在下一章`API`開發中為你詳細講述。
勤加修煉并掌握好這`ThinkPHP5.0`的五式調試大法,任何錯誤都會被你秒破!
- 脕茫隆壟脨貌脩脭
- 脕茫隆壟脨貌脩脭
- 脪祿隆壟祿霉麓隆
- 脪祿隆壟祿霉麓隆
- 露鎂隆壟URL潞脥脗路脫脡
- 露鎂隆壟URL潞脥脗路脫脡
- 脠媒隆壟脟毛脟貿潞脥脧矛脫婁
- 脠媒隆壟脟毛脟貿潞脥脧矛脫婁
- 脣脛隆壟脢媒戮脻驢芒
- 脣脛隆壟脢媒戮脻驢芒
- 脦氓隆壟虜茅脩爐脫茂脩脭
- 脦氓隆壟虜茅脩爐脫茂脩脭
- 脕霉隆壟脛攏脨脥潞脥鹿脴脕陋
- 攏簍1攏漏脛攏脨脥露簍脪氓
- 攏簍2攏漏祿霉麓隆虜脵脳梅
- 攏簍3攏漏露脕脠隆脝梅潞脥脨脼賂脛脝梅
- 攏簍4攏漏脌脿脨脥脳陋祿祿潞脥脳脭露爐脥錨魯脡
- 攏簍5攏漏虜茅脩爐路露脦摟
- 攏簍6攏漏脢盲脠毛潞脥脩茅脰隴
- 攏簍7攏漏鹿脴脕陋
- 攏簍8攏漏脛攏脨脥脢盲魯枚
- 脝脽隆壟脢脫脥錄潞脥脛攏擄氓
- 脝脽隆壟脢脫脥錄潞脥脛攏擄氓
- 擄脣隆壟碌梅脢脭潞脥脠脮脰戮
- 擄脣隆壟碌梅脢脭潞脥脠脮脰戮
- 戮脜隆壟API驢陋路壟
- 戮脜隆壟API驢陋路壟
- 脢廬隆壟脙眉脕卯脨脨鹿隴戮脽
- 脢廬隆壟脙眉脕卯脨脨鹿隴戮脽
- 脢廬脪祿隆壟脌漏脮鹿
- 脢廬脪祿隆壟脌漏脮鹿
- 脢廬露鎂隆壟脭脫脧卯
- Cookie
- Session
- 碌樓脭陋虜芒脢脭
- 脥錄脧帽麓婁脌鉚
- 脦脛錄鎂脡脧麓蘆
- 脩茅脰隴脗毛
- 賂陸脗錄
- A隆壟魯攏錄沒脦脢脤芒錄爐
- B隆壟3.2潞脥5.0脟酶鹵冒
- C隆壟脰煤脢脰潞爐脢媒
- 路盧脥芒脝陋攏潞脩摟脧擄ThinkPHP5碌脛脮媒脠路脳脣脢脝
- 路盧脥芒脝陋攏潞脩摟脧擄ThinkPHP5碌脛脮媒脠路脳脣脢脝