.NET Core API框架实战(六) Logger实现Log写入本地系统

.net core 自带一个基础的logger框架Microsoft.Extensions.Logging。

微软默认实现了Microsoft.Extensions.Logging.Console.dll。控制台的日志输出和Microsoft.Extensions.Logging.Debug.dll调试输出。

logger框架主要几个类:LoggerFactory,Logger,LoggerProvider。

看名字就很好理解,都不需要解释。

实现我们自己的file logger只需要实现logger,loggerProvider即可。

第一步:配置json文件

为LoggerFactory扩张一个方法,提供增加日志写文件方式的入口。相关的配置来自appsettings.json

//写本地log文件
  //已创建logger的categoryName区分配置。可配置多规则多文件写入
  "FileLogging": {
    // 相对路径:log:current path+'\log'
    // 绝对路径:C://log
    // linux 绝对路径:/var/log/netcorewebapp
    //多环境部署,可以使用环境变量配置文件方式。
    "DefaultPath": "log", //默认文件地址
    "DefaultMaxMB": 500, //默认最大文件大小
    "DefaultFileName": "yyyyMMdd", //默认文件名字
    "DefaultRollingType": "Day", //默认文件新建方式
    //过滤器,以.分割,逐层匹配
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information",
      "NetCoreWebApp": "Debug",
      "NewFile": "Debug"
    }
  }

第二步:入口

为LoggerFactory扩张一个方法,提供增加日志写文件方式的入口。

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace Dianshi.P2P.Core.Common.Logger
{
    public static class FileLoggerExtensions
    {
        //add 日志文件创建规则,分割规则,格式化规则,过滤规则 to appsettings.json
        public static ILoggerFactory AddFile(this ILoggerFactory factory, IConfiguration configuration)
        {
            return AddFile(factory, new FileLoggerSettings(configuration));
        }
        public static ILoggerFactory AddFile(this ILoggerFactory factory, FileLoggerSettings fileLoggerSettings)
        {
            factory.AddProvider(new FileLoggerProvider(fileLoggerSettings));
            return factory;
        }
    }
}

第三步:实现我们的logger提供程序,实现ILoggerProvider接口

using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;

namespace Dianshi.P2P.Core.Common.Logger
{
    public class FileLoggerProvider : ILoggerProvider, IDisposable
    {
        FileLoggerSettings _configuration;
        readonly ConcurrentDictionary<string, InitLoggerModel> _loggerKeys = new ConcurrentDictionary<string, InitLoggerModel>();
        readonly ConcurrentDictionary<string, FileLogger> _loggers = new ConcurrentDictionary<string, FileLogger>();

        public FileLoggerProvider(FileLoggerSettings configuration)
        {
            _configuration = configuration;
            _configuration.ChangeToken.RegisterChangeCallback(p =>
            {
                //appsettings.json changed. reload settings.
                _configuration.Reload();

                //update loggers settings form new settings
                foreach (var item in this._loggers.Values)
                {
                    InitLoggerModel model = new InitLoggerModel();
                    InitLoggerSettings(item.Name, model);
                    InitLogger(model, item);
                }

            }, null);
        }
        public ILogger CreateLogger(string categoryName)
        {
            var loggerKey = this._loggerKeys.GetOrAdd(categoryName, p =>
            {
                InitLoggerModel model = new InitLoggerModel();
                InitLoggerSettings(categoryName, model);
                return model;
            });
            var key = loggerKey.FileDiretoryPath + loggerKey.FileNameTemplate;
            return this._loggers.GetOrAdd(key, p =>
            {
                var logger = new FileLogger(categoryName);
                InitLogger(loggerKey, logger);
                return logger;
            });
        }

        private static void InitLogger(InitLoggerModel model, FileLogger logger)
        {
            logger.FileNameTemplate = model.FileNameTemplate;
            logger.FileDiretoryPath = model.FileDiretoryPath;
            logger.MinLevel = model.MinLevel;
        }

        class InitLoggerModel
        {
            public LogLevel MinLevel { get; set; }
            public string FileDiretoryPath { get; set; }
            public string FileNameTemplate { get; set; }

