分享 NET 5.x 自定义文件日志实现 原汁原味

  • 2021 年 12 月 24 日
  • 笔记

 

下面直接贴出实现代码

 

FileLoggerProvider

/// <summary>
    /// 文件记录器提供商
    /// </summary>
    public class FileLoggerProvider : ILoggerProvider
    {

        /// <summary>
        /// 配置
        /// </summary>
        private readonly IConfiguration configuration;

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="configuration">配置</param>
        public FileLoggerProvider(IConfiguration configuration)
        {
            this.configuration = configuration;
        }

        /// <summary>
        /// 创建记录器
        /// </summary>
        /// <param name="categoryName">类别名称</param>
        /// <returns></returns>
        public ILogger CreateLogger(string categoryName)
        {
            return new FileLogger(configuration, categoryName);
        }

        /// <summary>
        /// 释放方法
        /// </summary>
        public void Dispose()
        {
        }
    }
  Lock
/// <summary>
    /// IO锁
    /// </summary>
    public static class Lock
    {

        /// <summary>
        /// 文件读写锁
        /// </summary>
        public static readonly ReaderWriterLockSlim fileLockSlim = null;

        ///// <summary>
        ///// 数据库读写锁
        ///// </summary>
        //public static readonly ReaderWriterLockSlim dbLockSlim = null;

        /// <summary>
        /// 构造方法
        /// </summary>
        static Lock()
        {
            fileLockSlim = new ReaderWriterLockSlim();
            //dbLockSlim = new ReaderWriterLockSlim();
        }
    }

  FileLogger

