C#-多线程日志Helper
一、原单线程日志
/**
*┌──────────────────────────────────────────────────────────────┐
*│ 描 述:日志相关的工具类
*│ 作 者:执笔小白
*│ 版 本:1.0
*│ 创建时间:2020-6-13 15:40:56
*└──────────────────────────────────────────────────────────────┘
*┌──────────────────────────────────────────────────────────────┐
*│ 命名空间: WMSTOMESTT
*│ 类 名:ETools
*└──────────────────────────────────────────────────────────────┘
*/
using System;
using System.IO;
using System.Text;
namespace HOST_CONTROL_CENTER.Uril.LogHelper
{
public class LogHelper
{
// 写日志-不支持多线程
public static void WriteLogFile(string input, string logfilePath)
{
try
{
// exe的目录,web的请用:System.Web.Hosting.HostingEnvironment
// string logAdress = System.Environment.CurrentDirectory.ToString() + "\\Log\\"; // exe.config
// string logAdress = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase.ToString() + "\\Log\\"; // dll.config
string[] logfilePaths= logfilePath.Split('\\');
string logfileName = logfilePaths[logfilePaths.Length-1]; // 文件名
string logAdress = logfilePath.Replace(logfileName, ""); // 文件目录
if (!System.IO.Directory.Exists(logAdress))
{
System.IO.Directory.CreateDirectory(logAdress);//不存在就创建目录
}
if (!System.IO.Directory.Exists(logAdress))
{
System.IO.Directory.CreateDirectory(logAdress);//不存在就创建目录
}
///定义文件信息对象
FileInfo finfo = new FileInfo(logfilePath);
if (!finfo.Exists)
{
FileStream fs;
fs = File.Create(logfilePath);
fs.Close();
finfo = new FileInfo(logfilePath);
}
///判断文件是否存在以及是否大于2K
if (finfo.Length > 1024 * 1024 * 10)
{
/**/
///文件超过10MB则重命名
File.Move(logfilePath, logAdress + DateTime.Now.TimeOfDay + @"\" + logfileName);
/**/
///删除该文件
//finfo.Delete();
}
//finfo.AppendText();
/**/
///创建只写文件流
using (FileStream fs = finfo.OpenWrite())
{
/**/
///根据上面创建的文件流创建写数据流
StreamWriter w = new StreamWriter(fs, Encoding.UTF8);
/**/
///设置写数据流的起始位置为文件流的末尾
w.BaseStream.Seek(0, SeekOrigin.End);
w.WriteLine(" ");
w.WriteLine("---" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "----Start----------------------");
/**/
///写入当前系统时间并换行
/**/
///写入日志内容并换行
w.WriteLine(input);
/**/
///写入------------------------------------“并换行
w.WriteLine("------------------------End------------------------");
/**/
///清空缓冲区内容,并把缓冲区内容写入基础流
w.Flush();
/**/
///关闭写数据流
w.Close();
}
}
catch (Exception ex)
{ throw ex; }
}
}
#region 例子
public class ETest
{
public void WLogDemo()
{
StringBuilder strHead = new StringBuilder();
strHead.AppendLine("*****记录*****");
LogHelper.WriteLogFile(strHead.ToString(), "记录文件名");
}
}
#endregion
}
二、多线程日志的实现
1、日志信息对象类
namespace HOST_CONTROL_CENTER.Uril.LogHelper.LogThreed
{
/// <summary>
/// 日志信息对象类(用于在线程间共享)
/// </summary>
public class LogInfo
{
private int _ID;
/// <summary>
/// 日志ID
/// </summary>
public int ID
{
get { return _ID; }
set { _ID = value; }
}
private string _CreateTime;
/// <summary>
/// 日志时间
/// </summary>
public string CreateTime
{
get { return _CreateTime; }
set { _CreateTime = value; }
}
private string _Content;
/// <summary>
/// 日志内容
/// </summary>
public string Content
{
get { return _Content; }
set { _Content = value; }
}
private string _LogPath;
/// <summary>
/// 日志保存位置
/// </summary>
public string LogPath
{
get { return _LogPath; }
set { _LogPath = value; }
}
}
}
2、日志-同步事件类(用于线程间同步的事件对象)
using System.Threading;
namespace HOST_CONTROL_CENTER.Uril.LogHelper.LogThreed
{
/// <summary>
/// 日志-同步事件类(用于线程间同步的事件对象)
/// </summary>
public class SyncEvents
{
private EventWaitHandle _newItemEvent; // 添加新项
private EventWaitHandle _exitThreadEvent; // 退出线程
private WaitHandle[] _eventArray; // 线程组
/// <summary>
/// 构造器
/// </summary>
public SyncEvents()
{
_newItemEvent = new AutoResetEvent(false); // AutoResetEvent对象用来进行线程同步操作(表示线程同步事件在一个等待线程释放后收到信号时自动重置)
_exitThreadEvent = new ManualResetEvent(false); // ManualResetEvent是线程用来控制别一个线程的信号事件(表示线程同步事件中,收到信号时,必须手动重置该事件。 此类不能被继承。)
_eventArray = new WaitHandle[2];
_eventArray[0] = _newItemEvent;
_eventArray[1] = _exitThreadEvent;
}
/// <summary>
/// 获取要退出的线程
/// </summary>
public EventWaitHandle ExitThreadEvent
{
get { return _exitThreadEvent; }
}
/// <summary>
/// 获取新添加的项
/// </summary>
public EventWaitHandle NewItemEvent
{
get { return _newItemEvent; }
}
/// <summary>
/// 获取线程组
/// </summary>
public WaitHandle[] EventArray
{
get { return _eventArray; }
}
}
}
3、日志记录类-支持多线程
using System.Threading;
using System.Collections.Generic;
using System;
using System.Collections;
namespace HOST_CONTROL_CENTER.Uril.LogHelper.LogThreed
{
/// <summary>
/// 日志记录类-支持多线程
/// </summary>
public class Logger
{
private static Logger _logger;
private static object _lock = new object(); // 锁
private static Thread _thread; // 线程
private Queue<LogInfo> _queue; //日志队列
private SyncEvents _syncEvents; // 线程同步事件
/// <summary>
/// 构造器
/// </summary>
private Logger()
{
_queue = new Queue<LogInfo>();
_syncEvents = new SyncEvents();
}
/// <summary>
/// 获取日志记录类实例
/// </summary>
/// <returns></returns>
public static Logger GetLogger()
{
if (_logger == null)
{
// 加锁,防止多线程运行时,重复创建。
lock (_lock)
{
if (_logger == null)
{
_logger = new Logger();
}
}
}
return _logger;
}
#region 添加日志
private void AddLog(Object obj)
{
LogInfo log = obj as LogInfo;
if (!_syncEvents.ExitThreadEvent.WaitOne(0, false)) // 首先检查“退出线程”事件,因为 WaitOne 使用的第一个参数为零,该方法会立即返回,所以检查该事件的状态不会阻止当前线程。
{
lock (((ICollection)_queue).SyncRoot)
{
_queue.Enqueue(log);
_syncEvents.NewItemEvent.Set(); // 添加新项(启动一个新线程运行AddLog方法向共享队列中添加新日志)
Console.WriteLine("Input thread: add {0} items", log.ID);
}
}
}
/// <summary>
/// 添加日志
/// </summary>
/// <param name="log"></param>
public void Add(LogInfo log)
{
Thread t = new Thread(AddLog);
t.Start(log);
}
#endregion
#region 输出日志
/// <summary>
/// 日志保存
/// </summary>
private void Save()
{
int flag = 0;
while (flag >= 0) // 处理完所有线程时退出
{
if (_queue.Count == 0)
{
flag = WaitHandle.WaitAny(_syncEvents.EventArray);
if (flag == 1)
{
flag = -1;
}
}
lock (((ICollection)_queue).SyncRoot)
{
if (_queue.Count > 0)
{
LogInfo log = _queue.Dequeue(); // 移除队列
Console.WriteLine("Output Thread: process {0} items,{1}", log.ID,log.Content);
LogHelper.WriteLogFile(log.Content, log.LogPath); // 保存到文件
}
}
}
}
public void Run()
{
_thread = new Thread(Save);
_thread.Start();
}
#endregion
/// <summary>
/// 退出所有正在使用的线程
/// </summary>
public void Stop()
{
_syncEvents.ExitThreadEvent.Set();
}
}
}
觉得不需要,所以未对文件加锁。
4、添加的一个日志格式扩展类,作为使用参考(可以改成自己的)
using System;
namespace HOST_CONTROL_CENTER.Uril.LogHelper.LogThreed
{
public class AppLogs
{
// 本地MES的常规报错日志的保存地址
private static string _MesLogPath = System.Environment.CurrentDirectory.ToString();
// MES接口日志
private static string _MesAPILogPath = @"D:\MESlog\MES\";
// 工序日志
private static string _MESDataLogPath = @"D:\MESData\MES\";
/// <summary>
/// 常规报错日志
/// </summary>
public static void MesLog(string msgContent)
{
Logger logger = Logger.GetLogger();
logger.Run();
for (int i = 0; i < 1; i++)
{
LogInfo log = new LogInfo();
log.LogPath = _MesLogPath + @"\Log\" + DateTime.Now.ToString("yyyy-MM-dd") + ".csv";
log.ID = 1;
log.CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.SSS");
log.Content = msgContent;
logger.Add(log);
if (i == 1)
{
logger.Stop();
}
}
}
/// <summary>
/// MES接口日志
/// D:\MESlog\接口名\设备号_log_年-月-日;
/// </summary>
/// <param name="aPIName">接口名_操作名</param>
/// <param name="deviceNumber">设备号</param>
/// <param name="msgContent">消息内容</param>
public static void MesAPILog(string aPIName, string deviceNumber, string msgContent)
{
string iniPath = System.Environment.CurrentDirectory.ToString() + @"\MESConfig.ini";
//_MesAPILogPath = OperateFile_InI.ReadIniData("Log", "MesAPILogPath", "", iniPath);
Logger logger = Logger.GetLogger();
logger.Run();
for (int i = 0; i < 1; i++)
{
LogInfo log = new LogInfo();
log.ID = 1;
log.CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.SSS");
log.LogPath = _MesAPILogPath + aPIName + @"\" + deviceNumber + "_log_" + DateTime.Now.ToString("yyyy-MM-dd") + ".csv";
log.Content = msgContent;
logger.Add(log);
if (i == 1)
{
logger.Stop();
}
}
}
/// <summary>
/// 工序日志
/// 如:D:\MESData\MES\SFC\12B000000001_20150826105626.csv
/// </summary>
/// <param name="sFCName">SFC名</param>
/// <param name="deviceNumber">设备号</param>
/// <param name="msgContent">消息内容</param>
public static void MESDataLog(string sFCName, string deviceNumber, string msgContent)
{
// 获取
string iniPath = System.Environment.CurrentDirectory.ToString() + @"\MESConfig.ini";
//_MESDataLogPath = OperateFile_InI.ReadIniData("Log", "MESDataLogPath", "", iniPath);
Logger logger = Logger.GetLogger();
logger.Run();
for (int i = 0; i < 1; i++)
{
LogInfo log = new LogInfo();
log.ID = 1;
log.CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.SSS");
log.LogPath = _MESDataLogPath + sFCName + @"\" + deviceNumber + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".csv";
log.Content = msgContent;
logger.Add(log);
if (i == 1)
{
logger.Stop();
}
}
}
}
}
本文来自博客园,作者:꧁执笔小白꧂,转载请注明原文链接:https://www.cnblogs.com/qq2806933146xiaobai/p/15965201.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
2020-03-04 HTML中正确设置表格table边框border的三种办法