• net core体系-web应用程序-4asp.net core2.0 项目实战(1)-11项目日志解决方案


    本文目录
    1. Net下日志记录
    2. NLog的使用
        2.1 添加nuget引用NLog.Web.AspNetCore
        2.2 配置文件设置
        2.3 依赖配置及调用
        2.4 日志类型介绍
        2.5 产生的日志文件
    3. 基于Microsoft.Extensions.Logging封装
        3.1 添加引用Microsoft.Extensions.Logging
        3.2 实现我们的Logger
        3.3 调用WLogger
    2018-03-28 补充

    4. 总结

    1.  Net下日志记录

      Net Freamwork框架下在日志记录框架有很多,常见的有NLog、Log4Net、Loggr和内置 Microsoft.Diagnostics.Trace/Debug/TraceSource等。Asp.Net Core 2.0下大部分框架已不支持,Microsoft提供Microsoft.Extensions.Logging供大家实现自己的记录日志框架。现在笔者了解到的NLog已支持Net Core,下面我们介绍下nlog在项目中的使用以及基于Microsoft.Extensions.Logging封装自己的日志记录类。

    1.  NLog的使用

      2.1添加nuget引用NLog.Web.AspNetCore

      

      2.2配置文件设置

        在Asp.Net Core 2.0项目实战项目中,我们把配置文件统一放在configs文件夹中,方便管理。读取时用Path.Combine("configs", "nlog.config")即可。下面是nlog.config的配置。

    复制代码
    <?xml version="1.0" encoding="utf-8" ?>
    <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          autoReload="true"
          internalLogLevel="Warn"
          internalLogFile="internal-nlog.txt">
    
      <!--define various log targets-->
      <targets>
        <!--write logs to file-->
        <target xsi:type="File" name="allfile" fileName="nlog-all-${shortdate}.log"
                     layout="${longdate}|${logger}|${uppercase:${level}}|${message} ${exception}" />
        <target xsi:type="File" name="ownFile-web" fileName="nlog-my-${shortdate}.log"
                     layout="${longdate}|${logger}|${uppercase:${level}}|${message} ${exception}" />
        <target xsi:type="Null" name="blackhole" />
      </targets>
    
      <rules>
        <!--All logs, including from Microsoft-->
        <logger name="*" minlevel="Trace" writeTo="allfile" />
    
        <!--Skip Microsoft logs and so log only own logs-->
        <logger name="Microsoft.*" minlevel="Trace" writeTo="blackhole" final="true" />
        <logger name="*" minlevel="Trace" writeTo="ownFile-web" />
      </rules>
    
    </nlog>
    复制代码

      2.3依赖配置及调用

        在startup.cs中配置日志工厂,添加使用的服务配置后在项目中就可以调用。

    复制代码
         /// <summary>
            /// 配置
            /// </summary>
            /// <param name="app"></param>
            /// <param name="env"></param>
            /// <param name="loggerFactory"></param>
            public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
            {
                loggerFactory.AddNLog();//添加NLog  
                //读取Nlog配置文件,这里如果是小写,文件也一定是小写,否则linux下不识别  
                env.ConfigureNLog(Path.Combine("configs", "nlog.config"));
        }
    复制代码

         nlog调用,如在Controller中调用,如:在HomeController中

      2.4 日志类型介绍

    复制代码
    public enum LogLevel
    {
      Debug = 1,
      Verbose = 2,
      Information = 3,
      Warning = 4,
      Error = 5,
      Critical = 6,
      None = int.MaxValue
    }
    复制代码

      2.5产生的日志文件

        日志的位置默认是在binDebug etcoreapp2.0下面

     

        日志文件内容根据文件名可以很方便的区分开,其中nlog-all包含的内比较多,nlog-my中就只包含了我们记录日志的内容,大家动手试一下。

    3.基于Microsoft.Extensions.Logging封装

      由于受老项目webform影响,记录日志是在第三方类库dll中封装好了帮助类,这样在可以在项目中任何位置方便调用,这里我的nc.common工具库WLogger基于Microsoft NET Core的日志模型主要由三个核心对象构成,它们分别是Logger、LoggerProvider和LoggerFactory。现在只实现了文件记录日志txt,数据库模式有业务需求的朋友可自己扩展。

      3.1添加引用Microsoft.Extensions.Logging

        扩展微软日志记录框架,集成一个自己的Logger,现在扩展的是txt形式,后续可参考完善数据库模式。添加引用dll后,增加配置文件并配置,这里我先加在appsettings.json文件中,主要是配置是否开启日志和日志记录。

      3.2 实现我们的Logger

        首先实现日志工厂的扩展LoggerFactoryExtensions,为net core 依赖注入LoggerFactory扩张一个方法,提供增加日志写文件方式的入口。

    复制代码
    using Microsoft.Extensions.Logging;
    
    namespace NC.Common
    {
        public static class LoggerFactoryExtensions
        {
            public static ILoggerFactory AddFileLogger(this ILoggerFactory factory)
            {
                factory.AddProvider(new FileLoggerProvider());
                return factory;
            }
        }
    }
    复制代码

        然后实现ILoggerProvider接口,FileLoggerProvider提供程序真正具有日志写入功能的Logger。

    复制代码
    using Microsoft.Extensions.Logging;
    
    namespace NC.Common
    {
        public class FileLoggerProvider : ILoggerProvider
        {
            /// <summary>
            /// 默认构造函数,根据Provider进此构造函数
            /// </summary>
            /// <param name="categoryName"></param>
            /// <returns></returns>
            public ILogger CreateLogger(string categoryName)
            {
                return new FileLogger(categoryName);
            }
    
            public void Dispose()
            {
            }
        }
    }
    复制代码

        最后实现ILogger接口FileLogger继承并进行封装,方便写入文本日志。

    复制代码
    using Microsoft.Extensions.Logging;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    
    namespace NC.Common
    {
        public class FileLogger : ILogger
        {
            private string name;
            private bool IsOpen;
            private string WPath;
            
            public FileLogger(string _name)
            {
                name = _name;
            }
            public IDisposable BeginScope<TState>(TState state)
            {
                return null;
            }
            /// <summary>
            /// 是否禁用
            /// </summary>
            /// <param name="logLevel"></param>
            /// <returns></returns>
            public bool IsEnabled(LogLevel logLevel)
            {
                return true;
            }
            /// <summary>
            /// 实现接口ILogger
            /// </summary>
            /// <typeparam name="TState"></typeparam>
            /// <param name="logLevel"></param>
            /// <param name="eventId"></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)
            {
                IsOpen = UtilConf.GetSection("WLogger")["IsOpen"] == "true" ? true : false;
                if (IsOpen)
                {
                    //获取日志信息
                    var message = formatter?.Invoke(state, exception);
                    //日志写入文件
                    LogToFile(logLevel, message);
                }
            }
    
            /// <summary>
            /// 记录日志
            /// </summary>
            /// <param name="level">等级</param>
            /// <param name="message">日志内容</param>
            private void LogToFile(LogLevel level, string message)
            {
                var filename = GetFilename();
                var logContent = GetLogContent(level, message);
                File.AppendAllLines(filename, new List<string> { logContent }, Encoding.UTF8);
            }
            /// <summary>
            /// 获取日志内容
            /// </summary>
            /// <param name="level">等级</param>
            /// <param name="message">日志内容</param>
            /// <returns></returns>
            private string GetLogContent(LogLevel level, string message)
            {
                return $"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.h3")}]{level}|{name}|{message}";
            }
    
            private string DirectorySeparatorChar = Path.DirectorySeparatorChar.ToString();//目录分隔符
            /// <summary>
            /// 获取文件名
            /// </summary>
            private string GetFilename()
            {
                var dir = "";
                WPath = UtilConf.GetSection("WLogger")["WPath"];
                if (WPath.IndexOf(":") > -1)
                {
                    dir = WPath;
                }
                else
                {
                    //此方法不是真正的获取应用程序的当前方法,而是执行dotnet命令所在目录
                    dir = Directory.GetCurrentDirectory() + WPath;
                    
                }
                if (!Directory.Exists(dir))
                    Directory.CreateDirectory(dir);
                var result = $"{dir}/WLog-{DateTime.Now.ToString("yyyy-MM-dd")}.txt".Replace("/",DirectorySeparatorChar);
    
                return result;
            }
        }
    }
    复制代码

      3.3 调用WLogger

        在nc.common类库中封装好logger实现后,在调用连接使用数据库在core类库中调用实例如下。

        首先我们先做一下封装调用类

    复制代码
    using Microsoft.Extensions.Logging;
    
    namespace NC.Common
    {
        public class UtilLogger<T>
        {
            private static ILogger iLog;
            public static ILogger Log
            {
                get
                {
                    if (iLog != null) return iLog;
    
                    ////第一种写法
                    //ILoggerFactory loggerFactory = new LoggerFactory();
                    //loggerFactory.AddFileLogger();
                    //iLog = loggerFactory.CreateLogger<DbCommand>();
    
                    //第二种写法
                    iLog = new LoggerFactory().AddFileLogger().CreateLogger<T>();
                    return iLog;
                }
                set => iLog = value;
            }
        }
    }
    复制代码

        然后在DbCommand中调用就可以直接写成:

          public static ILogger Log = UtilLogger<DbCommand>.Log;//日志记录

          Log. LogInformation(string);

          Log.LogError(string)

        详细方法还可以参考

    2018-03-28补充:

      日志记录与全局错误处理结合,首先创建全局错误过滤类HttpGlobalExceptionFilter并在startup.cs中ConfigureServices方法下添加

    services.AddMvc(options =>
                {
                    options.Filters.Add(typeof(HttpGlobalExceptionFilter));//全局错误过滤日志
                }).AddControllersAsServices();

      然后实现OnException方法并记录日志,这样系统只要报异常,日志 就会被记录下来。

    复制代码
    using System;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Filters;
    using Microsoft.Extensions.Logging;
    using NC.Common;
    
    namespace NC.MVC
    {
        /// <summary>
        /// 错误处理类
        /// </summary>
        public class HttpGlobalExceptionFilter : IExceptionFilter
        {
            private readonly IHostingEnvironment _env;
            public static ILogger Log = UtilLogger<HttpGlobalExceptionFilter>.Log;//日志记录
    
            public HttpGlobalExceptionFilter(IHostingEnvironment env)
            {
                this._env = env;
            }
    
            public ContentResult FailedMsg(string msg = null)
            {
                string retResult = "{"status":" + JHEnums.ResultStatus.Failed + ","msg":"" + msg + ""}";//, msg);
                string json = JsonHelper.ObjectToJSON(retResult);
                return new ContentResult() { Content = json };
            }
            public void OnException(ExceptionContext filterContext)
            {
                if (filterContext.ExceptionHandled)
                    return;
    
                //执行过程出现未处理异常
                Exception ex = filterContext.Exception;
    #if DEBUG
                if (filterContext.HttpContext.Request.IsAjaxRequest())
                {
                    string msg = null;
    
                    if (ex is Exception)
                    {
                        msg = ex.Message;
                        filterContext.Result = this.FailedMsg(msg);
                        filterContext.ExceptionHandled = true;
                        return;
                    }
                }
    
                this.LogException(filterContext);
                return;
    #endif
                if (filterContext.HttpContext.Request.IsAjaxRequest())
                {
                    string msg = null;
    
                    if (ex is Exception)
                    {
                        msg = ex.Message;
                    }
                    else
                    {
                        this.LogException(filterContext);
                        msg = "服务器错误";
                    }
    
                    filterContext.Result = this.FailedMsg(msg);
                    filterContext.ExceptionHandled = true;
                    return;
                }
                else
                {
                    //对于非 ajax 请求
                    this.LogException(filterContext);
                    return;
                }
            }
            /// <summary>
            /// 记录日志
            /// </summary>
            /// <param name="filterContext"></param>
            private void LogException(ExceptionContext filterContext)
            {
                string mid = filterContext.HttpContext.Request.Query["mid"];//codding 后续完善每个action带一个id
                var areaName = (filterContext.RouteData.DataTokens["area"] == null ? "" : filterContext.RouteData.DataTokens["area"]).ToString().ToLower();
                var controllerName = (filterContext.RouteData.Values["controller"]).ToString().ToLower();
                var actionName = (filterContext.RouteData.Values["action"]).ToString().ToLower();
    
                #region --记录日志 codding 后续增加自定义字段的日志。如:记录Controller/action,模块ID等--
                Log.LogError(filterContext.Exception, "全局错误:areaName:" + areaName + ",controllerName:" + controllerName + ",action:" + actionName);
                #endregion
            }
        }
    }
    复制代码

    4.总结

      不管是生产环境还是开发环境,总会碰到这样或那样的问题,这时日志记录就为我们提供了记录分析问题的便利性,net core 2.0下记录日志功能是最需要我们及时实现的功能,这样为我们接下来的学习提供技术支撑。另外net core 生态还不完善,很多功能需要我们自己动手去实现,在这里希望大家多动手去实现去分享,文中有不清楚或有问题欢迎留言讨论。

    参考:

    https://msdn.microsoft.com/magazine/mt694089

    https://www.cnblogs.com/artech/p/inside-net-core-logging-2.html

    https://www.cnblogs.com/calvinK/p/5673218.html

    IT黑马
  • 相关阅读:
    POJ 2155 Matrix
    Codeforces 626D Jerry's Protest 「数学组合」「数学概率」
    Codeforces 626E Simple Skewness 「数学」「二分」
    Codeforces 633D
    Codeforces 631C
    二分查找
    CodeForces 617C【序枚举】
    HDU 4405 【概率dp】
    ZOJ 3329 【概率DP】
    POJ 2096 【期望DP】
  • 原文地址:https://www.cnblogs.com/hmit/p/10769576.html
Copyright © 2020-2023  润新知