1. 程式人生 > >通過編寫一個簡單的日誌類庫來加深瞭解C#的檔案訪問控制

通過編寫一個簡單的日誌類庫來加深瞭解C#的檔案訪問控制

在程式的開發除錯過程及釋出執行後的狀態監控中,日誌都有著極其重要的分量,通過在關鍵邏輯節點將關鍵資料記錄到日誌檔案當中能幫助我們儘快找到程式問題所在。網上有不少專業成熟的日誌元件可用,比如log4net和nlog等,由其專業及受歡迎程度可見日誌在一個程式中的重要性。

我只用過log4net,而在用log4net寫日誌的過程中慢慢覺著太繁瑣了點,不就寫個日誌嗎?為毛搞得那麼複雜?各種配置讓我有點抓狂。

於是我就想,自己來吧!

首先分析一下一個基本的日誌類庫應該具有的基本功能及實現的時候需要注意和解決的問題:

1.關於日誌檔案的寫入

寫日誌並不是簡單的開啟一個檔案然後寫入資料然後關閉了事,無論是web程式還是桌面程式,首要問題是多執行緒爭搶寫入一個日誌檔案的訪問控制,次要問題是要允許其它程序在寫入程序未釋放日誌檔案時日誌檔案能被讀取——試想如果日誌在寫的時候不能被讀取那日誌將毫無價值。

為了解決多執行緒寫入的問題,多執行緒寫入的資料將被快取在一個StringBuilder物件中,而後由一個專門的寫檔案執行緒來負責取出資料寫入到日誌檔案,以此來保證只有一個執行緒對日誌檔案進行寫操作,如果再解決在檔案流未關閉的情況下讓其它程序或執行緒能讀取日誌內容,那問題就都不是問題了,而在檔案流未關閉的情況下要讓其它程序或執行緒能讀取日誌內容只需要在開啟或建立日誌檔案的FileStream時指定System.IO.FileShare引數為Read即可。

2.關於日誌檔案的讀取

檔案寫入成功後會有讀取進行檢視及分析的需求。內容較少的時候直接記事本開啟即可,但是日誌較大的時候就費勁了,雖然也有一些專門的軟體能開啟大文字檔案,可開啟日誌檔案有時並不是只為了看上一眼而已,很可能需要提取一些受關注的資料做個統計分析,比如提取某個操作的耗時來做瓶頸參考,因此有必要實現對大文字檔案的讀取,在讀取過程中進行資料的留存分析。

對大文字檔案的讀取當然要按塊來讀取,比如一次讀取10M位元組,這樣即便是幾個G的檔案也沒幾次可讀的,重要的是不能截斷單詞和寬字元,所以每讀取到指定位元組數(如10M位元組)的資料後需要根據指定的參考字元(如換行符、空格、逗號、句號等)做偏移計算。

對檔案的讀取在建立檔案的讀取流的時候必須要指定System.IO.FileShare引數為ReadWrite,否則對正在被寫入或未被釋放的檔案的訪問將被拒絕,因為寫入的程序已經獲得了寫入許可權,作為後來的讀取者一定要允許其它程序可以對檔案讀寫,要不然衝突就是一定的了。

3.關於日誌的清理

隨著程式常年執行,日誌積累得越來越多,而日誌應該都有一定的時效性,過了時效期後的日誌就沒有什麼價值了,所以應該對日誌做定時的清理操作,因此寫日誌的時候應該有一個預設的時效值,使日誌在到期之後自動刪除,以免無限增多浪費了磁碟空間,畢竟磁碟空間是十分有限的。

 

下面開始上程式碼:

