Web APi之异常处理(NLog)

网上太杂乱,自己重新整理了一下,记录api访问日志,api异常日志和程序异常日志。

NLog

第一步则是下载我们需要的程序包,包括程序集以及配置文件

利用NLog记录日志同样可以实现如我们上篇文章利用Log4net来实现的那样,所以在这里就不多说,下面我们来讲另外一种方式,那就是利用.NET内置的跟踪级别类来进行记录日志。从而达到我们所需。
web.config配置:
<configuration>
  <configSections>
    <!--日志-->
    <section name="nlog" type="NLog.Config.ConfigSectionHandler,NLog" />
  </configSections>
  <nlog>
      <include file="${basedir}/NLog.config" /><!--包含文件 此处配置为前面引用NLog时添加的NLog.config文件-->
  </nlog>
</configuration>
在NLog.config配置文件中,我们添加如下进行日志的记录【注意:只是简单的利用了NLog,它还是比较强大,更多的详细内容请到官网或通过其他途径进行学习】
<?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"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
 
  <!-- optional, add some variables
https://github.com/nlog/NLog/wiki/Configuration-file#variables
  -->
  <variable name="myvar" value="myvalue"/>
 
  <!--
  See https://github.com/nlog/nlog/wiki/Configuration-file
  for information on customizing logging rules and outputs.
   -->
 
  <!-- 使用说明
                1.一般控制台调式日志(打印到控制台)
                logger.Trace("GetHotelBrand 操作数据库异常 sqlText : " + sqlText);
                2. 一般文本日志,如记录接口,响应值等 请求参数等(记录文本,支持异步),
                  logger.Info("GetHotelBrand 操作数据库异常 sqlText : " + sqlText);
                3.错误日志  一般影响到业务流程的正常使用 (记录到DB)
                logger.ErrorException("GetHotelBrand 操作数据库异常 sqlText : " + sqlText, ex);
                4.致命性错误  如金额数据,订单数据操作失败  (发送邮件通知)
                logger.FatalException("GetHotelBrand 操作数据库异常 sqlText : " + sqlText, ex);
  -->
 
  <targets>
 
    <!--
    add your targets here
    See https://github.com/nlog/NLog/wiki/Targets for possible targets.
    See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
    -->
    <!--调式打印控制台日志-->
    <target name="console" xsi:type="ColoredConsole" layout="[${date:format=yyyy-MM-dd HH\:mm\:ss}][${level}] ${message} ${exception}"/>
 
    <!-- 记录一般INFO文本日志(启用异步) -->
    <target name="logfile" xsi:type="AsyncWrapper" queueLimit="5000" overflowAction="Discard">
      <target xsi:type="File" fileName="${basedir}/logs/${date:format=yyyyMMdd}.log"  layout="${longdate} ${uppercase:${level}} ${message}"  maxArchiveFiles="100" />
    </target>
 
    <!-- 发生错误异常记录数据库日志 -->
    <target name="database" xsi:type="Database"  useTransactions="true" connectionString="Data Source=xxxxxxxx;Initial Catalog=Log;Persist Security Info=True;User ID=sa;Password=123456"  commandText="insert into NLogException_HomeinnsInterface([CreateOn],[Origin],[LogLevel], [Message], [Exception],[StackTrace]) values (getdate(), @origin, @logLevel, @message,@exception, @stackTrace);">
      <!--日志来源-->
      <parameter name="@origin" layout="${callsite}"/>
      <!--日志等级-->
      <parameter name="@logLevel" layout="${level}"/>
      <!--日志消息-->
      <parameter name="@message" layout="${message}"/>
      <!--异常信息-->
      <parameter name="@exception" layout="${exception}" />
      <!--堆栈信息-->
      <parameter name="@stackTrace" layout="${stacktrace}"/>
    </target>
 
    <!-- 发生致命错误发送邮件日志 -->
    <target name="email" xsi:type="Mail"
               header="-----header------"
               footer="-----footer-----"
               layout="${longdate} ${level} ${callsite} ${message} ${exception:format=Message, Type, ShortType, ToString, Method, StackTrace}"
               html="false"
               encoding="UTF-8"
               addNewLines="true"
               subject="${message}"
               to=""
               from=""
               body="${longdate} ${level} ${callsite} ${message} ${exception:format=Message, Type, ShortType, ToString, Method, StackTrace}"
               smtpUserName=""
               enableSsl="false"
               smtpPassword=""
               smtpAuthentication="Basic"
               smtpServer="smtp.163.com"
               smtpPort="25">
    </target>
 
    <!--
    Write events to a file with the date in the filename.
    <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
            layout="${longdate} ${uppercase:${level}} ${message}" />
    -->
  </targets>
 
  <rules>
    <!-- add your logging rules here -->
    <logger name="*" minlevel="Trace" writeTo="console" />
    <logger name="*" minlevel="Info" writeTo="logfile" />
    <!--<logger name="*" minlevel="Error" writeTo="database"/>
    <logger name="*" minlevel="Fatal" writeTo="email" />-->
   
    <!--
    Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace)  to "f"
    <logger name="*" minlevel="Debug" writeTo="f" />
    -->
  </rules>