            public override int GetHashCode()
            {
                return this.MinLevel.GetHashCode() + this.FileDiretoryPath.GetHashCode() + this.FileNameTemplate.GetHashCode();
            }
            public override bool Equals(object obj)
            {
                var b = obj as InitLoggerModel;
                if (b == null)
                    return false;
                return this.MinLevel == b.MinLevel && this.FileDiretoryPath == b.FileDiretoryPath && this.FileNameTemplate == b.FileNameTemplate;
            }

        }
        private void InitLoggerSettings(string categoryName, InitLoggerModel model)
        {
            model.MinLevel = LogLevel.Debug;
            var keys = this.GetKeys(categoryName);
            foreach (var item in keys)
            {
                var switchV = _configuration.GetSwitch(item);
                if (switchV.Item1)
                {
                    model.MinLevel = switchV.Item2;
                    break;
                }
            }
            model.FileDiretoryPath = this._configuration.DefaultPath;
            foreach (var item in keys)
            {
                var switchV = _configuration.GetDiretoryPath(item);
                if (switchV.Item1)
                {
                    model.FileDiretoryPath = switchV.Item2;
                    break;
                }
            }
            model.FileNameTemplate = this._configuration.DefaultFileName;
            foreach (var item in keys)
            {
                var switchV = _configuration.GetFileName(item);
                if (switchV.Item1)
                {
                    model.FileNameTemplate = switchV.Item2;
                    break;
                }
            }
        }

        IEnumerable<string> GetKeys(string categoryName)
        {
            while (!String.IsNullOrEmpty(categoryName))
            {
                // a.b.c
                //--result
                // a.b.c,a.b,a,Default
                yield return categoryName;
                var last = categoryName.LastIndexOf('.');
                if (last <= 0)
                {
                    yield return "Default";
                    yield break;
                }
                System.Diagnostics.Debug.WriteLine(categoryName + "--" + last);
                categoryName = categoryName.Substring(0, last);
            }
            yield break;

        }
        public void Dispose()
        {
        }
    }
}

第四步:实现我们的logger,实现ILogger接口。真正将log写入file

using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Text;
using System.Threading;

namespace Dianshi.P2P.Core.Common.Logger
{
    public class FileLogger : ILogger
    {
        static protected string delimiter = new string(new char[] { (char)1 });
        public FileLogger(string categoryName)
        {
            this.Name = categoryName;
        }
        class Disposable : IDisposable
        {
            public void Dispose()
            {
            }
        }
        Disposable _DisposableInstance = new Disposable();
        public IDisposable BeginScope<TState>(TState state)
        {
            return _DisposableInstance;
        }
        public bool IsEnabled(LogLevel logLevel)
        {
            return this.MinLevel <= logLevel;
        }
        public void Reload()
        {
            _Expires = true;
        }

        public string Name { get; private set; }

        public LogLevel MinLevel { get; set; }
        public string FileDiretoryPath { get; set; }
        public string FileNameTemplate { get; set; }
        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (!this.IsEnabled(logLevel))
                return;
            var msg = formatter(state, exception);
            this.Write(logLevel, eventId, msg, exception);
        }
        void Write(LogLevel logLevel, EventId eventId, string message, Exception ex)
        {
            EnsureInitFile();

            //TODO 提高效率 队列写!!!
            var log = String.Concat(DateTime.Now.ToString("HH:mm:ss"), '[', logLevel.ToString(), ']', '[',
                  Thread.CurrentThread.ManagedThreadId.ToString(), ',', eventId.Id.ToString(), ',', eventId.Name, ']',
                  delimiter, message, delimiter, ex?.ToString());
            lock (this)
            {
                this._sw.WriteLine(log);
            }
        }

        bool _Expires = true;
        string _FileName;
        protected StreamWriter _sw;
        void EnsureInitFile()
        {
            if (CheckNeedCreateNewFile())
            {
                lock (this)
                {
                    if (CheckNeedCreateNewFile())
                    {
                        InitFile();
                        _Expires = false;
                    }
                }
            }
        }
        bool CheckNeedCreateNewFile()
        {
            if (_Expires)
            {
                return true;
            }
            //TODO 使用 RollingType判断是否需要创建文件。提高效率!!!
            if (_FileName != DateTime.Now.ToString(this.FileNameTemplate))
            {
                return true;
            }
            return false;
        }
        void InitFile()
        {
            if (!Directory.Exists(this.FileDiretoryPath))
            {
                Directory.CreateDirectory(this.FileDiretoryPath);
            }
            var path = "";
            int i = 0;
            do
            {
                _FileName = DateTime.Now.ToString(this.FileNameTemplate);
                path = Path.Combine(this.FileDiretoryPath, _FileName + "_" + i + ".log");
                i++;
            } while (System.IO.File.Exists(path));
            var oldsw = _sw;
            _sw = new StreamWriter(new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read), Encoding.UTF8);
            _sw.AutoFlush = true;
            if (oldsw != null)
            {
                try
                {
                    _sw.Flush();
                    _sw.Dispose();
                }
                catch
                {
                }
            }
        }
    }
}

最后在我们项目中的Startup>Configure 注册

loggerFactory.AddFile(this.Configuration.GetSection("FileLogging"));

最后运行创建一个异常

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值