新建一個 .Net Standard 類庫,命名 Logger ,在類庫中新增一個 Core 資料夾,在 Core 資料夾新增以下檔案:

  1. ILog.cs 介面
  2. Log.cs 密封的介面實現類(不對程式集外提供訪問)
  3. TextFileReader.cs 文字檔案讀取
  4. Factory.cs 工廠類(生產和維護日誌物件)
 1 namespace Logger.Core
 2 {
 3     public interface ILog
 4     {
 5         void Write(string logInfo);
 6         void WriteFormat(string format, params object[] args);
 7         void SaveLogToFile();
 8         void ClearLogFile();
 9     }
10 }
ILog.cs
  1 namespace Logger.Core
  2 {
  3     internal class Log : ILog
  4     {
  5         private System.Text.StringBuilder logSource = null;
  6         private string logFilePre = string.Empty;
  7         private System.IO.FileStream fileStream = null;
  8         private DateTime logFileScanLastTime = DateTime.Now;
  9         private int logFileRetentionDays = 90;
 10 
 11 
 12         public Log(string logFilePre)
 13             : this(logFilePre, 90)
 14         {
 15 
 16         }
 17         public Log(string logFilePre, int logFileRetentionDays)
 18         {
 19             this.logFilePre = logFilePre;
 20             this.logSource = new System.Text.StringBuilder();
 21             if (logFileRetentionDays < 1)
 22             {
 23                 logFileRetentionDays = 1;
 24             }
 25             this.logFileRetentionDays = logFileRetentionDays;
 26             Factory.SetFileThreadStart();
 27         }
 28 
 29 
 30         private System.IO.FileStream GetFileStream()
 31         {
 32             if (!System.IO.Directory.Exists(Factory.logsDirPath))
 33             {
 34                 System.IO.Directory.CreateDirectory(Factory.logsDirPath);
 35             }
 36             System.IO.FileStream fs;
 37             string FilePath = System.IO.Path.Combine(Factory.logsDirPath, this.logFilePre + DateTime.Now.ToString("yyyyMMdd") + ".log");
 38             if (!System.IO.File.Exists(FilePath))
 39             {
 40                 if (fileStream != null)
 41                 {
 42                     fileStream.Close();
 43                 }
 44                 fileStream = fs = new System.IO.FileStream(FilePath, System.IO.FileMode.CreateNew, System.IO.FileAccess.Write, System.IO.FileShare.Read, 1024, true);
 45             }
 46             else
 47             {
 48                 if (fileStream != null)
 49                 {
 50                     fs = fileStream;
 51                 }
 52                 else
 53                 {
 54                     fileStream = fs = new System.IO.FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Write, System.IO.FileShare.Read, 1024, true);
 55                 }
 56             }
 57             return fs;
 58         }
 59         private string GetLogText()
 60         {
 61             string s = "";
 62             if (logSource.Length > 0)
 63             {
 64                 lock (logSource)
 65                 {
 66                     s = logSource.ToString();
 67                     logSource.Clear();
 68                 }
 69             }
 70             return s;
 71         }
 72 
 73 
 74         public void Write(string logInfo)
 75         {
 76             try
 77             {
 78                 if (logSource == null)
 79                 {
 80                     logSource = new System.Text.StringBuilder();
 81                 }
 82                 lock (this)
 83                 {
 84                     logSource.AppendFormat("{0} {1}{2}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff"), logInfo, System.Environment.NewLine);
 85                 }
 86             }
 87             catch { }
 88         }
 89         public void WriteFormat(string format, params object[] args)
 90         {
 91             try
 92             {
 93                 if (logSource == null)
 94                 {
 95                     logSource = new System.Text.StringBuilder();
 96                 }
 97                 lock (this)
 98                 {
 99                     logSource.AppendFormat("{0} ", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff"));
100                     logSource.AppendFormat(format, args);
101                     logSource.Append(System.Environment.NewLine);
102                 }
103             }
104             catch { }
105         }
106         public void SaveLogToFile()
107         {
108             try
109             {
110                 string logInfo = GetLogText();
111                 if (logInfo.Length > 0)
112                 {
113                     System.IO.FileStream fs = GetFileStream();
114                     byte[] buffer = System.Text.UTF8Encoding.UTF8.GetBytes(logInfo);
115                     long lockBegin = fs.Length;
116                     long lockEnd = buffer.Length;
117                     fs.Position = lockBegin;
118                     fs.Lock(lockBegin, lockEnd);
119                     //fs.WriteAsync(buffer, 0, buffer.Length);
120                     fs.Write(buffer, 0, buffer.Length);
121                     fs.Unlock(lockBegin, lockEnd);
122                     fs.Flush();
123                     //fs.Close();
124                 }
125             }
126             catch { }
127         }
128         public void ClearLogFile()
129         {
130             if ((DateTime.Now - logFileScanLastTime).TotalMinutes < 5)
131             {
132                 return;
133             }
134             logFileScanLastTime = DateTime.Now;
135             System.IO.DirectoryInfo directoryInfo = new System.IO.DirectoryInfo(Factory.logsDirPath);
136             if (!directoryInfo.Exists)
137             {
138                 return;
139             }
140             System.IO.FileInfo[] files = directoryInfo.GetFiles(this.logFilePre + "*.log", System.IO.SearchOption.TopDirectoryOnly);
141             if (files == null || files.Length < 1)
142             {
143                 return;
144             }
145             DateTime time = DateTime.Now.AddDays(0 - logFileRetentionDays);
146             foreach (System.IO.FileInfo file in files)
147             {
148                 try
149                 {
150                     if (file.CreationTime < time)
151                     {
152                         file.Delete();
153                     }
154                 }
155                 catch { }
156             }
157         }
158 
159 
160     }
161 }
Log.cs
  1 namespace Logger.Core
  2 {
  3     public class TextFileReader
  4     {
  5         bool _readStart = false;
  6         bool _readEnd = false;
  7         System.IO.FileStream _stream = null;
  8         System.Text.Encoding _code = null;
  9         long _fileLength = 0;
 10         long _currentPosition = 0;
 11         string _readStr = string.Empty;
 12         int _readBytes = 1024;
 13         string _filePath = "";
 14         readonly string[] _defaultOffsetStrArray = new string[] { System.Environment.NewLine, " ", ",", ".", "!", "?", ";", "", "", "", "", "" };
 15         string[] _offsetStrArray = null;
 16 
 17         public string ReadStr {
 18             get { return _readStr; }
 19         }
 20         public string FilePath {
 21             get { return _filePath; }
 22             set { _filePath = value; }
 23         }
 24         public int ReadBytes {
 25             get { return _readBytes < 1024 ? 1024 : _readBytes; }
 26             set { _readBytes = value; }
 27         }
 28         public string[] OffsetStrArray {
 29             get { return (_offsetStrArray == null|| _offsetStrArray.Length < 1)? _defaultOffsetStrArray : _offsetStrArray; }
 30             set { _offsetStrArray = value; }
 31         }
 32 
 33 
 34         public TextFileReader() {
 35             _offsetStrArray = _defaultOffsetStrArray;
 36         }
 37         public TextFileReader(string FilePath)
 38         {
 39             this.FilePath = FilePath;
 40             _offsetStrArray = _defaultOffsetStrArray;
 41         }
 42         private int GetPosition(string readStr, string[] offsetStrArray)
 43         {
 44             int position = -1;
 45             for (int i = 0; i < offsetStrArray.Length; i++)
 46             {
 47                 position = readStr.LastIndexOf(offsetStrArray[i]);
 48                 if (position > 0)
 49                 {
 50                     break;
 51                 }
 52             }
 53             return position;
 54         }
 55         public bool Read()
 56         {
 57             if (!_readStart)
 58             { 
 59                 //System.IO.FileShare.ReadWrite:允許其它程式讀寫檔案(重要,否則很可能會與負責寫入的程式衝突而被拒絕訪問)
 60                 _stream = new System.IO.FileStream(this.FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
 61                 _code = GetType(this.FilePath);
 62                 _currentPosition = 0;
 63                 _fileLength = _stream.Length;
 64                 _readStart = true;
 65             }
 66             if (_currentPosition < _fileLength)
 67             {
 68                 byte[] readBuffer = new byte[this.ReadBytes];
 69                 //設定讀取位置
 70                 _stream.Seek(_currentPosition, System.IO.SeekOrigin.Begin);
 71                 //本次實際讀到的位元組數
 72                 int currentReadBytes = _stream.Read(readBuffer, 0, readBuffer.Length);
 73                 //讀取位置偏移
 74                 _currentPosition += currentReadBytes;
 75 
 76                 //實際讀到的位元組少於指定的位元組數(在讀到最後一批時)
 77                 if (currentReadBytes < _readBytes)
 78                 {
 79                     byte[] temp = new byte[currentReadBytes];
 80                     int index = 0;
 81                     while (index < currentReadBytes)
 82                     {
 83                         temp[index] = readBuffer[index];
 84                         index++;
 85                     }
 86                     readBuffer = temp;
 87                 }
 88                 _readStr = _code.GetString(readBuffer);
 89                 //如果沒有讀到最後一個位元組則計算位置偏移
 90                 if (_currentPosition < _fileLength)
 91                 {
 92                     int offsetStrPosition = GetPosition(_readStr, this.OffsetStrArray);
 93                     if (offsetStrPosition > 0)//找到內容則計算位置偏移
 94                     {
 95                         //提取將被移除的內容
 96                         string removeStr = _readStr.Substring(offsetStrPosition + 1);
 97                         //移除內容
 98                         _readStr = _readStr.Remove(offsetStrPosition + 1);
 99                         //位置後退
100                         _currentPosition = _currentPosition - _code.GetBytes(removeStr).Length;
101                     }
102                 }
103             }
104             else
105             {
106                 _readEnd = true;
107                 _stream.Dispose();
108             }
109             return !_readEnd;
110         }
111         
112 
113         public static System.Text.Encoding GetType(string fullname)
114         {
115             System.IO.FileStream fs = new System.IO.FileStream(fullname, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
116             System.Text.Encoding r = GetType(fs);
117             fs.Close();
118             return r;
119         }
120         public static System.Text.Encoding GetType(System.IO.FileStream fs)
121         {
122             byte[] Unicode = new byte[] { 0xFF, 0xFE, 0x41 };
123             byte[] UnicodeBIG = new byte[] { 0xFE, 0xFF, 0x00 };
124             byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF };
125             System.Text.Encoding reVal = System.Text.Encoding.Default;
126 
127             System.IO.BinaryReader r = new System.IO.BinaryReader(fs, System.Text.Encoding.Default);
128             int i;
129             int.TryParse(fs.Length.ToString(), out i);
130             byte[] ss = r.ReadBytes(i);
131             if (IsUTF8Bytes(ss) || (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF))
132             {
133                 reVal = System.Text.Encoding.UTF8;
134             }
135             else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00)
136             {
137                 reVal = System.Text.Encoding.BigEndianUnicode;
138             }
139             else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41)
140             {
141                 reVal = System.Text.Encoding.Unicode;
142             }
143             r.Close();
144             return reVal;
145         }
146         private static bool IsUTF8Bytes(byte[] data)
147         {
148             int charByteCounter = 1;
149             byte curByte;
150             for (int i = 0; i < data.Length; i++)
151             {
152                 curByte = data[i];
153                 if (charByteCounter == 1)
154                 {
155                     if (curByte >= 0x80)
156                     {
157                         while (((curByte <<= 1) & 0x80) != 0)
158                         {
159                             charByteCounter++;
160                         }
161                         if (charByteCounter == 1 || charByteCounter > 6)
162                         {
163                             return false;
164                         }
165                     }
166                 }
167                 else
168                 {
169                     if ((curByte & 0xC0) != 0x80)
170                     {
171                         return false;
172                     }
173                     charByteCounter--;
174                 }
175             }
176             if (charByteCounter > 1)
177             {
178                 throw new Exception("非預期的byte格式");
179             }
180             return true;
181         }
182 
183 
184     }
185 }
TextFileReader.cs
 1 namespace Logger.Core
 2 {
 3     public static class Factory
 4     {
 5         private static object setFileThreadCreateLockObj = new object();
 6         private static object loggerCreateLockObj = new object();
 7         public static readonly string logsDirPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
 8         internal static readonly System.Collections.Generic.Dictionary<string, ILog> loggerDic = new System.Collections.Generic.Dictionary<string, ILog>();
 9         internal static System.Threading.Thread setFileThread = null;
10         internal static void SetFileThreadStartFunc(object obj)
11         {
12             while (true)
13             {
14                 try
15                 {
16                     foreach (string key in loggerDic.Keys)
17                     {
18                         loggerDic[key].SaveLogToFile();
19                         loggerDic[key].ClearLogFile();
20                     }
21                     System.Threading.Thread.Sleep(1);
22                 }
23                 catch { }
24             }
25         }
26         public static void SetFileThreadStart()
27         {
28             if (setFileThread == null)
29             {
30                 lock (setFileThreadCreateLockObj)
31                 {
32                     if (setFileThread == null)
33                     {
34                         setFileThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(SetFileThreadStartFunc));
35                         setFileThread.IsBackground = true;
36                         setFileThread.Start(null);
37                     }
38                 }
39             }
40         }
41         public static ILog GetLogger()
42         {
43             return GetLogger("Trace");
44         }
45         public static ILog GetLogger(string LogFilePre)
46         {
47             return GetLogger(LogFilePre, 90);
48         }
49         public static ILog GetLogger(string logFilePre, int logFileRetentionDays)
50         {
51             logFilePre = GetLogFilePre(logFilePre);
52             if (loggerDic.ContainsKey(logFilePre))
53             {
54                 return loggerDic[logFilePre];
55             }
56             else
57             {
58                 lock (loggerCreateLockObj)
59                 {
60                     if (loggerDic.ContainsKey(logFilePre))
61                     {
62                         return loggerDic[logFilePre];
63                     }
64                     else
65                     {
66                         ILog _logger = new Log(logFilePre, logFileRetentionDays);
67                         loggerDic.Add(logFilePre, _logger);
68                         return _logger;
69                     }
70                 }
71             }
72         }
73         public static string GetLogFilePre(string logFilePre)
74         {
75             if (string.IsNullOrEmpty(logFilePre))
76 
77             {
78                 logFilePre = "Trace";
79             }
80             logFilePre = logFilePre.ToLower();
81             if (!logFilePre.EndsWith("-"))
82             {
83                 logFilePre = logFilePre + "-";
84             }
85             logFilePre = logFilePre.Substring(0, 1).ToUpper() + logFilePre.Substring(1);
86             return logFilePre;
87         }
88         public static System.Collections.Generic.List<string> GetLogFilePreList()
89         {
90             System.Collections.Generic.List<string> reval = new System.Collections.Generic.List<string>();
91             foreach(string key in loggerDic.Keys)
92             {
93                 reval.Add(key);
94             }
95             return reval;
96         }
97 
98     }
99 }
Factory.cs