</nlog>

第二步

 

建立Log处理类和异常处理过滤属性
AppLog.cs 异常处理类
    public sealed class AppLog : ITraceWriter
    {
        //日志写入
        private static readonly Logger AppLogger = LogManager.GetCurrentClassLogger();
        private static readonly Lazy<Dictionary<TraceLevel, Action<string>>> LoggingMap = new Lazy<Dictionary<TraceLevel, Action<string>>>(() => new Dictionary<TraceLevel, Action<string>>
        {
            {TraceLevel.Info,AppLogger.Info },
            {TraceLevel.Debug,AppLogger.Debug },
            {TraceLevel.Error,AppLogger.Error },
            {TraceLevel.Fatal,AppLogger.Fatal },
            {TraceLevel.Warn,AppLogger.Warn }
        });
 
 
        private Dictionary<TraceLevel, Action<string>> Logger
        {
            get { return LoggingMap.Value; }
        }
 
        /// <summary>
        /// 跟踪编写器接口实现
        /// </summary>
        /// <param name="request"></param>
        /// <param name="category"></param>
        /// <param name="level"></param>
        /// <param name="traceAction"></param>
        public void Trace(HttpRequestMessage request, string category, TraceLevel level, Action<TraceRecord> traceAction)
        {
            if (level != TraceLevel.Off)//未禁用日志跟踪
            {
                if (traceAction != null && traceAction.Target != null)
                {
                    category = category + Environment.NewLine + "Action Parameters : " + JsonConvert.SerializeObject(traceAction.Target);
                }
                var record = new TraceRecord(request, category, level);
                if (traceAction != null)
                {
                    traceAction(record);
                }
                //  traceAction?.Invoke(record);
                Log(record);
            }
            //throw new NotImplementedException();
        }
        /// <summary>
        /// 日志写入
        /// </summary>
        /// <param name="record"></param>
        private void Log(TraceRecord record)
        {
            var message = new StringBuilder();
 
            /**************************运行日志****************************/
 
            if (!string.IsNullOrWhiteSpace(record.Message))
            {
                message.Append("").Append(record.Message + Environment.NewLine);
            }
 
            if (record.Request != null)
            {
                if (record.Request.Method != null)
                {
                    message.Append("Method : " + record.Request.Method + Environment.NewLine);
                }
 
                if (record.Request.RequestUri != null)
                {
                    message.Append("").Append("URL : " + record.Request.RequestUri + Environment.NewLine);
                }
 
                if (record.Request.Headers != null && record.Request.Headers.Contains("Token") && record.Request.Headers.GetValues("Token") != null && record.Request.Headers.GetValues("Token").FirstOrDefault() != null)
                {
                    message.Append("").Append("Token : " + record.Request.Headers.GetValues("Token").FirstOrDefault() + Environment.NewLine);
                }
            }
 
            if (!string.IsNullOrWhiteSpace(record.Category))
            {
                message.Append("").Append(record.Category);
            }
 
            if (!string.IsNullOrWhiteSpace(record.Operator))
            {
                message.Append(" ").Append(record.Operator).Append(" ").Append(record.Operation);
            }
 
            //***************************异常日志***********************************//
            if (record.Exception != null && !string.IsNullOrWhiteSpace(record.Exception.GetBaseException().Message))
            {
                var exceptionType = record.Exception.GetType();
                message.Append(Environment.NewLine);
                message.Append("").Append("Error : " + record.Exception.GetBaseException().Message + Environment.NewLine);
 
            }
            //日志写入本地文件
            Logger[record.Level](Convert.ToString(message) + Environment.NewLine);
        }
    }

 

AbnormalFilterAttribute.cs 记录调用api产生的异常日志
 
   public class AbnormalFilterAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext actionExecutedContext)
        {
            GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new AppLog());
            var trace = GlobalConfiguration.Configuration.Services.GetTraceWriter();
            trace.Error(actionExecutedContext.Request, "Controller : " + actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName + Environment.NewLine + "Action : " + actionExecutedContext.ActionContext.ActionDescriptor.ActionName, actionExecutedContext.Exception);
            var exceptionType = actionExecutedContext.Exception.GetType();
            if (exceptionType == typeof(ValidationException))
            {
                var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(actionExecutedContext.Exception.Message), ReasonPhrase = "ValidationException" };
                throw new HttpResponseException(resp);
            }
            else if (exceptionType == typeof(UnauthorizedAccessException))
            {
                throw new HttpResponseException(actionExecutedContext.Request.CreateResponse(HttpStatusCode.Unauthorized));
            }
            else
            {
                throw new HttpResponseException(actionExecutedContext.Request.CreateResponse(HttpStatusCode.InternalServerError));
            }
            //base.OnException(actionExecutedContext);
        }
    }

 