/// <summary>
    /// 文件记录器
    /// </summary>
    public class FileLogger : ILogger
    {

        /// <summary>
        /// 配置
        /// </summary>
        private readonly IConfiguration configuration;

        /// <summary>
        /// 类别名称
        /// </summary>
        private readonly string categoryName;

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="configuration">配置</param>
        /// <param name="categoryName">类别名称</param>
        public FileLogger(IConfiguration configuration, string categoryName)
        {
            this.configuration = configuration;
            this.categoryName = categoryName;
        }

        /// <summary>
        /// 开始范围
        /// </summary>
        /// <typeparam name="TState">状态类型</typeparam>
        /// <param name="state">状态</param>
        /// <returns></returns>
        public IDisposable BeginScope<TState>(TState state)
        {
            return null;
        }

        /// <summary>
        /// 是否使用
        /// </summary>
        /// <param name="logLevel">日志级别</param>
        /// <returns></returns>
        public bool IsEnabled(LogLevel logLevel)
        {
            var list = new List<IConfigurationSection>();
            list.AddRange(configuration.GetSection("Logging:LogLevel").GetChildren());
            list.AddRange(configuration.GetSection("Logging:FileLog:LogLevel").GetChildren());

            var category = list.LastOrDefault(f => this.categoryName.StartsWith(f.Key));

            if (category == null)
            {
                category = list.LastOrDefault(f => f.Key == "Default");
            }

            if (category != null && Enum.TryParse(typeof(LogLevel), category.Value, out var level))
            {
                return (int)(LogLevel)level <= (int)logLevel;
            }
            return 2 <= (int)logLevel;
        }

        /// <summary>
        /// 日志
        /// </summary>
        /// <typeparam name="TState">状态类型</typeparam>
        /// <param name="logLevel">日志级别</param>
        /// <param name="eventId">事件ID</param>
        /// <param name="state">状态</param>
        /// <param name="exception">异常</param>
        /// <param name="formatter">格式化委托</param>
        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (IsEnabled(logLevel))
            {
                try
                {
                    Lock.fileLockSlim.EnterWriteLock();
                    var baseDirectory = configuration.GetSection("Logging:FileLog:BaseDirectory").Value;
                    var fileName = configuration.GetSection("Logging:FileLog:FileName").Value;
                    var extensionName = configuration.GetSection("Logging:FileLog:ExtensionName").Value;

                    var directory = Path.Combine(AppContext.BaseDirectory, string.IsNullOrWhiteSpace(baseDirectory) ? "app_log" : baseDirectory);

                    if (!Directory.Exists(directory))
                    {
                        Directory.CreateDirectory(directory);
                    }
                    if (string.IsNullOrWhiteSpace(fileName))
                    {
                        fileName = DateTime.Now.ToString("yyyy-MM-dd");
                    }
                    else
                    {
                        fileName = DateTime.Now.ToString(fileName);
                    }
                    extensionName = string.IsNullOrWhiteSpace(extensionName) ? ".log" : extensionName;

                    var path = Path.Combine(directory, $"{fileName}{extensionName}");
                    var flag = true;
                    if (File.Exists(path))
                    {
                        var maxSize = configuration.GetSection("Logging:FileLog:MaxFileSize").Value;
                        var fileInfo = new FileInfo(path);
                        flag = fileInfo.Length / 1024.00 > (string.IsNullOrWhiteSpace(maxSize) ? 2048.00 : Convert.ToDouble(maxSize));
                    }

                    var streamWrite = flag ? File.CreateText(path) : File.AppendText(path);
                    var dateTimeFormart = configuration.GetSection("Logging:FileLog:DateTimeFormat").Value;

                    var logTime = DateTime.Now.ToString((string.IsNullOrWhiteSpace(dateTimeFormart) ? "yyyy-MM-dd HH:mm:ss.fff" : dateTimeFormart));
                    var message = formatter(state, exception);

                    var stackTrace = exception?.StackTrace;

                    var template = configuration.GetSection("Logging:FileLog:Template").Value;

                    if (string.IsNullOrWhiteSpace(template))
                    {
                        streamWrite.WriteLine($"日志时间:{logTime}  类别名称:{categoryName}  日志级别:{logLevel}  消息:{message}");

                        if (!string.IsNullOrWhiteSpace(stackTrace))
                        {
                            streamWrite.WriteLine(stackTrace);
                        }
                    }
                    else
                    {
                        template = template.Replace("{logTime}", logTime, StringComparison.OrdinalIgnoreCase);
                        template = template.Replace("{catetoryName}", categoryName, StringComparison.OrdinalIgnoreCase);
                        template = template.Replace("{eventId}", eventId.Id.ToString(), StringComparison.OrdinalIgnoreCase);
                        template = template.Replace("{eventName}", eventId.Name, StringComparison.OrdinalIgnoreCase);
                        template = template.Replace("{logLevel}", logLevel.ToString(), StringComparison.OrdinalIgnoreCase);
                        template = template.Replace("{message}", message, StringComparison.OrdinalIgnoreCase);
                        template = template.Replace("{stackTrace}", stackTrace, StringComparison.OrdinalIgnoreCase);
                        template = template.Trim();
                        streamWrite.WriteLine(template);
                    }

                    streamWrite.WriteLine();
                    streamWrite.Close();

                    var directoryInfo = new DirectoryInfo(directory);
                    var fileInfos = directoryInfo.GetFiles();
                    var fileCount = Convert.ToInt32(configuration.GetSection("Logging:FileLog:MaxFileCount").Value);
                    if (fileInfos.Length > fileCount && fileCount > 0)
                    {
                        var removeFileInfo = fileInfos.OrderBy(o => o.CreationTime).ThenBy(o => o.LastWriteTime).SkipLast(fileCount);
                        foreach (var item in removeFileInfo)
                        {
                            File.Delete(item.FullName);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"写入文件日志异常:{ex.Message}");
                    Console.WriteLine(ex.StackTrace);
                }
                finally
                {
                    Lock.fileLockSlim.ExitWriteLock();
                }
            }
        }
    }
  ILoggingBuilderExtensions
/// <summary>
    /// 日志生成器扩展类
    /// </summary>
    public static class ILoggingBuilderExtensions
    {

        /// <summary>
        /// 添加文件日志
        /// </summary>
        /// <param name="loggingBuilder">日志构建</param>
        public static ILoggingBuilder AddFileLog(this ILoggingBuilder loggingBuilder)
        {
            loggingBuilder.Services.AddSingleton<FileLoggerProvider>();
            var sevices = loggingBuilder.Services.BuildServiceProvider();
            return loggingBuilder.AddProvider(sevices.GetService<FileLoggerProvider>());
        }

    }

  

Json配置格式示例

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
    },
    "FileLog": {
      "LogLevel": {
        "Default": "Information"
      },
      "BaseDirectory": "app_log",
      "FileName": "yyyy-MM-dd",
      "ExtensionName": ".log",
      "Template": "LogTime:{LogTime}  CatetoryName:{CatetoryName}  LogLevel:{LogLevel}\r\n{Message}\r\n{StackTrace}\r\n",
      "MaxFileCount": 10,
      "MaxFileSize": 2048,
      "DateTimeFormat": "yyyy-MM-dd HH:mm:ss.fff"
    }
  }
}