运用C#编写Http服务器
什么是HTTP服务器
Web服务器是指驻留于因特网上某种类型计算机的程序。当Web浏览器(客户端)连到服务器上并请求文件时,服务器将处理该请求并将文件反馈到该浏览器上,附带的信息会告诉浏览器如何查看该文件(即文件类型)。服务器使用HTTP(超文本传输协议)与客户机浏览器进行信息交流,这就是人们常把它们称为HTTP服务器的原因。
HTTP协议
HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的规范化工作正在进行之中,而且HTTP-NG(Next Generation of HTTP)的建议已经提出。
关于HTTP协议讲解这里有一篇文章,感兴趣可以去看看 https://blog.csdn.net/gueter/article/details/1524447
代码
BaseHeader类
用于定义一些HTTP请求类和HTTP响应类的公共字段
public class BaseHeader { /// <summary> /// http编码格式 /// </summary> public Encoding Encoding { get; set; } /// <summary> /// 报文类型 /// </summary> public string Content_Type { get; set; } /// <summary> /// 报文长度 /// </summary> public int Content_Length { get; set; } /// <summary> /// 报头的编码格式字段 /// </summary> public string Content_Encoding { get; set; } /// <summary> /// 报文Body /// </summary> public string Content { get; set; } /// <summary> /// 用于存储报头的字典 /// </summary> public Dictionary<string, string> Headers { get; set; } /// <summary> /// 日志单例对象 /// </summary> public static LogManager _logger = LogManager.Instance; }
HttpServer类
用于监听端口开启服务
public class HttpServer { /// <summary> /// 服务器IP /// </summary> public string ServerIP { get; private set; } /// <summary> /// 服务器端口 /// </summary> public int ServerPort { get; private set; } /// <summary> /// 是否运行 /// </summary> public bool IsRunning { get; private set; } /// <summary> /// 服务端Socket /// </summary> private TcpListener serverListener; /// <summary> /// 日志单例对象 /// </summary> public static LogManager _logger = LogManager.Instance; public event Action<HttpRequest, HttpResponse> PostOps;//post事件 public event Action<HttpRequest, HttpResponse> GetOps;//get事件 /// <summary> /// 构造函数 /// </summary> /// <param name="ip">IP地址</param> /// <param name="port">端口</param> public HttpServer(int port) { this.ServerPort = port; } public HttpServer(string port) { this.ServerPort = Convert.ToInt32(port); } //开启服务器 public void Start() { if (IsRunning) return; //创建服务端Socket this.serverListener = new TcpListener(IPAddress.Any, ServerPort); this.IsRunning = true; this.serverListener.Start(); _logger.Info(string.Format("Http服务器正在监听{0}", serverListener.LocalEndpoint.ToString())); try { while (IsRunning) { TcpClient client = serverListener.AcceptTcpClient(); Thread requestThread = new Thread(() => { ProcessRequest(client); }); requestThread.IsBackground = true; requestThread.Start(); } } catch { } } //关闭服务器 public void Stop() { if (!IsRunning) return; IsRunning = false; serverListener.Stop(); } #region 内部方法 /// <summary> /// 处理客户端请求 /// </summary> /// <param name="tcpClient">客户端Socket</param> private void ProcessRequest(TcpClient tcpClient) { //处理请求 Stream clientStream = tcpClient.GetStream(); Thread.Sleep(390);//不知道为什么不Sleep一下接收不到body,可以试试去掉 if (clientStream != null) { //构造HTTP请求 HttpRequest request = new HttpRequest(clientStream); //构造HTTP响应 HttpResponse response = new HttpResponse(clientStream); //处理请求类型 switch (request.Method)//根据请求方法触发不同事件 { case "GET": OnGet(request, response); break; case "POST": OnPost(request, response); break; default: OnDefault(request, response); break; } } } #endregion #region 方法 /// <summary> /// 响应Get请求 /// </summary> /// <param name="request">请求报文</param> public void OnGet(HttpRequest request, HttpResponse response) { GetOps(request, response); } /// <summary> /// 响应Post请求 /// </summary> /// <param name="request"></param> public void OnPost(HttpRequest request, HttpResponse response) { PostOps(request, response); } /// <summary> /// 响应默认请求 /// </summary> public void OnDefault(HttpRequest request, HttpResponse response) { } #endregion }
HttpRequest类
用于接收HTTP请求,并解析
public class HttpRequest : BaseHeader { #region 字段属性 /// <summary> /// URL参数 /// </summary> public Dictionary<string, string> Params { get; private set; } /// <summary> /// http请求方法 /// </summary> public string Method { get; set; } /// <summary> /// http请求的URL地址 /// </summary> public string URL { get; set; } /// <summary> /// http协议版本 /// </summary> public string HTTP_Version { get; set; } #endregion /// <summary> /// 定义缓冲区 /// </summary> private const int MAX_SIZE = 1024 * 1024 * 2; private byte[] bytes = new byte[MAX_SIZE]; private Stream DataStream; public HttpRequest(Stream stream) { this.DataStream = stream; string dataString = GetData(DataStream); var dataArray = Regex.Split(dataString, Environment.NewLine); var requestLine = Regex.Split(dataArray[0], @"(\s+)") .Where(e => e.Trim() != string.Empty) .ToArray();//分割出请求行的信息 if (requestLine.Length > 0) this.Method = requestLine[0]; if (requestLine.Length > 1) this.URL = Uri.UnescapeDataString(requestLine[1]); if (requestLine.Length > 2) this.HTTP_Version = requestLine[2]; this.Headers = GetHeader(dataArray,out int index); if (this.Method == "POST") { this.Content_Length = Convert.ToInt32(Headers["Content-Length"]); if (Content_Length != 0)//数据长度不等于0 { this.Content_Type = Headers["Content-Type"]; this.Encoding = Content_Type.Split('=')[1] == "utf-8" ? Encoding.UTF8 : Encoding.Default; this.Content = GetBody(dataArray, index);//真正的数据 _logger.Info("收到消息:\r\n" + Content + "\r\n"); //Task.Run(() => //{ //if (socket != null) //{ //byte[] buffer = this.Encoding.GetBytes(this.Content); //socket.Send(buffer); //} //else //{ // _logger.Debug("上位机未连接!"); //} //}); } else { _logger.Info("报文为空!"); } } } /// <summary> /// 获取数据流中的数据data /// </summary> /// <param name="DataStream"></param> /// <returns></returns> public string GetData(Stream DataStream) { try { var length = 0; var data = string.Empty; do { length = DataStream.Read(bytes, 0, MAX_SIZE - 1); data += Encoding.UTF8.GetString(bytes, 0, length); } while (length > 0 && !data.Contains("\r\n\r\n")); return data; } catch { return ""; } } /// <summary> /// 获取报头header /// </summary> /// <param name="dataArray"></param> /// <returns></returns> public Dictionary<string,string> GetHeader(string[] dataArray,out int index) { var header = new Dictionary<string, string>(); index = 0; foreach (var item in dataArray) { index++; if (item == "")//读取到空行表示header已经读取完成 { return header; } if (item.Contains(':')) { var dataTemp = item.Split(':').ToList();//把报头数据分割以键值对方式存入字典 if (dataTemp.Count > 2)//特殊情况Host有两个“:” { for (int i = 2; i < dataTemp.Count;) { dataTemp[1] += ":"+dataTemp[i]; dataTemp.Remove(dataTemp[i]); } } header.Add(dataTemp[0].Trim(), dataTemp[1].Trim()); } } return header; } /// <summary> /// 获取报文body /// </summary> /// <param name="dataArray"></param> /// <param name="index"></param> /// <returns></returns> public string GetBody(string[] dataArray, int index) { string Body = string.Empty; for (int i = index ; i < dataArray.Length; i++) { Body += dataArray[i]; } return Body; } #region 反射 //通过反射查找请求对应的方法(未完成) public void reflex() { Type t1 = this.GetType(); //根据字符标题,取得当前函数调用 MethodInfo method = t1.GetMethod("miaox"); //获取需要传入的参数 ParameterInfo[] parms = method.GetParameters(); int haha = 1111; //调用 method.Invoke(this, new object[] { haha }); } public void miao(int x) { } #endregion }
HttpResponse类
用于构造响应HTTP请求
public class HttpResponse : BaseHeader { #region 字段属性 /// <summary> /// http协议版本 /// </summary> public string HTTP_Version { get; set; } /// <summary> /// 状态码 /// </summary> public string StatusCode { get; set; } /// <summary> /// http数据流 /// </summary> private Stream DataStream; #endregion public HttpResponse(Stream stream) { this.DataStream = stream; } /// <summary> /// 构建响应头部 /// </summary> /// <returns></returns> protected string BuildHeader() { StringBuilder builder = new StringBuilder();//StringBuilder(字符串变量)进行运算时,是一直在已有对象操作的,适合大量频繁字符串的拼接或删除 //string(字符串常量)在进行运算的时候是重新生成了一个新的string对象,不适合大量频繁字符串的拼接或删除 if (!string.IsNullOrEmpty(StatusCode)) builder.Append(HTTP_Version + " " + StatusCode + "\r\n"); if (!string.IsNullOrEmpty(this.Content_Type)) builder.AppendLine("Content-Type:" + this.Content_Type); return builder.ToString(); } /// <summary> /// 发送数据 /// </summary> public void Send() { if (!DataStream.CanWrite) return; try { //发送响应头 var header = BuildHeader(); byte[] headerBytes = this.Encoding.GetBytes(header); DataStream.Write(headerBytes, 0, headerBytes.Length); //发送空行 byte[] lineBytes = this.Encoding.GetBytes(System.Environment.NewLine); DataStream.Write(lineBytes, 0, lineBytes.Length); //发送内容 byte[] buffer = this.Encoding.GetBytes(this.Content); DataStream.Write(buffer, 0, buffer.Length); } catch(Exception e) { _logger.Debug("服务器响应异常!" + e.Message); } finally { DataStream.Close(); } } }
调用
#region...日志...
/// <summary>
/// 日志单例对象
/// </summary>
public static LogManager _logger = LogManager.Instance;
/// <summary>
/// 写入日志框
/// </summary>
/// <param name="obj">日志对象</param>
private void LogManager_Event(LogInfo obj)
{
log_txt.Font = new Font("黑体", 8, FontStyle.Regular);
//若是超过N行 则清除
if (log_txt.Lines.Length > 500)
{
log_txt.Clear();
log_txt.AppendText("更多日志,请到日志文件中查看。\r\n");
}
if (obj.LogLevel == LogLevel.Info)
{
log_txt.SelectionColor = Color.DarkBlue;
}
if (obj.LogLevel == LogLevel.Error)
{
log_txt.SelectionColor = Color.Red;
}
if (obj.LogLevel == LogLevel.Debug)
{
log_txt.SelectionColor = Color.Orange;
}
if (obj.LogLevel == LogLevel.Fatal)
{
//this._textBox.SelectionColor = Color.Red;
log_txt.SelectionColor = Color.Purple;
}
log_txt.AppendText(obj.Message + "\r\n");
log_txt.ScrollToCaret();
}
/// <summary>
/// 初始化日志
/// </summary>
private void LogInit()
{
LogManager.Event += LogManager_Event;
}
#endregion
private void Form1_Load(object sender, EventArgs e)
{
HttpPort_txt.Text = MsgList[0];
LogInit();
_logger.Info("日志初始化...\r\n");
Control.CheckForIllegalCrossThreadCalls = false;
httpServer = new HttpServer(HttpPort_txt.Text);
httpServer.GetOps += HttpServer_GetOps;
httpServer.PostOps += HttpServer_PostOps;
Task.Run(new Action(() => { httpServer.Start(); }));
_logger.Info("服务器正在开启,请稍等...");
}
日志类LogManager
用于显示日志信息和生成日志文件
/// <summary> /// 日志级别 /// </summary> public enum LogLevel { /// <summary> /// 信息级别 /// </summary> Info, /// <summary> /// debug级别 /// </summary> Debug, /// <summary> /// 错误级别 /// </summary> Error, /// <summary> /// 致命级别 /// </summary> Fatal } /// <summary> /// 日志信息 /// </summary> public class LogInfo { /// <summary> /// 时间 /// </summary> public DateTime Time { get; set; } /// <summary> /// 线程id /// </summary> public int ThreadId { get; set; } /// <summary> /// 日志级别 /// </summary> public LogLevel LogLevel { get; set; } /// <summary> /// 异常源 /// </summary> public string Source { get; set; } /// <summary> /// 异常信息 /// </summary> public string Message { get; set; } /// <summary> /// 异常对象 /// </summary> public Exception Exception { get; set; } /// <summary> /// 日志类型 /// </summary> public string ExceptionType { get; set; } /// <summary> /// 请求路径 /// </summary> public string RequestUrl { get; set; } /// <summary> /// 客户端代理 /// </summary> public string UserAgent { get; set; } } /// <summary> /// 日志组件 /// </summary> public class LogManager { /// <summary> /// 日志对象单例 /// </summary> static volatile LogManager _instance; static object _lock = new object();//双重验证锁 /// <summary> /// 获取日志对象的单例 /// </summary> public static LogManager Instance { get { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new LogManager(); } } } return _instance; } } static ConcurrentQueue<Tuple<string, string>> LogQueue = new ConcurrentQueue<Tuple<string, string>>(); /// <summary> /// 自定义事件 /// </summary> public static event Action<LogInfo> Event; static DateTime _now { get { return DateTime.Now; } }//日志记录时间 private LogManager() { var writeTask = new Task((obj) => { while (true) { Pause.WaitOne(1000, true); List<string[]> temp = new List<string[]>(); Tuple<string, string> logItem; LogQueue.TryDequeue(out logItem); if (logItem != null) { string logPath = logItem.Item1; string logMergeContent = string.Concat(logItem.Item2, "----------------------------------------------------------------------------------------------------------------------\r\n"); string[] logArr = temp.FirstOrDefault(d => d[0].Equals(logPath)); if (logArr != null) { logArr[1] = string.Concat(logArr[1], logMergeContent); } else { logArr = new[] { logPath, logMergeContent }; temp.Add(logArr); foreach (var item in temp) { WriteText(item[0], item[1]); } } } } }, null, TaskCreationOptions.LongRunning); writeTask.Start(); } private static AutoResetEvent Pause => new AutoResetEvent(false); /// <summary> /// 日志存放目录,默认日志放在当前应用程序运行目录下的logs文件夹中 /// </summary> public static string LogDirectory { get { return Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory).Any(s => s.Contains("Web.config")) ? AppDomain.CurrentDomain.BaseDirectory + @"App_Data\Logs\" : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"); } set { } } /// <summary> /// 写入Info级别的日志 /// </summary> /// <param name="info"></param> public void Info(string info) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(info).ToUpper()}\r\n日志消息:{info}\r\n")); var log = new LogInfo() { LogLevel = LogLevel.Info, Message = info, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId }; Event?.Invoke(log); } /// <summary> /// 写入Info级别的日志 /// </summary> /// <param name="source"></param> /// <param name="info"></param> public void Info(string source, string info) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(info).ToUpper()}\r\n日志资源:{source}\r\n日志消息:{info}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Info, Message = info, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source }; Event?.Invoke(log); } /// <summary> /// 写入Info级别的日志 /// </summary> /// <param name="source"></param> /// <param name="info"></param> public void Info(Type source, string info) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(info).ToUpper()}\r\n日志资源名:{source.FullName}\r\n日志消息:{info}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Info, Message = info, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source.FullName }; Event?.Invoke(log); } /// <summary> /// 写入debug级别日志 /// </summary> /// <param name="debug">异常对象</param> public void Debug(string debug) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(debug).ToUpper()}\r\n日志消息:{debug}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Debug, Message = debug, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId }; Event?.Invoke(log); } /// <summary> /// 写入debug级别日志 /// </summary> /// <param name="source">异常源的类型</param> /// <param name="debug">异常对象</param> public void Debug(string source, string debug) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(debug).ToUpper()}\r\n日志资源:{source}\r\n日志消息:{debug}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Debug, Message = debug, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source }; Event?.Invoke(log); } /// <summary> /// 写入debug级别日志 /// </summary> /// <param name="source">异常源的类型</param> /// <param name="debug">异常对象</param> public void Debug(Type source, string debug) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(debug).ToUpper()}\r\n日志资源:{source.FullName}\r\n日志消息:{debug}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Debug, Message = debug, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source.FullName }; Event?.Invoke(log); } /// <summary> /// 写入error级别日志 /// </summary> /// <param name="error">异常对象</param> public void Error(Exception error) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(error).ToUpper()}\r\n日志资源:{error.Source}\r\n日志消息:{error.Message}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Error, Message = error.Message, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = error.Source, Exception = error, ExceptionType = error.GetType().Name }; Event?.Invoke(log); } public void Error(string error) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(error).ToUpper()}\r\n日志消息:{error}\r\n")); var log = new LogInfo() { LogLevel = LogLevel.Error, Message = error, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId }; Event?.Invoke(log); } /// <summary> /// 写入error级别日志 /// </summary> /// <param name="source">异常源的类型</param> /// <param name="error">异常对象</param> public void Error(Type source, Exception error) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(error).ToUpper()}\r\n日志资源:{source.FullName}\r\n日志消息:{error.Message}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Error, Message = error.Message, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source.FullName, Exception = error, ExceptionType = error.GetType().Name }; Event?.Invoke(log); } /// <summary> /// 写入error级别日志 /// </summary> /// <param name="source">异常源的类型</param> /// <param name="error">异常信息</param> public void Error(Type source, string error) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(error).ToUpper()}\r\n日志资源:{source.FullName}\r\n日志消息:{error}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Error, Message = error, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source.FullName, //Exception = error, ExceptionType = error.GetType().Name }; Event?.Invoke(log); } /// <summary> /// 写入error级别日志 /// </summary> /// <param name="source">异常源的类型</param> /// <param name="error">异常对象</param> public void Error(string source, Exception error) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(error).ToUpper()}\r\n日志资源:{source}\r\n日志消息:{error.Message}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Error, Message = error.Message, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source, Exception = error, ExceptionType = error.GetType().Name }; Event?.Invoke(log); } /// <summary> /// 写入error级别日志 /// </summary> /// <param name="source">异常源的类型</param> /// <param name="error">异常信息</param> public void Error(string source, string error) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(error).ToUpper()}\r\n日志资源:{source}\r\n日志消息:{error}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Error, Message = error, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source, //Exception = error, ExceptionType = error.GetType().Name }; Event?.Invoke(log); } /// <summary> /// 写入fatal级别日志 /// </summary> /// <param name="fatal">异常对象</param> public void Fatal(Exception fatal) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(fatal).ToUpper()}\r\n日志资源:{fatal.Source}\r\n日志消息:{fatal.Message}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Fatal, Message = fatal.Message, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = fatal.Source, Exception = fatal, ExceptionType = fatal.GetType().Name }; Event?.Invoke(log); } /// <summary> /// 写入fatal级别日志 /// </summary> /// <param name="source">异常源的类型</param> /// <param name="fatal">异常对象</param> public void Fatal(Type source, Exception fatal) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间: {_now} \r\n线程ID:[ {Thread.CurrentThread.ManagedThreadId} ]\r\n日志级别: {nameof(fatal).ToUpper()} \r\n日志资源: {source.FullName} \r\n日志消息: {fatal.Message} \r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Fatal, Message = fatal.Message, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source.FullName, Exception = fatal, ExceptionType = fatal.GetType().Name }; Event?.Invoke(log); } /// <summary> /// 写入fatal级别日志 /// </summary> /// <param name="source">异常源的类型</param> /// <param name="fatal">异常对象</param> public void Fatal(Type source, string fatal) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间: {_now} \r\n线程ID:[ {Thread.CurrentThread.ManagedThreadId} ]\r\n日志级别: {nameof(fatal).ToUpper()} \r\n日志资源: {source.FullName} \r\n日志消息: {fatal} \r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Fatal, Message = fatal, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source.FullName, //Exception = fatal, ExceptionType = fatal.GetType().Name }; Event?.Invoke(log); } /// <summary> /// 写入fatal级别日志 /// </summary> /// <param name="source">异常源的类型</param> /// <param name="fatal">异常对象</param> public void Fatal(string source, Exception fatal) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间:{_now}\r\n线程ID:[{Thread.CurrentThread.ManagedThreadId}]\r\n日志级别:{nameof(fatal).ToUpper()}\r\n日志资源:{source}\r\n日志消息:{fatal.Message}\r\n")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Fatal, Message = fatal.Message, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source, Exception = fatal, ExceptionType = fatal.GetType().Name }; Event?.Invoke(log); } /// <summary> /// 写入fatal级别日志 /// </summary> /// <param name="source">异常源的类型</param> /// <param name="fatal">异常对象</param> public void Fatal(string source, string fatal) { LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"时间: {_now} \r\n线程ID:[ {Thread.CurrentThread.ManagedThreadId} ]\r\n日志级别: {nameof(fatal).ToUpper()} \r\n日志资源: {source} \r\n日志消息:{fatal}")); LogInfo log = new LogInfo() { LogLevel = LogLevel.Fatal, Message = fatal, Time = _now, ThreadId = Thread.CurrentThread.ManagedThreadId, Source = source, ExceptionType = fatal.GetType().Name }; Event?.Invoke(log); } private string GetLogPath() { string newFilePath; String logDir = string.IsNullOrEmpty(LogDirectory) ? Path.Combine(Environment.CurrentDirectory, "logs") : LogDirectory; if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); } string extension = ".log"; string fileNameNotExt = _now.ToString("yyyyMMdd"); string fileNamePattern = string.Concat(fileNameNotExt, "(*)", extension); List<string> filePaths = Directory.GetFiles(logDir, fileNamePattern, SearchOption.TopDirectoryOnly).ToList(); if (filePaths.Count > 0) { int fileMaxLen = filePaths.Max(d => d.Length); string lastFilePath = filePaths.Where(d => d.Length == fileMaxLen).OrderByDescending(d => d).FirstOrDefault(); if (new FileInfo(lastFilePath).Length > 1 * 1024 * 1024) { string no = new Regex(@"(?is)(?<=\()(.*)(?=\))").Match(Path.GetFileName(lastFilePath)).Value; Int32 tempno; bool parse = int.TryParse(no, out tempno); string formatno = $"({(parse ? (tempno + 1) : tempno)})"; string newFileName = String.Concat(fileNameNotExt, formatno, extension); newFilePath = Path.Combine(logDir, newFileName); } else { newFilePath = lastFilePath; } } else { string newFileName = string.Concat(fileNameNotExt, $"({0})", extension); newFilePath = Path.Combine(logDir, newFileName); } return newFilePath; } private void WriteText(string logPath, string logContent) { try { if (!File.Exists(logPath)) { File.CreateText(logPath).Close(); } using (StreamWriter sw = File.AppendText(logPath)) { sw.Write(logContent); } } catch (Exception e) { throw e; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构