AppErrorAttribute.cs 记录程序异常信息
 
   public class AppErrorAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext filterContext)
        {
            base.OnException(filterContext);
            if (!filterContext.ExceptionHandled)
            {
                string ControllerName = string.Format("{0}Controller", filterContext.RouteData.Values["controller"] as string);
                string ActionName = filterContext.RouteData.Values["action"] as string;
 
                GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new AppLog());
                var trace = GlobalConfiguration.Configuration.Services.GetTraceWriter();
                trace.Error(filterContext.HttpContext.Request.AsHttpRequestMessage(),
                    "Controller : " + ControllerName + Environment.NewLine + "Action : " + ActionName, filterContext.Exception.GetBaseException().Message);
                var exceptionType = filterContext.Exception.GetType();
                if (exceptionType == typeof(ValidationException))
                {
                    var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(filterContext.Exception.Message), ReasonPhrase = "ValidationException" };
                    throw new HttpResponseException(resp);
                }
                else if (exceptionType == typeof(UnauthorizedAccessException))
                {
                    throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized));
                }
                else
                {
                    throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError));
                }
            }
        }
 
    }

LogFilterAttribute.cs Api调用日志记录

    
   public class LogFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new AppLog());
            var trace = GlobalConfiguration.Configuration.Services.GetTraceWriter();
            trace.Info(
                actionContext.Request,
                "Controller : " + actionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName + Environment.NewLine + "Action : " + actionContext.ActionDescriptor.ActionName,
                "JSON",
                actionContext.ActionArguments);
            //base.OnActionExecuting(actionContext);
        }
 

第三步 

程序引用配置
App_Data -> FilterConfig.cs 
  public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
            filters.Add(new AppErrorAttribute());
        }
 
        public static void RegisterHttpFilters(HttpFilterCollection filters)
        {
            //记录controler请求日志
            //filters.Add(new LogFilterAttribute());
            //记录异常
            filters.Add(new AbnormalFilterAttribute());
        }
    }
 
Global.asax.cs 
        protected void Application_Start()
        {
            //日志配置过滤
            FilterConfig.RegisterHttpFilters(GlobalConfiguration.Configuration.Filters);
        }

 

第四步

使用到的拓展:
HttpRequestBaseExtensions.cs
 
   public static class HttpRequestBaseExtensions
    {
        public static HttpRequestMessage AsHttpRequestMessage(this HttpRequestBase request)
        {
            if (request == null)
            {
                return null;
            }
            var httpRequest = new HttpRequestMessage(new HttpMethod(request.HttpMethod), request.Url);
            request.CopyHeadersTo(httpRequest);
 
            if (request.Form != null)
            {
                // Avoid a request message that will try to read the request stream twice for already parsed data.
                httpRequest.Content = new FormUrlEncodedContent(request.Form.AsKeyValuePairs());
            }
            else if (request.InputStream != null)
            {
                httpRequest.Content = new StreamContent(request.InputStream);
            }
 
            return httpRequest;
        }
 
        /// <summary>
        /// Clones an <see cref="HttpWebRequest" /> in order to send it again.
        /// </summary>
        /// <param name="message">The message to set headers on.</param>
        /// <param name="request">The request with headers to clone.</param>
        public static void CopyHeadersTo(this HttpRequestBase request, HttpRequestMessage message)
        {
            if (request == null)
            {
                return;
            }
 
            foreach (string headerName in request.Headers)
            {
                string[] headerValues = request.Headers.GetValues(headerName);
                if (!message.Headers.TryAddWithoutValidation(headerName, headerValues))
                {
                    message.Content.Headers.TryAddWithoutValidation(headerName, headerValues);
                }
            }
        }
    }
CollectionExtensions.cs 
   public static class CollectionExtensions
    {
        /// <summary>
        /// Enumerates all members of the collection as key=value pairs.
        /// </summary>
        /// <param name="nvc">The collection to enumerate.</param>
        /// <returns>A sequence of pairs.</returns>
        public static IEnumerable<KeyValuePair<string, string>> AsKeyValuePairs(this NameValueCollection nvc)
        {
            foreach (string key in nvc)
            {
                foreach (string value in nvc.GetValues(key))
                {
                    yield return new KeyValuePair<string, string>(key, value);
                }
            }
        }
    }
 

 

posted @ 2017-09-08 14:13  Paulson  阅读(730)  评论(0)    收藏  举报