以上是實現日誌功能的核心程式碼,下面在類庫專案下直接新增兩個靜態類:

  1. LogWriter.cs 負責寫,定義了常規的 Fatal , Error , Info , Debug 等方法及預設的日誌時效期
  2. LogReader.cs 負責讀,如獲取日誌型別列表,獲取日誌檔案列表,或取日誌檔案的TextFileReader物件等
 1 namespace Logger
 2 {
 3     public static class LogWriter
 4     {
 5         public static Core.ILog Debug()
 6         {
 7             return Core.Factory.GetLogger("Debug", 3);
 8         }
 9         public static Core.ILog Debug(string logInfo)
10         {
11             Core.ILog logger = Debug();
12             logger.Write(logInfo);
13             return logger;
14         }
15         public static Core.ILog Debug(string format, params object[] args)
16         {
17             Core.ILog logger = Debug();
18             logger.WriteFormat(format, args);
19             return logger;
20         }
21         public static Core.ILog Info()
22         {
23             return Core.Factory.GetLogger("Info", 60);
24         }
25         public static Core.ILog Info(string logInfo)
26         {
27             Core.ILog logger = Info();
28             logger.Write(logInfo);
29             return logger;
30         }
31         public static Core.ILog Info(string format, params object[] args)
32         {
33             Core.ILog logger = Info();
34             logger.WriteFormat(format, args);
35             return logger;
36         }
37         public static Core.ILog Error()
38         {
39             return Core.Factory.GetLogger("Error", 60);
40         }
41         public static Core.ILog Error(string logInfo)
42         {
43             Core.ILog logger = Error();
44             logger.Write(logInfo);
45             return logger;
46         }
47         public static Core.ILog Error(string format, params object[] args)
48         {
49             Core.ILog logger = Error();
50             logger.WriteFormat(format, args);
51             return logger;
52         }
53         public static Core.ILog Fatal()
54         {
55             return Core.Factory.GetLogger("Fatal", 60);
56         }
57         public static Core.ILog Fatal(string logInfo)
58         {
59             Core.ILog logger = Fatal();
60             logger.Write(logInfo);
61             return logger;
62         }
63         public static Core.ILog Fatal(string format, params object[] args)
64         {
65             Core.ILog logger = Fatal();
66             logger.WriteFormat(format, args);
67             return logger;
68         }
69     }
70 }
LogWriter.cs
 1 namespace Logger
 2 {
 3     public static class LogReader
 4     {
 5         public static System.Collections.Generic.List<string> GetLogFilePreList()
 6         {
 7             return Core.Factory.GetLogFilePreList();
 8         }
 9         public static System.IO.FileInfo[] GetLogFiles(string logFilePre)
10         {
11             System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(Core.Factory.logsDirPath);
12             if (!dir.Exists)
13             {
14                 return new System.IO.FileInfo[] { };
15             }
16             logFilePre = Core.Factory.GetLogFilePre(logFilePre);
17             System.IO.FileInfo[] fis = dir.GetFiles(logFilePre + "*.log", System.IO.SearchOption.TopDirectoryOnly);
18             if (fis == null)
19             {
20                 fis = new System.IO.FileInfo[] { };
21             }
22             return fis;
23         }
24         public static Core.TextFileReader GetTextFileReader(System.IO.FileInfo logFileInfo)
25         {
26             Core.TextFileReader textFileReader = new Core.TextFileReader(logFileInfo.FullName);
27             textFileReader.ReadBytes = 1024 * 1024 * 2;
28             return textFileReader;
29         }
30     }
31 }
LogReader

 

