應用程式資訊中心模組提供了日誌、異常、效能、狀態四大服務。來看一下最新的Log Service定義:
void Debug(string message); void Debug(string moduleName, string message); void Debug(string categoryName, string subcategoryName, string message); void Debug(string moduleName, string categoryName, string subcategoryName, string message); void Debug(string message, ExtraInfo extraInfo); void Debug(string moduleName, string message, ExtraInfo extraInfo); void Debug(string categoryName, string subcategoryName, string message, ExtraInfo extraInfo); void Debug(string moduleName, string categoryName, string subcategoryName, string message, ExtraInfo extraInfo);
和之前版本的Adhesive相比,最新的Log Service增加了模組名,我們來看一下這些重載的應用範圍:
1、對於基礎模組,推薦定義模組名、大類和小類。這三個參數是級聯的:
[MongodbPresentationItem(MongodbCascadeFilterOption = MongodbCascadeFilterOption.LevelOne, DisplayName = "模組名", ShowInTableView = true)] [MongodbPersistenceItem(MongodbIndexOption = MongodbIndexOption.Ascending, ColumnName = "MDN")] public string ModuleName { get; set; } [MongodbPresentationItem(MongodbCascadeFilterOption = MongodbCascadeFilterOption.LevelTwo, DisplayName = "大類", ShowInTableView = true)] [MongodbPersistenceItem(MongodbIndexOption = MongodbIndexOption.Ascending, ColumnName = "C")] public string CategoryName { get; set; } [MongodbPresentationItem(MongodbCascadeFilterOption = MongodbCascadeFilterOption.LevelThree, DisplayName = "小類", ShowInTableView = true)] [MongodbPersistenceItem(MongodbIndexOption = MongodbIndexOption.Ascending, ColumnName = "SC")]
如果沒有定義模組名,那麼使用的是“主模組”,也就表明這個日誌是主程式的日誌,而不是其中某一個模組的日誌。
在架構內部的所有模組的日誌都定義了模組名,這樣我們很容易區分日誌的來源,比如:
AppInfoCenterService.LoggingService.Warning(MongodbServiceConfiguration.ModuleName, "InternalGetMongodbData", typeFullName, string.Format("遇到重複的列,類型:{0},列名:{1}", typeFullName, columnName));
這是我們的Mongodb資料服務記錄的日誌,我們除了定義模組名,還使用方法名來作為大類,使用資料的類型名作為小類。以便我們搜尋日誌。
對於非基礎的模組,無需指定模組名,至於大類小類可以指定,比如可以把大類設定為頁面名,把小類設定為方法名。當然,對於主模組,大類預設就是頁面名或是類名。
2、請善於利用ExtraInfo,我們知道日誌的訊息是不允許搜尋的,如果需要搜尋請通過ExtraInfo,比如:
AppInfoCenterService.LoggingService.Info(AlarmConfiguration.ModuleName, "AlarmService", "InternalHandleAlarm", string.Format("{0} {1} <= {2} 沒達到郵件發送條件", logMessgae, mailPastTime.ToString(), group.MailMessageIntervalTimeSpan.ToString()), new ExtraInfo { DropDownListFilterItem1 = "沒達到郵件發送條件", DropDownListFilterItem2 = configItem.ConfigName, });
在這裡是一條警示服務的日誌,我們把ExtraInfo中的兩個下拉框設定都設定了值,這樣我們就可以搜尋了:
同樣,異常服務的介面中也允許我們指定模組名、大類、小類以及ExtraInfo:
void Handle(Exception exception); void Handle(string moduleName, Exception exception); void Handle(string categoryName, string subcategoryName, Exception exception); void Handle(string moduleName, string categoryName, string subcategoryName, Exception exception); void Handle(Exception exception, string description); void Handle(string moduleName, Exception exception, string description); void Handle(string categoryName, string subcategoryName, Exception exception, string description); void Handle(string moduleName, string categoryName, string subcategoryName, Exception exception, string description); void Handle(Exception exception, ExtraInfo extraInfo); void Handle(string moduleName, Exception exception, ExtraInfo extraInfo); void Handle(string categoryName, string subcategoryName, Exception exception, ExtraInfo extraInfo); void Handle(string moduleName, string categoryName, string subcategoryName, Exception exception, ExtraInfo extraInfo); void Handle(Exception exception, string description, ExtraInfo extraInfo); void Handle(string moduleName, Exception exception, string description, ExtraInfo extraInfo); void Handle(string categoryName, string subcategoryName, Exception exception, string description, ExtraInfo extraInfo); void Handle(string moduleName, string categoryName, string subcategoryName, Exception exception, string description, ExtraInfo extraInfo);
在架構內部的各個模組中我們捕獲的異常也遵循這個原則,就是指定模組名,而不是使用主模組:
ex.Handle(MongodbServerConfiguration.ModuleName, "MongodbServerMaintainceCenter", "列", "建立索引出錯", new ExtraInfo { DisplayItems = new Dictionary<string, string> { {"資料庫名", databaseName }, {"表名", collectionName }, {"列名" , indexColumnDescription.ColumnName } } });
這裡,還注意到description這個參數,我們往往可以把它設定為對異常的描述,在這裡可以把一些參數資訊寫在描述中,也可以使用ExtraInfo的DisplayItems。在上面那個異常中我們使用了ExtraInfo,因為KeyValue更清晰,再來看看另外一個例子:
ex.Handle(AppInfoCenterService.ModuleName, ServiceName, "Report", string.Format("調用彙報狀態回調方法出錯,類型:{0}", configuration.TypeFullName));
異常的描述是開發人員自己提供的,往往比原始的Message更容易看出問題,比如NullReferenceException的異常訊息往往對我們沒用,只有靠我們自己:
第三個服務是狀態服務,在使用上需要注意的是,由於狀態服務其實是一個後台線程,請以靜態方式全域為一種狀態定義一個狀態服務,比如:
public class Global : System.Web.HttpApplication { private static IStateService testStateService; public static int stateValue = 100; protected void Application_Start(object sender, EventArgs e) { Adhesive.Common.AdhesiveFramework.Start(); testStateService = AppInfoCenterService.StateService; testStateService.Init(new StateServiceConfiguration("Adhesive.Test.WebApp.TestState", () => { return new List<BaseInfo> { new TestState { StateValue = stateValue, } }; })); }...
光是代碼方式定義了狀態服務還不夠,還需要在佈建服務中根據類型完整名進行相應的定義:
要注意兩點:
1、類型完整名要和代碼中寫的保持一致
2、狀態服務屬於資訊中心模組,資訊中心模組是私人的不是全域的配置,請看圖中的路徑
第四個服務是效能計算服務,使用方式如下:
protected void Performance_Click(object sender, EventArgs e) { AppInfoCenterService.PerformanceService.StartPerformanceMeasure("效能測試1"); for (int i = 0; i < 5; i++) { MethodA(); MethodB(); } } private void MethodA() { Thread.Sleep(int.Parse(SleepTime.Text)); AppInfoCenterService.PerformanceService.SetPerformanceMeasurePoint("效能測試1", "MethodA"); } private void MethodB() { for (int i = 0; i < int.Parse(CpuTime.Text); i++) { var d = 10000000; Math.Cos(d); Math.Sin(d); Math.Log10(d); Math.Sqrt(d); } AppInfoCenterService.PerformanceService.SetPerformanceMeasurePoint("效能測試1", "MethodB"); }
這裡需要注意幾個問題:
1、StartPerformanceMeasure應該在要統計的代碼一開始寫
2、SetPerformanceMeasurePoint應該在要統計的方法的最後寫
3、對於同一個效能統計,SetPerformanceMeasurePoint和StartPerformanceMeasure中的效能測試名需要保持一致
總之一定,Adhesive的資訊中心模組只是提供了一個資訊收集的渠道,如果希望自己的程式可以儘快排查定位問題,那麼需要遵循幾個實踐原則:
1、不吞吃所有異常,都調用Handle方法來處理異常,Handle不意味萬能,在必要的時候throw出異常
2、合理利用各種Log Service來記錄各種日誌,合理區分各種LogLevel,一旦日誌過多,我們可以根據LogLevel來分別開關,比如:
在這裡我們通過定義一個新的日誌策略,來關閉警示服務模組Debug日誌的遠程記錄功能
並且,我們也可以調整日誌記錄的詳細程式,比如我們希望警示模組的Info層級的日誌包含資訊策略為None,而不是主模組預設的Simple:
條件中限制了LogLevel是Info而ModuleName是警示服務模組:
3、盡量通過模組、分類來區分各種日誌和異常
4、盡量在日誌的訊息、異常的描述中體現多一點資訊,而不是簡單的一些“出錯”字樣,但要注意方法的重載,不要把分類寫到描述,把描述寫到分類
5、合理利用ExtraInfo來提供資料的篩選功能,或提交參數資訊
6、針對資訊中心模組自己記錄的網站未處理異常、應用程式定義域未處理異常設定基於統計的警示策略
7、可以針對資訊中心模組自己記錄的網站請求狀態等狀態設定基於狀態的警示策略
8、合理利用資訊中心模組的代碼效能統計服務來排查效能問題
9、對於需要索引的項,比如模組名、大類、小類,以及ExtraInfo中的一些過濾欄位,在資料庫中是做索引的,因此這些欄位應該是枚舉數量控制在一定範圍,比如100之內的,如果它們的變化範圍達到幾千甚至幾萬,那麼就不適合了