通常來講,對於什麼時候寫日誌並無明確的限制和約束,只要你以爲記錄的日誌是有價值的,對跟蹤bug是有幫助的,你就能夠去添加日誌。固然一些敏感信息除外,好比你正在開發一套支付系統,不要把客戶的卡號和密碼等信息記錄在日誌中,由於日誌並不會被刻意保護,有可能被其餘的用戶羣體收集到。
另外不要擔憂大量的日誌會對服務器形成壓力,通常來講在產品環境都會採用消息隊列配合搜索引擎的方式存儲日誌,經過定義準確的日誌級別也會減小生產環境的日誌數量。java
以log4net爲例,在想要添加日誌的任何地方使用下列的方法添加日誌:git
private readonly ILog log = log4net.LogManager.GetLogger(typeof(T)); log.Info("message");
通常來講日誌組件都會有Debug
,Info
,Warn
,Error
,Fatal
五種日誌級別,其中Fatal
用來處理Unhandled exception
,所以對開發者而言在開發過程當中只有四種可用的日誌級別:github
以log.Error()舉例:服務器
log.Error(string message)
的時機if (string.IsNullOrEmpty(userName)) { var message = "user name is empty"; logger.Error(message); throw new UserNameEmptryException(); }
上面的使用方式是顯而易見的,業務代碼發現用戶名爲空,記錄日誌同時拋出異常,上面的場景不能使用下面的方式記錄日誌:網絡
logger.Error(new Exception(message));
選擇錯誤的重載會致使日誌調用堆棧丟失,日誌變得再也不完整,最終會在查找日誌的時候浪費時間。框架
log.Error(Exeception exception)
的時機在捕獲到異常的時候使用此重載:asp.net
try { var response = AlipayHandler.Pay(); } catch (AlipayRequestException e) { logger.Error(e); throw; }
log.Error(string message, Exception exception)
的時機上面的場景也符合這個重載,區別在於你不但想記錄異常信息,還想添加自定義的信息。ide
通常來講使用不一樣的框架,添加Unhandler exception handler的方式也不一樣。搜索引擎
public class UnhandledExceptionFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { var logger = LoggerFactory.GetLogger(typeof(UnhandledExceptionFilterAttribute)); var exception = filterContext.Exception; logger.Fatal("Unhandled exception", exception); base.OnActionExecuting(filterContext); } }
AppDomain.CurrentDomain.UnhandledException += (sender, e) => { logger.Fatal("Unhandled exception", exception);; };
總之,開發不一樣類型的應用程序都有相對應的方法來處理unhandled exception,使用時搜索最佳實踐便可。url
private void RequestAlipay() { try { var response = httpClient.Request(url); } catch (Exception e) { logger.Error(e); } }
上面記錄日誌的方式犯了兩個錯誤:
HttpRequestException
而不是Exception
。所以正確的寫法以下:private void RequestAlipay() { try { var response = httpClient.Request(url); } catch (HttpRequestException e) { logger.Error(e); } }
你並不知道如何處理其餘類型的異常,所以你不該該捕獲其餘類型的異常。
private void RequestAlipay() { try { var response = httpClient.Request(url); } catch (HttpRequestException e) { logger.Error(e); throw; } }
注意: 不要經過下面的方式拋出異常,由於這樣會丟掉調用堆棧Why catch and rethrow an exception in C#?
throw ex;
好比在一個ProductSellingService中:
public void Sell(string itemId) { Product product; try { car = GetProducts().Single(x => x.ItemId.Contains(itemId)); } catch (InvalidOperationException) { log.Error($"product {itemId} has been sold out.") throw new ProductsHasBeenSoldOutException(); } }
當用戶要購買的產品已經不存在時,除了記錄一條日誌,還會拋出一個ProductsHasBeenSoldOutException異常。由於做爲ProductSellingService並不知道如何處理這種異常,因此須要上層的調用者作決策。
在ProductSellingController中使用了ProductSellingService:
try { ProductSellingService.Sell(id); var response = Request.CreateResponse(HttpStatusCode.OK, "successful"); } catch (ProductsHasBeenSoldOutException e) { var response = Request.CreateResponse(HttpStatusCode.NotFound, "product is been sold out"); return response; }
ProductSellingController知道如何處理ProductsHasBeenSoldOutException,最終返回給用戶一個錯誤消息。
準確的使用日誌能夠方便bug追蹤,數據分析,達到事半功倍的效果,相反,錯誤的日誌使用方式只會讓開發者在調查緣由的過程當中浪費時間,下降效率。本文將重點描述日誌的代碼設計部分,在正式環境還要考慮如何經過消息隊列和搜索引擎存儲海量日誌,以及日誌監控等解決方案的設計,敬請期待後續文章。