新建一個控制檯程式來測試一下,測試程式碼:

  1 class Program
  2 {
  3         static void Main(string[] args)
  4         {
  5             Writer();
  6             Reader();
  7         }
  8         static void Writer()
  9         {
 10             for (var i = 1; i < 6; i++)
 11             {
 12                 System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(WriterFunc));
 13                 thread.IsBackground = true;
 14                 thread.Start(i);
 15             }
 16         }
 17         static void WriterFunc(object num)
 18         {
 19             int threadNum = (int)num;
 20             while (true)
 21             {
 22                 Logger.LogWriter.Info("這是執行緒{0}", threadNum);
 23                 Logger.LogWriter.Error("這是執行緒{0}", threadNum);
 24                 Logger.LogWriter.Fatal("這是執行緒{0}", threadNum);
 25                 System.Threading.Thread.Sleep(10);
 26             }
 27         }
 28         static void Reader()
 29         {
 30             string cmd = "";
 31             while (cmd != "r")
 32             {
 33                 Console.Write("輸入 r 讀取日誌:");
 34                 cmd = Console.ReadLine();
 35             }
 36 
 37             System.Collections.Generic.List<string> preList = Logger.LogReader.GetLogFilePreList();
 38             if (preList.Count < 1)
 39             {
 40                 Console.ForegroundColor = ConsoleColor.Red;
 41                 Console.WriteLine("未能檢索到日誌記錄!");
 42                 Console.ResetColor();
 43                 Reader();
 44             }
 45             Console.WriteLine("-----------------------------------------------------------");
 46 
 47             Console.WriteLine("編號\t型別字首");
 48             Console.ForegroundColor = ConsoleColor.Red;
 49             for (var i = 0; i < preList.Count; i++)
 50             {
 51                 Console.WriteLine("{0}\t{1}", i + 1, preList[i]+"*");
 52             }
 53             Console.ResetColor();
 54             Console.WriteLine("-----------------------------------------------------------");
 55 
 56             Console.Write("輸入編號讀取日誌檔案列表:");
 57             int preNum = GetInputNum(1, preList.Count);
 58 
 59             var files = Logger.LogReader.GetLogFiles(preList[preNum-1]);
 60             if (files.Length < 1)
 61             {
 62                 Console.ForegroundColor = ConsoleColor.Red;
 63                 Console.WriteLine("未能檢索到日誌檔案!");
 64                 Console.ResetColor();
 65                 Reader();
 66             }
 67             Console.WriteLine("-----------------------------------------------------------");
 68 
 69             Console.WriteLine("編號\t日誌檔案");
 70             Console.ForegroundColor = ConsoleColor.Red;
 71             for (var i = 0; i < files.Length; i++)
 72             {
 73                 Console.WriteLine("{0}\t{1}", i + 1, System.IO.Path.GetFileName(files[i].FullName));
 74             }
 75             Console.ResetColor();
 76             Console.WriteLine("-----------------------------------------------------------");
 77 
 78             Console.Write("輸入編號讀取日誌:");
 79             int fileNum = GetInputNum(1, files.Length);
 80             Console.WriteLine("-----------------------------------------------------------");
 81 
 82             var reader = Logger.LogReader.GetTextFileReader(files[fileNum - 1]);
 83             while (reader.Read())
 84             {
 85                 Console.Write(reader.ReadStr);
 86             }
 87 
 88             Console.WriteLine();
 89 
 90             Reader();
 91 
 92         }
 93         static int GetInputNum(int min, int max)
 94         {
 95             int num = -1;
 96             while (true)
 97             {
 98                 string inputNum = Console.ReadLine();
 99                 bool flag = false;
100                 if (System.Text.RegularExpressions.Regex.IsMatch(inputNum, @"^\d{1,9}$"))
101                 {
102                     num = Convert.ToInt32(inputNum);
103                     flag = num <= max && num >= min;
104                 }
105                 if (!flag)
106                 {
107                     Console.Write("輸入不合法,請重新輸入:");
108                     num = -1;
109                 }
110                 else
111                 {
112                     break;
113                 }
114             }
115             return num;
116         }
117 }
Program.cs

程式執行截圖:

 

至此,一個日誌類庫就算完成了。

鑑於個人水平問題,不敢妄言更高效或更優雅,但是可以整合到其它專案中工作了,該程式碼作者在公司的實際專案中有使用。

不用各種繁雜的配置,想寫就寫,如果想要新增一個其它型別的日誌只要在LogWriter.cs中增加方法即可。

 

(^_^)大神莫笑,小菜莫怕,歡迎善意的溝通和交流!