C#写日志工具类

CPU:i5-8265U 硬盘:固态硬盘 测试结果:每秒写入文件大约1万到3万条日志,每条日志的字符串长度是140多个字符

支持多线程并发,支持多进程并发,支持按文件大小分隔日志文件

LogUtil.cs代码:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace Utils
{
    /// <summary>
    /// 写日志类
    /// </summary>
    public class LogUtil
    {
        #region 字段
        private static string _path = null;

        private static Mutex _mutexDebug = new Mutex(false, "LogUtil.Mutex.Debug.252F8025254D4DAA8EFB7FFE177F13E0");
        private static Mutex _mutexInfo = new Mutex(false, "LogUtil.Mutex.Info.180740C3B1C44D428683D35F84F97E22");
        private static Mutex _mutexError = new Mutex(false, "LogUtil.Mutex.Error.81273C1400774A3B8310C2EC1C3AFFFF");

        private static ConcurrentDictionary<string, int> _dictIndex = new ConcurrentDictionary<string, int>();
        private static ConcurrentDictionary<string, long> _dictSize = new ConcurrentDictionary<string, long>();
        private static ConcurrentDictionary<string, FileStream> _dictStream = new ConcurrentDictionary<string, FileStream>();
        private static ConcurrentDictionary<string, StreamWriter> _dictWriter = new ConcurrentDictionary<string, StreamWriter>();

        private static ConcurrentDictionary<string, string> _dictPathFolders = new ConcurrentDictionary<string, string>();

        private static TaskSchedulerEx _scheduler = new TaskSchedulerEx(2, 2);

        private static int _fileSize = 10 * 1024 * 1024; //日志分隔文件大小
        #endregion

        #region 写文件
        /// <summary>
        /// 写文件
        /// </summary>
        private static void WriteFile(LogType logType, string log, string path)
        {
            try
            {
                FileStream fs = null;
                StreamWriter sw = null;

                if (!(_dictStream.TryGetValue(logType.ToString() + path, out fs) && _dictWriter.TryGetValue(logType.ToString() + path, out sw)))
                {
                    foreach (string key in _dictWriter.Keys)
                    {
                        if (key.StartsWith(logType.ToString()))
                        {
                            StreamWriter item;
                            _dictWriter.TryRemove(key, out item);
                            item.Close();
                        }
                    }

                    foreach (string key in _dictStream.Keys)
                    {
                        if (key.StartsWith(logType.ToString()))
                        {
                            FileStream item;
                            _dictStream.TryRemove(key, out item);
                            item.Close();
                        }
                    }

                    if (!Directory.Exists(Path.GetDirectoryName(path)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(path));
                    }

                    fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
                    sw = new StreamWriter(fs);
                    _dictWriter.TryAdd(logType.ToString() + path, sw);
                    _dictStream.TryAdd(logType.ToString() + path, fs);
                }

                fs.Seek(0, SeekOrigin.End);
                sw.WriteLine(log);
                sw.Flush();
                fs.Flush();
            }
            catch (Exception ex)
            {
                string str = ex.Message;
            }
        }
        #endregion

        #region 生成日志文件路径
        /// <summary>
        /// 生成日志文件路径
        /// </summary>
        private static string CreateLogPath(LogType logType, string log)
        {
            try
            {
                if (_path == null)
                {
                    UriBuilder uri = new UriBuilder(Assembly.GetExecutingAssembly().CodeBase);
                    _path = Path.GetDirectoryName(Uri.UnescapeDataString(uri.Path));
                }

                string pathFolder = Path.Combine(_path, "Log\" + logType.ToString() + "\");
                if (!_dictPathFolders.ContainsKey(pathFolder))
                {
                    if (!Directory.Exists(Path.GetDirectoryName(pathFolder)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(pathFolder));
                    }
                    _dictPathFolders.TryAdd(pathFolder, pathFolder);
                }

                int currentIndex;
                long size;
                string strNow = DateTime.Now.ToString("yyyyMMdd");
                string strKey = pathFolder + strNow;
                if (!(_dictIndex.TryGetValue(strKey, out currentIndex) && _dictSize.TryGetValue(strKey, out size)))
                {
                    _dictIndex.Clear();
                    _dictSize.Clear();

                    GetIndexAndSize(pathFolder, strNow, out currentIndex, out size);
                    if (size >= _fileSize)
                    {
                        currentIndex++;
                        size = 0;
                    }
                    _dictIndex.TryAdd(strKey, currentIndex);
                    _dictSize.TryAdd(strKey, size);
                }

                int index = _dictIndex[strKey];
                string logPath = Path.Combine(pathFolder, strNow + (index == 1 ? "" : "_" + index.ToString()) + ".txt");

                _dictSize[strKey] += Encoding.UTF8.GetByteCount(log);
                if (_dictSize[strKey] > _fileSize)
                {
                    _dictIndex[strKey]++;
                    _dictSize[strKey] = 0;
                }

                return logPath;
            }
            catch (Exception ex)
            {
                string str = ex.Message;
                return null;
            }
        }
        #endregion

        #region 拼接日志内容
        /// <summary>
        /// 拼接日志内容
        /// </summary>
        private static string CreateLogString(LogType logType, string log)
        {
            return string.Format(@"{0} {1} {2}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), ("[" + logType.ToString() + "]").PadRight(7, ' '), log);
        }
        #endregion

        #region 获取初始Index和Size
        /// <summary>
        /// 获取初始Index和Size
        /// </summary>
        private static void GetIndexAndSize(string pathFolder, string strNow, out int index, out long size)
        {
            index = 1;
            size = 0;
            Regex regex = new Regex(strNow + "_*(\d*).txt");
            string[] fileArr = Directory.GetFiles(pathFolder);
            string currentFile = null;
            foreach (string file in fileArr)
            {
                Match match = regex.Match(file);
                if (match.Success)
                {
                    string str = match.Groups[1].Value;
                    if (!string.IsNullOrWhiteSpace(str))
                    {
                        int temp = Convert.ToInt32(str);
                        if (temp > index)
                        {
                            index = temp;
                            currentFile = file;
                        }
                    }
                    else
                    {
                        index = 1;
                        currentFile = file;
                    }
                }
            }

            if (currentFile != null)
            {
                FileInfo fileInfo = new FileInfo(currentFile);
                size = fileInfo.Length;
            }
        }
        #endregion

        #region 写调试日志
        /// <summary>
        /// 写调试日志
        /// </summary>
        public static Task Debug(string log)
        {
            return Task.Factory.StartNew(() =>
            {
                try
                {
                    _mutexDebug.WaitOne();

                    log = CreateLogString(LogType.Debug, log);
                    string path = CreateLogPath(LogType.Debug, log);
                    WriteFile(LogType.Debug, log, path);
                }
                catch (Exception ex)
                {
                    string str = ex.Message;
                }
                finally
                {
                    _mutexDebug.ReleaseMutex();
                }
            }, CancellationToken.None, TaskCreationOptions.None, _scheduler);
        }
        #endregion

        #region 写错误日志
        public static Task Error(Exception ex, string log = null)
        {
            return Error(string.IsNullOrEmpty(log) ? ex.Message + "
" + ex.StackTrace : (log + "") + ex.Message + "
" + ex.StackTrace);
        }

        /// <summary>
        /// 写错误日志
        /// </summary>
        public static Task Error(string log)
        {
            return Task.Factory.StartNew(() =>
            {
                try
                {
                    _mutexError.WaitOne();

                    log = CreateLogString(LogType.Error, log);
                    string path = CreateLogPath(LogType.Error, log);
                    WriteFile(LogType.Error, log, path);
                }
                catch (Exception ex)
                {
                    string str = ex.Message;
                }
                finally
                {
                    _mutexError.ReleaseMutex();
                }
            }, CancellationToken.None, TaskCreationOptions.None, _scheduler);
        }
        #endregion

        #region 写操作日志
        /// <summary>
        /// 写操作日志
        /// </summary>
        public static Task Log(string log)
        {
            return Task.Factory.StartNew(() =>
            {
                try
                {
                    _mutexInfo.WaitOne();

                    log = CreateLogString(LogType.Info, log);
                    string path = CreateLogPath(LogType.Info, log);
                    WriteFile(LogType.Info, log, path);
                }
                catch (Exception ex)
                {
                    string str = ex.Message;
                }
                finally
                {
                    _mutexInfo.ReleaseMutex();
                }
            }, CancellationToken.None, TaskCreationOptions.None, _scheduler);
        }
        #endregion

    }

    #region 日志类型
    /// <summary>
    /// 日志类型
    /// </summary>
    public enum LogType
    {
        Debug,

        Info,

        Error
    }
    #endregion

}
View Code

依赖的TaskSchedulerEx.cs代码:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Utils
{
    /// <summary>
    /// TaskScheduler扩展
    /// 每个实例都是独立线程池
    /// </summary>
    public class TaskSchedulerEx : TaskScheduler, IDisposable
    {
        #region 外部方法
        [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
        public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
        #endregion

        #region 变量属性事件
        private ConcurrentQueue<Task> _tasks = new ConcurrentQueue<Task>();
        private int _coreThreadCount = 0;
        private int _maxThreadCount = 0;
        private int _auxiliaryThreadTimeOut = 20000; //辅助线程释放时间
        private int _activeThreadCount = 0;
        private System.Timers.Timer _timer;
        private object _lockCreateTimer = new object();
        private bool _run = true;
        private AutoResetEvent _evt = new AutoResetEvent(false);

        /// <summary>
        /// 活跃线程数
        /// </summary>
        public int ActiveThreadCount
        {
            get { return _activeThreadCount; }
        }

        /// <summary>
        /// 核心线程数
        /// </summary>
        public int CoreThreadCount
        {
            get { return _coreThreadCount; }
        }

        /// <summary>
        /// 最大线程数
        /// </summary>
        public int MaxThreadCount
        {
            get { return _maxThreadCount; }
        }
        #endregion

        #region 构造函数
        /// <summary>
        /// TaskScheduler扩展
        /// 每个实例都是独立线程池
        /// </summary>
        /// <param name="coreThreadCount">核心线程数(大于或等于0,不宜过大)(如果是一次性使用,则设置为0比较合适)</param>
        /// <param name="maxThreadCount">最大线程数</param>
        public TaskSchedulerEx(int coreThreadCount = 10, int maxThreadCount = 20)
        {
            _maxThreadCount = maxThreadCount;
            CreateCoreThreads(coreThreadCount);
        }
        #endregion

        #region override GetScheduledTasks
        protected override IEnumerable<Task> GetScheduledTasks()
        {
            return _tasks;
        }
        #endregion

        #region override TryExecuteTaskInline
        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            return false;
        }
        #endregion

        #region override QueueTask
        protected override void QueueTask(Task task)
        {
            CreateTimer();
            _tasks.Enqueue(task);
            _evt.Set();
        }
        #endregion

        #region 资源释放
        /// <summary>
        /// 资源释放
        /// 队列中尚未执行的任务不再执行
        /// </summary>
        public void Dispose()
        {
            _run = false;

            if (_timer != null)
            {
                _timer.Stop();
                _timer.Dispose();
                _timer = null;
            }

            while (_activeThreadCount > 0)
            {
                _evt.Set();
            }
        }
        #endregion

        #region 创建核心线程池
        /// <summary>
        /// 创建核心线程池
        /// </summary>
        private void CreateCoreThreads(int? coreThreadCount = null)
        {
            if (coreThreadCount != null) _coreThreadCount = coreThreadCount.Value;

            for (int i = 0; i < _coreThreadCount; i++)
            {
                Interlocked.Increment(ref _activeThreadCount);
                Thread thread = null;
                thread = new Thread(new ThreadStart(() =>
                {
                    Task task;
                    while (_run)
                    {
                        if (_tasks.TryDequeue(out task))
                        {
                            TryExecuteTask(task);
                        }
                        else
                        {
                            _evt.WaitOne();
                        }
                    }
                    Interlocked.Decrement(ref _activeThreadCount);
                    if (_activeThreadCount == 0)
                    {
                        GC.Collect();
                        GC.WaitForPendingFinalizers();
                        if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                        {
                            SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
                        }
                    }
                }));
                thread.IsBackground = true;
                thread.Start();
            }
        }
        #endregion

        #region 创建辅助线程
        /// <summary>
        /// 创建辅助线程
        /// </summary>
        private void CreateThread()
        {
            Interlocked.Increment(ref _activeThreadCount);
            Thread thread = null;
            thread = new Thread(new ThreadStart(() =>
            {
                Task task;
                DateTime dt = DateTime.Now;
                while (_run && DateTime.Now.Subtract(dt).TotalMilliseconds < _auxiliaryThreadTimeOut)
                {
                    if (_tasks.TryDequeue(out task))
                    {
                        TryExecuteTask(task);
                        dt = DateTime.Now;
                    }
                    else
                    {
                        _evt.WaitOne(_auxiliaryThreadTimeOut);
                    }
                }
                Interlocked.Decrement(ref _activeThreadCount);
                if (_activeThreadCount == _coreThreadCount)
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                    {
                        SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
                    }
                }
            }));
            thread.IsBackground = true;
            thread.Start();
        }
        #endregion

        #region 创建定时器
        private void CreateTimer()
        {
            if (_timer == null) //_timer不为空时,跳过,不走lock,提升性能
            {
                if (_activeThreadCount >= _coreThreadCount && _activeThreadCount < _maxThreadCount) //活跃线程数达到最大线程数时,跳过,不走lock,提升性能
                {
                    lock (_lockCreateTimer)
                    {
                        if (_timer == null)
                        {
                            _timer = new System.Timers.Timer();
                            _timer.Interval = _coreThreadCount == 0 ? 1 : 500;
                            _timer.Elapsed += (s, e) =>
                            {
                                if (_activeThreadCount >= _coreThreadCount && _activeThreadCount < _maxThreadCount)
                                {
                                    if (_tasks.Count > 0)
                                    {
                                        if (_timer.Interval != 20) _timer.Interval = 20;
                                        CreateThread();
                                    }
                                    else
                                    {
                                        if (_timer.Interval != 500) _timer.Interval = 500;
                                    }
                                }
                                else
                                {
                                    if (_timer != null)
                                    {
                                        _timer.Stop();
                                        _timer.Dispose();
                                        _timer = null;
                                    }
                                }
                            };
                            _timer.Start();
                        }
                    }
                }
            }
        }
        #endregion

        #region 全部取消
        /// <summary>
        /// 全部取消
        /// 取消队列中尚未执行的任务
        /// </summary>
        public void CancelAll()
        {
            Task tempTask;
            while (_tasks.TryDequeue(out tempTask)) { }
        }
        #endregion

    }
}
View Code

依赖的RunHelper.cs代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Utils
{
    /// <summary>
    /// 线程工具类
    /// </summary>
    public static class RunHelper
    {
        #region 变量属性事件

        #endregion

        #region 线程中执行
        /// <summary>
        /// 线程中执行
        /// </summary>
        public static Task Run(this TaskScheduler scheduler, Action<object> doWork, object arg = null, Action<Exception> errorAction = null)
        {
            return Task.Factory.StartNew((obj) =>
            {
                try
                {
                    doWork(obj);
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run错误");
                }
            }, arg, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

        #region 线程中执行
        /// <summary>
        /// 线程中执行
        /// </summary>
        public static Task Run(this TaskScheduler scheduler, Action doWork, Action<Exception> errorAction = null)
        {
            return Task.Factory.StartNew(() =>
            {
                try
                {
                    doWork();
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run错误");
                }
            }, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

        #region 线程中执行
        /// <summary>
        /// 线程中执行
        /// </summary>
        public static Task<T> Run<T>(this TaskScheduler scheduler, Func<object, T> doWork, object arg = null, Action<Exception> errorAction = null)
        {
            return Task.Factory.StartNew<T>((obj) =>
            {
                try
                {
                    return doWork(obj);
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run错误");
                    return default(T);
                }
            }, arg, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

        #region 线程中执行
        /// <summary>
        /// 线程中执行
        /// </summary>
        public static Task<T> Run<T>(this TaskScheduler scheduler, Func<T> doWork, Action<Exception> errorAction = null)
        {
            return Task.Factory.StartNew<T>(() =>
            {
                try
                {
                    return doWork();
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run错误");
                    return default(T);
                }
            }, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

        #region 线程中执行
        /// <summary>
        /// 线程中执行
        /// </summary>
        public static async Task<T> RunAsync<T>(this TaskScheduler scheduler, Func<object, T> doWork, object arg = null, Action<Exception> errorAction = null)
        {
            return await Task.Factory.StartNew<T>((obj) =>
            {
                try
                {
                    return doWork(obj);
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run错误");
                    return default(T);
                }
            }, arg, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

        #region 线程中执行
        /// <summary>
        /// 线程中执行
        /// </summary>
        public static async Task<T> RunAsync<T>(this TaskScheduler scheduler, Func<T> doWork, Action<Exception> errorAction = null)
        {
            return await Task.Factory.StartNew<T>(() =>
            {
                try
                {
                    return doWork();
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run错误");
                    return default(T);
                }
            }, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

    }
}
View Code

测试代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Utils;

namespace LogUtilTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Task.Factory.StartNew(() =>
            {
                int n = 100000;
                string processId = Process.GetCurrentProcess().Id.ToString().PadLeft(8, ' ');
                List<Task> taskList = new List<Task>();
                string str = "    abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcda3.1415bcdabcdabcdabcdabc@#$%^&dabcdabcdabcdabcdabcdabcdabcdabcd";
                DateTime dtStart = DateTime.Now;
                for (int i = 1; i <= n; i++)
                {
                    Task task = LogUtil.Log("ProcessId:【" + processId + "】 测试" + i.ToString().PadLeft(8, '0') + str);
                    taskList.Add(task);
                    task = LogUtil.Debug("ProcessId:【" + processId + "】 测试" + i.ToString().PadLeft(8, '0') + str);
                    taskList.Add(task);
                }
                Task.WaitAll(taskList.ToArray());

                double sec = DateTime.Now.Subtract(dtStart).TotalSeconds;
                MessageBox.Show(n + "条日志完成,耗时" + sec.ToString("0.000") + "");
            });
        }
    }
}
View Code

测试结果截图:

原文地址:https://www.cnblogs.com/s0611163/p/4023859.html