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();
                }
            }
        }
    }
}

  参考:支持多线程的日志记录类实现

posted @   ꧁执笔小白꧂  阅读(790)  评论(5编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
历史上的今天:
2020-03-04 HTML中正确设置表格table边框border的三种办法
点击右上角即可分享
微信分享提示