天天看點

C# 使用MVC和lo4net記錄發生異常,并顯示自定義錯誤頁面

  使用日志元件log4net記錄異常日志

到達效果:

在控制器層自動捕獲錯誤,并把錯誤儲存到資料庫,之後顯示希望使用者看到的錯誤頁面

1 首先安裝log4net

(1)“程式包管理器控制台”用指令安裝:先要打開控制台,在“工具”→“NuGet程式包管理器”→“程式包管理器控制台”這樣就可以打開程式包管理器控制台。

C# 使用MVC和lo4net記錄發生異常,并顯示自定義錯誤頁面

(2)也可以在項目中添加對log4net.dll的引用

2   配置日志檔案

1 。

 如果是CS程式,則在預設的App.config檔案(沒有建立一個)中添加内容;如果是BS程式,則添加到Web.config檔案中

<section name="log4net"

type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />

  </configSections>
<log4net>
</log4net>
           

再按照以下添加檔案方式配置

2 添加日志的配置檔案Log4net.xml,再從後面通過Log4netHelper幫助類執行個體化

日志類型

Appenders

Appenders用來定義日志的輸出方式,

經常使用的輸出方式有:

AdoNetAppender 将日志記錄到資料庫中。可以采用SQL和存儲過程兩種方式。

FileAppender 将日志輸出到檔案。

 代碼如下:

<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
  </appender>
           

定義日志的類型

可以按照本地檔案的日志記錄,比如

<log4net>
  <!--錯誤日志類-->
  <logger name="logerror"><!--日志類的名字-->
    <level value="ALL" /><!--定義記錄的日志級别-->
    <appender-ref ref="ErrorAppender" /><!--記錄到哪個媒體中去-->
  </logger>
  <!--資訊日志類-->
  <logger name="loginfo">
    <level value="ALL" />
    <appender-ref ref="InfoAppender" />
  </logger>
  <!--錯誤日志附加媒體-->
  <appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender"><!-- name屬性指定其名稱,type則是log4net.Appender命名空間的一個類的名稱,意思是,指定使用哪種媒體-->
    <param name="File" value="Log\\LogError\\" /><!--日志輸出到exe程式這個相對目錄下-->
    <param name="AppendToFile" value="true" /><!--輸出的日志不會覆寫以前的資訊-->
    <param name="MaxSizeRollBackups" value="100" /><!--備份檔案的個數-->
    <param name="MaxFileSize" value="10240" /><!--當個日志檔案的最大大小-->
    <param name="StaticLogFileName" value="false" /><!--是否使用靜态檔案名-->
    <param name="DatePattern" value="yyyyMMdd&quot;.htm&quot;" /><!--日志檔案名-->
    <param name="RollingStyle" value="Date" /><!--檔案建立的方式,這裡是以Date方式建立-->
    <!--錯誤日志布局-->
    <layout type="log4net.Layout.PatternLayout">
      <param name="ConversionPattern" value="&lt;HR COLOR=red&gt;%n異常時間:%d [%t] &lt;BR&gt;%n異常級别:%-5p &lt;BR&gt;%n異 常 類:%c [%x] &lt;BR&gt;%n%m &lt;BR&gt;%n &lt;HR Size=1&gt;"  />
    </layout>
  </appender>
  <!--資訊日志附加媒體-->
  <appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
    <param name="File" value="Log\\LogInfo\\" />
    <param name="AppendToFile" value="true" />
    <param name="MaxFileSize" value="10240" />
    <param name="MaxSizeRollBackups" value="100" />
    <param name="StaticLogFileName" value="false" />
    <param name="DatePattern" value="yyyyMMdd&quot;.htm&quot;" />
    <param name="RollingStyle" value="Date" />
    <!--資訊日志布局-->
    <layout type="log4net.Layout.PatternLayout">
      <param name="ConversionPattern" value="&lt;HR COLOR=blue&gt;%n日志時間:%d [%t] &lt;BR&gt;%n日志級别:%-5p &lt;BR&gt;%n日 志 類:%c [%x] &lt;BR&gt;%n%m &lt;BR&gt;%n &lt;HR Size=1&gt;"  />
    </layout>
  </appender>
</log4net>
           

 使用C#代碼初始化lo4net

log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(Server.MapPath("~/Web.config")));
           

3,而我使用的是資料庫的方式,檔案如下:

<?xml version="1.0" encoding="utf-8" ?>
<log4net>

  <logger name="WebExceptionLog">
    <level value="ALL" />
    <appender-ref ref="AdoNetAppender" />
  </logger>

  <logger name="ServiceExceptionLog">
    <level value="ALL" />
    <appender-ref ref="AdoNetAppender" />
  </logger>

  <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
    <bufferSize value="1" />
    <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    <connectionString value="{connectionString}" />
    <commandText value="INSERT INTO [Log4Net] ([Date],[Host],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @host, @thread, @log_level, @logger, @message, @exception)" />
    <parameter>
      <parameterName value="@log_date" />
      <dbType value="DateTime" />
      <layout type="log4net.Layout.RawTimeStampLayout" />
    </parameter>
    <parameter>
      <parameterName value="@thread" />
      <dbType value="String" />
      <size value="255" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%thread" />
      </layout>
    </parameter>

    <parameter>
      <parameterName value="@host" />
      <dbType value="String" />
      <size value="50" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%property{log4net:HostName}" />
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@log_level" />
      <dbType value="String" />
      <size value="50" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%level" />
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@logger" />
      <dbType value="String" />
      <size value="255" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%logger" />
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@message" />
      <dbType value="String" />
      <size value="4000" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%message" />
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@exception" />
      <dbType value="String" />
      <size value="4000" />
      <layout type="log4net.Layout.ExceptionLayout" />
    </parameter>
  </appender>

