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黑马
原文地址:https://www.cnblogs.com/hmit/p/10769576.html