1. 程式人生 > >你的日誌元件記錄夠清晰嘛?--自己開發日誌元件 Logger

你的日誌元件記錄夠清晰嘛?--自己開發日誌元件 Logger

現在現成的日誌元件實在是太多太多,為什麼我還需要自己實現呢?????

需求來源於java的log4j,

[07-31 16:40:00:557:WARN : com.game.engine.thread.ServerThread:117] -> 全域性排行榜同步執行器-->ServerThread[全域性排行榜同步執行器]執行 執行時間過長:23

簡單的一句日誌資訊,但是我卻可以很清晰的定位日誌輸出的程式碼位置;com.game.engine.thread包下面的ServerThread這個類檔案的第117行;

而log4net卻不行,

也許是因為我米有找到正確的對應配置檔案、可惜吧~!

於是我打算自己重組日誌元件來實現清洗的日誌記錄。當然你也可以說,.net平臺下面的exception拋錯的話日誌也很清晰。

但是有時候我們邏輯錯誤或者是引數錯誤,根本不會拋錯,這種情況下我們沒有得到預期結果的時候只能通過簡單除錯找出原因。

那麼很明顯費時,費力,所以我就想得到像log4j那樣打印出清晰的日誌。

可能你會問有這個必要嘛?很有必要哦,比如你程式上線一個版本,然後列印的資訊和現在最新版本行號已經不符合了,資料流向和清晰度自然而然就不存在多大的意義~!

如果需要找到這樣清晰的日誌。那麼就需要得到方法的呼叫堆疊資訊。

查閱文件發現 System.Diagnostics.StackTrace 類是記錄堆疊資訊的

於是開始無盡的測試之行

 1 namespace Sz.StackTraceTest
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Test();
 8             Console.ReadLine();
 9         }
10 
11         static public void Test()
12         {
13             ShowStackTrace();
14 } 15 16 static public void ShowStackTrace() 17 { 18 19 StackTrace trace = new StackTrace(); 20 var frames = trace.GetFrames(); 21 foreach (var item in frames) 22 { 23 Console.WriteLine(string.Format("[{0},檔案{1},方法{2},行{3}]", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), item.GetFileName(), item.GetMethod(), item.GetFileLineNumber())); 24 } 25 } 26 27 } 28 }

執行後發現:

並未得到檔案等資訊,為啥會這樣

        // 摘要: 
        //     用呼叫方的幀初始化 System.Diagnostics.StackTrace 類的新例項。
        public StackTrace();
        //
        // 摘要: 
        //     用呼叫方的幀初始化 System.Diagnostics.StackTrace 類的新例項,可以選擇捕獲源資訊。
        //
        // 引數: 
        //   fNeedFileInfo:
        //     如果為 true,則捕獲檔名、行號和列號;否則為 false。
        public StackTrace(bool fNeedFileInfo);

查詢了一下StackTrace類的過載得知,應該是預設建構函式並未捕獲資訊

那麼重來一次

這次取到了。可是意外的發現,記錄的檔案居然 是全路徑。,

顯然這不是我想要的結果。

那麼再試試看有麼有其他路徑。

於是想到,既然有函式,那麼可以通過函式入手啊,函式,肯定有父節啊。

        //
        // 摘要: 
        //     獲取宣告該成員的類。
        //
        // 返回結果: 
        //     宣告該成員的類的 Type 物件。
        public abstract Type DeclaringType { get; }

於是找到這個屬性,宣告該函式的物件。那麼肯定會得到型別的完全限定名稱了;

 1 namespace Sz.StackTraceTest
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Test();
 8             Console.ReadLine();
 9         }
10 
11         static public void Test()
12         {
13             ShowStackTrace();
14         }
15 
16         static public void ShowStackTrace()
17         {
18 
19             StackTrace trace = new StackTrace(true);
20             var frames = trace.GetFrames();
21             foreach (var item in frames)
22             {
23                 Console.WriteLine(string.Format("[{0},檔案{1},方法{2},行{3}]", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), item.GetMethod().DeclaringType.FullName, item.GetMethod(), item.GetFileLineNumber()));
24             }
25         }
26 
27     }
28 }

