引言
接觸過php框架的朋友們可能都知道,日誌在專案中的重要角色了,他可以幫助我們定位錯誤的位置,讓程式更友善(處理得當的話不會直接拋出一大堆只有程式猿才真正動的英文),調試的時候也會很方便,還可以記錄一些重要的操作等等,總之一個完整的項目要是沒了日誌系統,就已經開發的路上佈滿了荊棘、坑洼,一定會磕磕絆絆的。
簡介
要掌握PHP日誌系統,必須先對這幾點了解透徹。
一、php的幾個函數
set_exception_handler(callback $exception_handler); //异常捕获自定义处理函数注册 set_error_handler(callback $error_handler); //错误捕获自定义处理函数注册 register_shutdown_function(callback $callback); //程序执行时异常终止错误捕获处理函数注册
這三個函數在錯誤處理控制中給了開發者很大的自主空間,在日誌系統中記錄日誌資訊有他們的功勞。
在程式中出現異常(exception)問題時,php核心會拋出異常錯誤,然後將錯誤訊息列印給使用者,如果註冊了exception處理函數,php拋出的異常會轉給自定義的註冊的異常捕獲函數,這個函數裡麵包含了我們要做的處理,記錄錯誤訊息(包括錯誤詳細內容、錯誤位置),該函數處理完異常後,異常就會終止。
當程式中出現error時,我們註冊的error處理函數會在函數中將錯誤訊息轉換為錯誤異常物件傳遞給異常處理函數,也就是第一步的$exception_handler函數。
當成續重出現shutdown錯誤時,會執行我們註冊的異常終止處理函數,該函數透過error_get_last()取得到最後的shutdown時的錯誤對象,接著和上一部一樣,產生一個error exception對象,將該物件傳遞給我們註冊的異常處理函數。
可以看到,其實不管是異常還是錯誤,都是將自己的訊息轉化為異常處理函數認識的異常訊息,然後交給異常處理函數處理,非異常訊息就像化了妝的就像女人一樣,異常處理程序不認識這些非異常訊息,只有將裝卸掉(非異常訊息自己轉化為異常訊息,準確的說應該是拋出),異常處理才認識。
php日誌系統中的錯誤處理流程
那麼現在問題來了,這幾個函數一般會配合一個異常處理類別庫,加上一個錯誤日誌記錄類別庫來進行工作,異常處理類別庫中包含要註冊的3個函數,日誌記錄類別庫在$exception_handler中調用,用來合理的記錄和放置日誌檔案的位置,上面說到的幾個函數一般是在程式框架入口處進行載入註冊的,就像下面這樣:
這裡面用的是array(class,function)這種方式。
set_exception_handler(array("Myexception","exceptionHandler")); set_error_handler(array("Myexception","errorHandler")); register_shutdown_function(array("Myexception","shutdownHandler"));
二、日誌記錄相關類別庫
第一部分介紹到的東西只是對異常、錯誤、shutdown進行了捕獲,這只是第一步,接下來也要對捕獲到的資訊進行合理的處理,比如說記錄這些日誌資訊到本地文件系統中(這個操作是在array(“Myexception”,”exceptionHandler”)),這個地方就用到了日誌記錄類庫。 (下面要說的類別庫是藉用了kohana日誌系統的設計)。
日式日誌記錄也很簡單只要做的將資訊添加到文件末尾就行,這個很容易實現,相信大家都可以自己實現,但是要設計出一個便捷的、高效的、擴展的日誌記錄類別庫就不那麼簡單了,要經過長時間的實踐總結優化才可以,kohana框架中的日誌記錄類別庫已經比較成熟了,因此這裡拿來借鑒。
相信使用過kohana的使用者一定對kohana框架中的日誌記錄比較熟悉,不熟悉也沒關係,我下面會大概的說說,在kohana源碼中的application/bootstrap.php檔案中的第109——112行可以看到下面的程式碼:
/** * Attach the file write to logging. Multiple writers are supported. */ Kohana::$log->attach(new Log_File(APPPATH.'logs'));
這個就是添加一個日誌記錄物件到日誌物件中,注意橄欖色打底的兩個,他們是不同的類別庫實例,在kohana中,日誌記錄對象分為兩部分,第一部分就是日誌對象,用來維護一個日誌記錄對象的列表,這個要怎麼理解呢,其實就像一個容器,裡麵包含了一個或多個日誌記錄對象(這個就是第二部分,這些日誌記錄物件才是真正來記錄日誌的),還有每個物件要記錄的錯誤等級的數組,當滿足錯誤等級的時候才會去記錄,不滿足就會略掉。以下是我自己簡化重命名後的日誌記錄方式:
self::$log = Log::instance(); self::$log->attach(new Logwriter("./data/debug"),Log::DEBUG); self::$log->attach(new Logwriter("./data/notice"),Log::NOTICE);
我这里面为了更好地理解,将“容器”命名为Log,记录的实例命名为Logwriter,可以看到我在程序入口处很容易的添加了两不同的日志种类,第一个是记录所有错误号比Log::DEBUG小的错误(错误级别比他高),并按规则记录在文件夹./data/debug下面,第二个是记录级别等于或高于Log::NOTICE的错误,当然了你还可以更详细制定具体哪些错误好,传递数组就行了,这个就是我感觉方便、快捷的地方,我们可以根据需求来添加错误日志、分不同的日志目录,下面看一幅图也许会有助于理解:
log与logwriter的关系
通过上面的图你就会看到Log是一个容器,包含了具体的不同的logwriter对象,每个对象可能要记录不同的信息,当错误信息要刷到文件中的时候,会运行每一个Logwriter实例,看看自己是否要记录errormessage中的错误,errormessage中的level不包含在Logwriter内时忽略。
这本分和第一部分怎么合作的呢?
其实很简单,当exception捕获的异常时会调用添加一条错误信息(包括错误位置、错误代号、错误信息等信息)到Log容器中的errormessage数组中,然后当程序结束之后在将这些信息写入文件,这里还要注意下,也许你在阅读kohana代码是发现没有明显的直接写入到日志中去,这里面kohana优化的比较好,因为php的一次执行可能出现多个错误,如果来一个错误你就去记录一次这样会在程序返回之前占用多余的io和时间,所以kohana的做法是默认将所有的错误、异常、日志存放在Log::$errormessage中,并在实例化的时候讲Log中的writer操作注册register_shutdown_function,这个函数的作用是在程序异常终止或者执行完成之后执行,前面第一部分也有使用到,这样日志记录就不会对本次php的执行产生带大的影响。
推荐:《PHP培训》
以上是關於PHP框架中日誌系統的詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!