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

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

原文地址:https://www.cnblogs.com/ruzi/p/10166506.html

时间: 2024-10-12 06:52:07

通过编写一个简单的日志类库来加深了解C#的文件访问控制的相关文章

Linux下一个简单的日志系统的设计及其C代码实现

1.概述 在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件.在日志文件中存放程序流程中的一些重要信息, 包括:变量名称及其值.消息结构定义.函数返回值及其执行情况.脚本执行及调用情况等.通过阅读日志文件,我们能够较快地跟踪程序流程,并发现程序问题. 因此,熟练掌握日志系统的编写方法并快速地阅读日志文件,是对一个软件开发工程师的基本要求. 本文详细地介绍了Linux下一个简单的日志系统的设计方法,并给出了其C代码实现.本文为相关开发项目Linux

【C++】编写一个简单的类。包含构造函数,成员函数等。

<pre name="code" class="cpp">//编写一个简单的类.包含构造函数,成员函数等. #include <iostream> using namespace std; class Rec { public: Rec(int l,int w); int Area(); void Print(); private: int length,wide; }; Rec::Rec(int l,int w) { length=l; w

【C++】编写一个简单的函数实现重载。

//编写一个简单的函数实现重载. #include <iostream> using namespace std; int max(int a,int b) { return a>b?a:b; } int max(int a,int b,int c) { int x=max(a,b); return max(x,c); } double max(double a,double b) { return a>b?a:b; } int main() { cout<<"

编写一个简单的内核驱动模块时报错 “/lib/modules/3.13.0-32-generic/bulid: 没有那个文件或目录。 停止。”

编写一个简单的内核驱动模块 1 static int hello_init() 2 { 3 printk("hello,I am in kernel now\n"); 4 return 0; 5 } 6 void addfunc(int a,int b) 7 {return a+b;} 8 static void hello_exit() 9 { 10 printk("hello ,I will leave the kernel now\n"); 11 } 12 m

手把手教你编写一个简单的PHP模块形态的后门

看到Freebuf 小编发表的用这个隐藏于PHP模块中的rootkit,就能持久接管服务器文章,很感兴趣,苦无作者没留下PoC,自己研究一番,有了此文 0×00. 引言 PHP是一个非常流行的web server端的script语言.目前很多web应用程序都基于php语言实现.由于php是个开源软件并易于扩展,所以我们可以通过编写一个PHP模块(module 或者叫扩展 extension)来实现一个Backdoor. 本文就简单介下如何一步步编写一个简单的php 动态扩展后门. 0×01. p

编写一个简单的jdbc例子程序

1 package it.cast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 public class Base { 10 11 public static void main(String[] args) th

编写一个简单的javaEE加法程序

一 .javaEE的安装及环境配置 工具: 32位系统准备eclipse-jee-mars-2-win32.zip,64位系统准备eclipse-jee-mars-2-win32-x86_64.zip jdk1.7 maven3.3.9.rar m2.rar 环境配置: 1. 设置eclipse的配置文件eclipse.ini,修改虚拟机路径,在-vmargs之前添加 -vm E:\jee\jdk1.7\bin\javaw.exe 注意:用写字板打开修改,-vm有的电脑要换行,有的电脑不用换行

用 C 语言编写一个简单的垃圾回收器

人们似乎认为编写垃圾回收机制是很难的,是一种只有少数智者和Hans Boehm(et al)才能理解的高深魔法.我认为编写垃圾回收最难的地方就是内存分配,这和阅读K&R所写的malloc样例难度是相当的. 在开始之前有一些重要的事情需要说明一下:第一,我们所写的代码是基于Linux Kernel的,注意是Linux Kernel而不是GNU/Linux.第二,我们的代码是32bit的.第三,请不要直接使用这些代码.我并不保证这些代码完全正确,可能其中有一些我 还未发现的小的bug,但是整体思路仍

Swift语言编写一个简单的条形码扫描APP

swift语言编写一个简单的条形码扫描APP 原文地址:appcoda 在处理职员在杂货店的收银台排了很长的队伍,在机场帮助检查背包和旅客,或者在主要的食品供应商,协助处理乏味的存货清单过程,条形码扫描是很简单的处理工具.实际上,他们已经用了这个办法来解决消费者在智能购物,图书分类,等其他目的.因此,让我们来制作一个iPhone版本的条形码扫描工具吧! 对我们来说幸运的是,苹果已经制作了条形码扫描的程序,实现它是一件很简单的事情.我们将要研究进入AV Foundation框架的世界,组建APP,