看看那執行結果

得到名稱空間和型別名了。

但是圖中我們看到,其實我只想Test()中輸出列印的地方加入一個而已,

但是打印出了整個走向,這個時候我們是普通列印日誌根本不需要整個的流程走向

再次檢視StackTrace類還有一個過載方式

 1         //
 2         // 摘要: 
 3         //     從呼叫方的幀初始化 System.Diagnostics.StackTrace 類的新例項,跳過指定的幀數並可以選擇捕獲源資訊。
 4         //
 5         // 引數: 
 6         //   skipFrames:
 7         //     堆疊中的幀數,將從其上開始跟蹤。
 8         //
 9         //   fNeedFileInfo:
10         //     如果為 true,則捕獲檔名、行號和列號;否則為 false。
11         //
12         // 異常: 
13         //   System.ArgumentOutOfRangeException:
14         //     skipFrames 引數為負數。
15         public StackTrace(int skipFrames, bool fNeedFileInfo);

跳過指定幀;

 1 namespace Sz.StackTraceTest
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Test();
 8             Console.ReadLine();
 9         }
10 
11         static public void Test()
12         {
13             Log("test");
14             Log("test");
15             Log("test");
16         }
17 
18         static public void Log(string msg)
19         {
20             StackTrace trace = new StackTrace(1, true);
21             if (trace.GetFrames().Length > 0)
22             {
23                 var item = trace.GetFrame(0);
24                 Console.WriteLine(string.Format("[{0},檔案{1},方法{2},行{3}]:{4}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), item.GetMethod().DeclaringType.FullName, item.GetMethod(), item.GetFileLineNumber(), msg));
25             }
26             else
27             {
28                 Console.WriteLine(string.Format("[{0},檔案{1},方法{2},行{3}]:{4}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), null, null, null, msg));
29             }
30             //var frames = trace.GetFrames();
31 
32             //foreach (var item in frames)
33             //{
34             //    Console.WriteLine(string.Format("[{0},檔案{1},方法{2},行{3}]", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), item.GetMethod().DeclaringType.FullName, item.GetMethod(), item.GetFileLineNumber()));
35             //}
36         }
37     }
38 }

再來看看

ok已完全符合我的需求了。是不是呢???這樣檢查邏輯錯誤的時候方便許多了。

附上我的全部日誌元件原始碼

元件實現了簡單的配置;

由於日誌列印控制還是輸出文字檔案都是比較耗時的事情;所以加入執行緒模型

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Diagnostics;
  4 using System.IO;
  5 using System.Linq;
  6 using System.Text;
  7 using System.Threading;
  8 
  9 /**
 10  * 
 11  * @author 失足程式設計師
 12  * @Blog http://www.cnblogs.com/ty408/
 13  * @mail [email protected]
 14  * @phone 13882122019
 15  * 
 16  */
 17 namespace Sz
 18 {
 19     /// <summary>
 20     /// 日誌輔助
 21     /// <para>AppSettings 設定 LogRootPath 為日誌的根目錄</para>
 22     /// </summary>
 23     public class Logger
 24     {
 25         static ThreadModel logConsoleThread = new ThreadModel("Console Log Thread");
 26         static ThreadModel logFileThread = new ThreadModel("File Log Thread");
 27         static string logInfoPath = "log/info/";
 28         static string logErrorPath = "log/error/";
 29 
 30         /// <summary>
 31         /// 設定日誌的輸出根目錄
 32         /// </summary>
 33         /// <param name="path"></param>
 34         static public void SetLogRootPath(string path)
 35         {
 36 
 37             logInfoPath = path + logInfoPath;
 38             logErrorPath = path + logErrorPath;
 39             if (!Directory.Exists(logInfoPath)) { Directory.CreateDirectory(logInfoPath); }
 40             if (!Directory.Exists(logErrorPath)) { Directory.CreateDirectory(logErrorPath); }
 41 
 42         }
 43 
 44         static Logger()
 45         {
 46 
 47             if (System.Configuration.ConfigurationManager.AppSettings.AllKeys.Contains("LogRootPath"))
 48             {
 49                 string logPath = System.Configuration.ConfigurationManager.AppSettings["LogRootPath"].ToString();
 50                 if (!(logPath.EndsWith("\\") || logPath.EndsWith("/")))
 51                 {
 52                     logPath = "\\";
 53                 }
 54                 logInfoPath = logPath + logInfoPath;
 55                 logErrorPath = logPath + logErrorPath;
 56             }
 57             if (!Directory.Exists(logInfoPath)) { Directory.CreateDirectory(logInfoPath); }
 58             if (!Directory.Exists(logErrorPath)) { Directory.CreateDirectory(logErrorPath); }
 59 
 60         }
 61 
 62         #region 日子寫入檔案輔助任務 class LogTaskFile : TaskBase
 63         /// <summary>
 64         /// 日子寫入檔案輔助任務
 65         /// </summary>
 66         class LogTaskFile : TaskBase
 67         {
 68             string msg, mathed;
 69             Exception exce;
 70             StackTrace trace;
 71             static readonly StringBuilder sb = new StringBuilder();
 72             public LogTaskFile(StackTrace trace, string mathed, string msg, Exception exce)
 73                 : base("File Log Task")
 74             {
 75                 this.mathed = mathed;
 76                 this.trace = trace;
 77                 this.msg = msg;
 78                 this.exce = exce;
 79             }
 80 
 81             public override void TaskRun()
 82             {
 83                 var frame = trace.GetFrame(0);
 84                 DateTime dnow = DateTime.Now;
 85                 sb.Clear();
 86                 sb.Append("[")
 87                     .Append(dnow.NowString())
 88                     .Append(mathed.PadRight(5))
 89                     .Append("")
 90                     .Append(frame.GetMethod().DeclaringType.FullName)
 91                     .Append(", ")
 92                     .Append(frame.GetMethod())
 93                     .Append(", ")
 94                     .Append(frame.GetFileLineNumber())
 95                     .Append("] ").Append(msg);
 96 
 97                 if (exce != null)
 98                 {
 99                     sb.AppendLine("")
100                     .AppendLine("----------------------Exception--------------------------")
101                     .Append(exce.GetType().FullName).Append(": ").AppendLine(exce.Message)
102                     .AppendLine(exce.StackTrace)
103                     .AppendLine("----------------------Exception--------------------------");
104                 }
105                 string logPath = string.Format("{0}info_{1}.log", logInfoPath, dnow.ToString("yyyyMMdd"));
106 
107                 System.IO.File.AppendAllText(logPath, sb.ToString(), UTF8Encoding.Default);
108                 System.IO.File.AppendAllText(logPath, "\r\n", UTF8Encoding.Default);
109 
110                 logPath = string.Format("{0}error_{1}.log", logErrorPath, dnow.ToString("yyyyMMdd"));
111                 System.IO.File.AppendAllText(logPath, sb.ToString(), UTF8Encoding.Default);
112                 System.IO.File.AppendAllText(logPath, "\r\n", UTF8Encoding.Default);
113 
114             }
115         }
116         #endregion
117 
118         #region 日誌寫入控制檯輸出 class LogTaskConsole : TaskBase
119         /// <summary>
120         /// 日誌寫入控制檯輸出
121         /// </summary>
122         class LogTaskConsole : TaskBase
123         {
124             string msg, mathed;
125             Exception exce;
126             StackTrace trace;
127             static readonly StringBuilder sb = new StringBuilder();
128 
129             public LogTaskConsole(StackTrace trace, string mathed, string msg, Exception exce)
130                 : base("Console Log Task")
131             {
132                 this.mathed = mathed;
133                 this.trace = trace;
134                 this.msg = msg;
135                 this.exce = exce;
136             }
137 
138             public override void TaskRun()
139             {
140                 sb.Clear();
141                 var frame = trace.GetFrame(0);
142                 sb.Append("[")
143                     .Append(DateTime.Now.NowString())
144                     .Append(mathed.PadRight(5))
145                     .Append("")
146                     .Append(frame.GetMethod().DeclaringType.FullName)
147                     //.Append(", ")
148                     //.Append(frame.GetMethod())
149                     .Append(", ")
150                     .Append(frame.GetFileLineNumber())
151                     .Append("] ").Append(msg);
152 
153                 if (exce != null)
154                 {
155                     sb.AppendLine("")
156                     .AppendLine("----------------------Exception--------------------------")
157                     .Append(exce.GetType().FullName).Append(": ").AppendLine(exce.Message)
158                     .AppendLine(exce.StackTrace)
159                     .AppendLine("----------------------Exception--------------------------");
160                 }
161                 Console.WriteLine(sb.ToString());
162             }
163         }
164         #endregion
165 
166         string name;
167         /// <summary>
168         /// 
169         /// </summary>
170         /// <param name="name"></param>
171         public Logger(string name)
172         {
173             this.name = name;
174         }
175 
176         /// <summary>
177         /// 輸出到控制檯
178         /// </summary>
179         /// <param name="msg"></param>
180         static public void Debug(string msg)
181         {
182             StackTrace trace = new StackTrace(1, true);
183             LogTaskConsole logConsole = new LogTaskConsole(trace, "Debug", msg, null);
184             logConsoleThread.AddTask(logConsole);
185         }
186 
187         /// <summary>
188         /// 控制檯和文字檔案
189         /// </summary>
190         /// <param name="msg"></param>
191         static public void Info(string msg)
192         {
193             AddLog("Info", msg, null);
194         }
195         /// <summary>
196         /// 控制檯和文字檔案
197         /// </summary>
198         static public void Error(string msg)
199         {
200             AddLog("Error", msg, null);
201         }
202         /// <summary>
203         /// 控制檯和文字檔案
204         /// </summary>
205         static public void Error(string msg, Exception exception)
206         {
207             AddLog("Error", msg, exception);
208         }
209 
210         static void AddLog(string mathed, string msg, Exception exception)
211         {
212             StackTrace trace = new StackTrace(2, true);
213             LogTaskConsole logConsole = new LogTaskConsole(trace, mathed, msg, exception);
214             logConsoleThread.AddTask(logConsole);
215             LogTaskFile logFile = new LogTaskFile(trace, mathed, msg, exception);
216             logFileThread.AddTask(logFile);
217         }
218     }
219 }
View Code

執行緒模型的任務型別

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 /**
 8  * 
 9  * @author 失足程式設計師
10  * @Blog http://www.cnblogs.com/ty408/
11  * @mail [email protected]
12  * @phone 13882122019
13  * 
14  */
15 namespace Sz
16 {
17     internal abstract class TaskBase
18     {
19         
20         public string Name { get; private set; }
21 
22         public TaskBase(string name)
23         {
24             this.Name = name;
25             TempAttribute = new ObjectAttribute();
26         }
27         public ObjectAttribute TempAttribute { get; set; }
28 
29         public abstract void TaskRun();
30     }
31 }
View Code

執行緒模型

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 using System.Threading.Tasks;
 7 
 8 /**
 9  * 
10  * @author 失足程式設計師
11  * @Blog http://www.cnblogs.com/ty408/
12  * @mail [email protected]
13  * @phone 13882122019
14  * 
15  */
16 namespace Sz
17 {
18     /// <summary>
19     /// 執行緒模型
20     /// </summary>    
21     internal class ThreadModel
22     {
23         public bool IsStop = false;
24         /// <summary>
25         /// ID
26         /// </summary>
27         public int ID;
28 
29         static int StaticID = 0;
30 
31         public ThreadModel(string name)
32