通过编写一个简单的日志类库来加深了解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中增加方法即可。

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

猜你喜欢

转载自www.cnblogs.com/ruzi/p/10166506.html