</log4net>
           

4 添加本地配置檔案讀取類,通過這個公共類很友善讀取本地配置XML檔案

public class ConfigContext
    {
        public IConfigService ConfigService { get; set; }

        /// <summary>
        /// 預設以檔案形式存取配置
        /// </summary>
        public ConfigContext() : this(new FileConfigService())
        {
        }
        
        public ConfigContext(IConfigService pageContentConfigService)
        {
            this.ConfigService = pageContentConfigService;
        }

        public virtual T Get<T>(string index = null) where T : ConfigFileBase, new()
        {
            var result = new T();
            this.VilidateClusteredByIndex(result, index);
            result = this.GetConfigFile<T>(index);

            return result;
        }

        public void Save<T>(T configFile, string index = null) where T : ConfigFileBase
        {
            this.VilidateClusteredByIndex(configFile, index);

            configFile.Save();

            var fileName = this.GetConfigFileName<T>(index);
            this.ConfigService.SaveConfig(fileName, SerializationHelper.XmlSerialize(configFile));
        }

        private T GetConfigFile<T>(string index = null) where T : ConfigFileBase, new()
        {
            var result = new T();

            var fileName = this.GetConfigFileName<T>(index);
            var content = this.ConfigService.GetConfig(fileName);
            if (content == null)
            {
                this.ConfigService.SaveConfig(fileName, string.Empty);
            }
            else if (!string.IsNullOrEmpty(content))
            {
                try
                {
                    result = (T)SerializationHelper.XmlDeserialize(typeof(T), content);
                }
                catch
                {
                    result = new T();
                }
            }

            return result;
        }

        public virtual void VilidateClusteredByIndex<T>(T configFile, string index) where T : ConfigFileBase
        {
            //if (configFile.ClusteredByIndex && string.IsNullOrEmpty(index))
            //    throw new Exception("調用時沒有提供配置檔案的分區索引");
        }

        public virtual string GetConfigFileName<T>(string index = null)
        {
            var fileName = typeof(T).Name;
            if (!string.IsNullOrEmpty(index))
                fileName = string.Format("{0}_{1}", fileName, index);
            return fileName;
        }
    }
           

添加log.xml

<?xml version="1.0" encoding="utf-16"?>
  <Log xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Log>Data Source=Data Source;Initial Catalog=Catalog;uid=uid;pwd=pwd;MAX Pool Size=512;Min Pool Size=50;Connection Lifetime=30</Log>
 
  </Log>
           

5.添加Log4netHelper類實列化配置檔案

public static class Log4NetHelper
    {
        static Log4NetHelper()
        {
            //初始化log4net配置
            var config = CachedConfigContext.Current.ConfigService.GetConfig("log4net");
            //重寫log4net配置裡的連接配接字元串
            config = config.Replace("{connectionString}", CachedConfigContext.Current.Log);
            var ms = new MemoryStream(Encoding.Default.GetBytes(config));
            log4net.Config.XmlConfigurator.Configure(ms);
        }

        public static void Debug(LoggerType loggerType, object message, Exception e)
        {
            var logger = LogManager.GetLogger(loggerType.ToString());
            logger.Debug(SerializeObject(message), e);
        }

        public static void Error(LoggerType loggerType, object message, Exception e)
        {
            var logger = LogManager.GetLogger(loggerType.ToString());
            logger.Error(SerializeObject(message), e);
        }

        public static void Info(LoggerType loggerType, object message, Exception e)
        {
            var logger = LogManager.GetLogger(loggerType.ToString());
            logger.Info(SerializeObject(message), e);
        }

        public static void Fatal(LoggerType loggerType, object message, Exception e)
        {
            var logger = LogManager.GetLogger(loggerType.ToString());
            logger.Fatal(SerializeObject(message), e);
        }

        public static void Warn(LoggerType loggerType, object message, Exception e)
        {
            var logger = LogManager.GetLogger(loggerType.ToString());
            logger.Warn(SerializeObject(message), e);
        }

        private static object SerializeObject(object message)
        {
            if (message is string || message == null)
                return message;
            else
                return JsonConvert.SerializeObject(message, new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
        }
    }

    public enum LoggerType
    {
        WebExceptionLog,
        ServiceExceptionLog
    }
           

6寫一個所有controllb繼承的controllbase父類,所有通路的controller發生的異常在這裡處理并記錄

public class ControllerBase : Controller
    {
        /// <summary>
        /// 發生異常寫Log
        /// </summary>
        /// <param name="filterContext"></param>
          protected override void OnException(ExceptionContext filterContext)
        {
            base.OnException(filterContext);
            var e = filterContext.Exception;

            var str = "錯誤記錄";
            Log4NetHelper.Error(LoggerType.WebExceptionLog, str, e);
        }
    }
           

7、在WebConfig中把過濾器配置啟動

<customErrors mode="On">
    </customErrors>
           

8控制器的代碼報錯時,會跳轉到~/Views/Shared/Error.cshtml頁面。mode="Off"頁面不會跳轉直接顯示錯誤資訊。

9綁定異常過濾器(過濾範圍是在controller的action方法中。)

public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }
   }