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 _mutex = new Mutex(false, "LogUtil.Mutex.180740C3B1C44D428683D35F84F97E22");

        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++;
                    _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
                {
                    _mutex.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
                {
                    _mutex.ReleaseMutex();
                }
            }, CancellationToken.None, TaskCreationOptions.None, _scheduler);
        }
        #endregion

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

        /// <summary>
        /// 写错误日志
        /// </summary>
        public static Task Error(string log)
        {
            return Task.Factory.StartNew(() =>
            {
                try
                {
                    _mutex.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
                {
                    _mutex.ReleaseMutex();
                }
            }, CancellationToken.None, TaskCreationOptions.None, _scheduler);
        }
        #endregion

        #region 写操作日志
        /// <summary>
        /// 写操作日志
        /// </summary>
        public static Task Log(string log)
        {
            return Task.Factory.StartNew(() =>
            {
                try
                {
                    _mutex.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
                {
                    _mutex.ReleaseMutex();
                }
            }, CancellationToken.None, TaskCreationOptions.None, _scheduler);
        }
        #endregion

    }

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

        Info,

        Error
    }
    #endregion

}
View Code

相关文章: