前言
在項目正式上線后,如果出現錯誤、異常、崩潰等情況,
我們往往第一想到的事就是查看日志。
所以日志對於一個系統的維護是非常重要的。
聲明
本文中的示例代碼旨在這個框架是怎么工作的,具體實現可以自由發揮。
貫穿所有的日志系統
日志系統,往往是貫穿一個程序的所有代碼的;
試想一下,如果你的日志完全是由第三方組件提供的;
那么就意味着,你的所有項目都必須引用這個dll;
也許你會說自己可以2次封裝,那么依然需要所有項目都引用你的這個封裝后的log項目,
另一方面
一些log組件需要實例化后才可以使用,比如log4net,這又意味着你得有一個全局的靜態變量,或者你自己二次封裝,
但其實微軟已經為我們提供了2個十分方便的靜態類,用於日志的記錄。
System.Diagnostics.Trace和System.Diagnostics.Debug
關於這2個類的文檔可以去看MSDN
它使用非常方便,不用引用任何dll。
調用它的方法也很簡單
using System.Diagnostics;
...
...
Trace.TraceError("這是一個Error級別的日志");
Trace.TraceWarning("這是一個Warning級別的日志");
Trace.TraceInformation("這是一個Info級別的日志");
Trace.WriteLine("這是一個普通日志");
Trace.Flush();//立即輸出
...
...
[Conditional("DEBUG")]
表明了,在Release模式下(沒有定義DEBUG常量時),該方法不會被編譯的(不是不執行,而是根本不會編譯到程序中去)。
也就是說 Debug.XXX() 方法僅在Debug模式下運行,這個又可以為我們省下很多事。
重寫日志實現
Trace和Debug中的方法的默認行為是輸出到控制台Console,和Console.Write是一樣的。
但是我們通過改變他的監聽器TraceListener,來實現更多的操作,
必須實現的方法有:
void Write(string message);
void WriteLine(string message);
不過也可以主動重寫其他方法。
隨便寫一個MyTraceListener:
class MyTraceListener : TraceListener
{
public override void Write(string message)
{
File.AppendAllText("d:\\1.log",message);
}
public override void WriteLine(string message)
{
File.AppendAllText("d:\\1.log", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + message + Environment.NewLine);
}
}
現在程序入口中初始化監聽器Trace.Listeners。
PS下:Trace和Debug的監聽器的共用的。
static void Main(string[] args)
{
Trace.Listeners.Clear(); //清除系統監聽器 (就是輸出到Console的那個)
Trace.Listeners.Add(new MyTraceListener()); //添加MyTraceListener實例
}
再隨便來個方法測試下:
private static void Test()
{
try
{
int i = 0;
Console.WriteLine(5 / i); //出現除0異常
}
catch (Exception ex)
{
Trace.TraceError("出現異常:" + ex.Message);//記錄日志
}
}
由於大部分方法都是可重寫的,所以其實最終輸出什么都是可以非常靈活。
通過配置文件初始化監聽器
通過配置文件初始化監聽器比直接寫代碼稍稍復雜一點,但是也更方便,我們可以快速的,不重新編譯系統,即可進行對日志監聽器進行設定。
演示如下:
示例:
我們將ProjectTraceListener獨立成一個項目,編譯為dll。
ProjectTraceListener.cs
1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using System.Diagnostics;
5: using System.IO;
6:
7: namespace ProjectLog
8: {
9: public class ProjectTraceListener : TraceListener
10: {
11: public string FilePath { get; private set; }
12:
13: public ProjectTraceListener(string filePath)
14: {
15: FilePath = filePath;
16: }
17:
18: public override void Write(string message)
19: {
20: File.AppendAllText(FilePath, message);
21: }
22: public override void WriteLine(string message)
23: {
24: File.AppendAllText(FilePath, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + message + Environment.NewLine);
25: }
26: public override void Write(object o, string category)
27: {
28: string message = string.Empty;
29: if (!string.IsNullOrEmpty(category))
30: {
31: message = category + ":";
32: }
33: if (o is Exception)//如果參數對象o是與Exception類兼容,輸出異常消息+堆棧,否則輸出o.ToString()
34: {
35: var ex = (Exception)o;
36: message += ex.Message + Environment.NewLine;
37: message += ex.StackTrace;
38: }
39: else if(null != o)
40: {
41: message += o.ToString();
42: }
43:
44: WriteLine(message);
45: }
46: }
47: }
在控制台項目中測試:
App.config
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.diagnostics>
4: <trace autoflush="false" indentsize="4">
5: <listeners>
6: <clear/>
7: <!--清除默認監聽器-->
8: <!--添加自定義監聽器 initializeData 就是初始化參數-->
9: <add name="ProjectTraceListener" type="ProjectLog.ProjectTraceListener, ProjectLog, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" initializeData="d:\Error.log" />
10: </listeners>
11: </trace>
12: <switches>
13: <!--這里可以設定監聽級別,可以設置Error,Warning,Info或者留空-->
14: <add name="ProjectTraceListener" value="Error" />
15: </switches>
16: </system.diagnostics>
17: </configuration>
Program.cs
1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using System.Diagnostics;
5: using ProjectLog;
6:
7: namespace ProjectLogDemo
8: {
9: class Program
10: {
11: static void Main(string[] args)
12: {
13: //刪除初始化代碼,改為在配置文件中設置
14: //Trace.Listeners.Clear(); //清除系統監聽器 (就是輸出到Console的那個)
15: //Trace.Listeners.Add(new ProjectTraceListener(@"d:\Error.log")); //添加ProjectTraceListener實例
16: Test();
17: }
18:
19: private static void Test()
20: {
21: try
22: {
23: int i = 0;
24: Console.WriteLine(5 / i); //出現除0異常
25: }
26: catch (Exception ex)
27: {
28: Trace.Write(ex, "計算員工工資出現異常");
29: }
30: }
31: }
32: }
在web項目中測試:
Web.config
1: <?xml version="1.0"?>
2: <configuration>
3:
4: <appSettings />
5: <connectionStrings />
6: <system.web>
7: <compilation debug="true">
8:
9: </compilation>
10: <!--
11: 通過 <authentication> 節可以配置
12: 安全身份驗證模式,ASP.NET
13: 使用該模式來識別來訪用戶身份。
14: -->
15: <authentication mode="Windows" />
16: <!--
17: 如果在執行請求的過程中出現未處理的錯誤,
18: 則通過 <customErrors> 節
19: 可以配置相應的處理步驟。具體而言,
20: 開發人員通過該節可配置要顯示的 html 錯誤頁,
21: 以代替錯誤堆棧跟蹤。
22:
23: <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
24: <error statusCode="403" redirect="NoAccess.htm" />
25: <error statusCode="404" redirect="FileNotFound.htm" />
26: </customErrors>
27: -->
28:
29: </system.web>
30: <system.diagnostics>
31: <trace autoflush="false" indentsize="4">
32: <listeners>
33: <clear/>
34: <!--清除默認監聽器-->
35: <!--添加自定義監聽器 initializeData 就是初始化參數-->
36: <add name="ProjectTraceListener" type="ProjectLog.ProjectTraceListener, ProjectLog, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" initializeData="d:\Error.log" />
37: </listeners>
38: </trace>
39: <switches>
40: <!--這里可以設定監聽級別,可以設置Error,Warning,Info或者留空-->
41: <add name="ProjectTraceListener" value="Error" />
42: </switches>
43: </system.diagnostics>
44: </configuration>
Default.aspx
1: using System;
2: using System.Collections.Generic;
3: using System.Web;
4: using System.Web.UI;
5: using System.Web.UI.WebControls;
6: using System.Diagnostics;
7: using ProjectLog;
8:
9: namespace ProjectLogDemoByWeb
10: {
11: public partial class _Default : System.Web.UI.Page
12: {
13: protected void Page_Load(object sender, EventArgs e)
14: {
15: //刪除初始化代碼,改為在配置文件中設置
16: //System.Diagnostics.Trace.Listeners.Clear(); //清除系統監聽器 (就是輸出到Console的那個)
17: //System.Diagnostics.Trace.Listeners.Add(new ProjectLog.ProjectTraceListener(@"d:\Error.log")); //添加ProjectTraceListener實例
18: Test();
19: }
20:
21: private static void Test()
22: {
23: try
24: {
25: int i = 0;
26: Console.WriteLine(5 / i); //出現除0異常
27: }
28: catch (Exception ex)
29: {
30: System.Diagnostics.Trace.Write(ex, "計算員工工資出現異常");
31: }
32: }
33: }
34: }
配置文件中的type參數可以這樣獲得:
typeof(MyLog.MyTraceListener).AssemblyQualifiedName
拓展
以log4net為例說明此框架對其他log系統的引用。
public class ProjectTraceListener : TraceListener
{
log4net _log = new log4net();
public MyTraceListener(string filepath)
{
_log = new log4net();
_log.FilePath = filepath;
}
public override void Write(string message)
{
_log.Info(message);
}
public override void WriteLine(string message)
{
_log.Info(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + message + Environment.NewLine);
}
}
原文來自:冰麟輕武的博客園