Log4Net应用于MVC监视Action的信息

在MVC项目中应用log4net记录Action的信息。


1.概述

log4net是.Net下一个非常优秀的开源日志记录组件。log4net记录日志的功能非常强大。它可以将日志分不同的等级,以不同的格式,输出到不同的媒介。本文主要是介绍如何在Visual Studio2008中使用log4net快速创建系统日志,如何扩展以输出自定义字段。


2.Log4Net主要组成部分

2.1Appenders

用来定义日志的输出方式,即日志要写到那种介质上去。较常用的Log4net已经实现好了,直接在配置文件中调用即可,可参见上面配置文件例子;当然也可以自己写一个,需要从log4net.Appender.AppenderSkeleton类继承。它还可以通过配置Filters和Layout来实现日志的过滤和输出格式。

已经实现的输出方式有:

AdoNetAppender 将日志记录到数据库中。可以采用SQL和存储过程两种方式。

AnsiColorTerminalAppender 将日志高亮输出到ANSI终端。

AspNetTraceAppender  能用asp.net中Trace的方式查看记录的日志。

BufferingForwardingAppender 在输出到子Appenders之前先缓存日志事件。

ConsoleAppender 将日志输出到应用程序控制台。

EventLogAppender 将日志写到Windows Event Log。

FileAppender 将日志输出到文件。

ForwardingAppender 发送日志事件到子Appenders。

LocalSyslogAppender 将日志写到local syslog service (仅用于UNIX环境下)。

MemoryAppender 将日志存到内存缓冲区。

NetSendAppender 将日志输出到Windows Messenger service.这些日志信息将在用户终端的对话框中显示。

OutputDebugStringAppender 将日志输出到Debuger,如果程序没有Debuger,就输出到系统Debuger。如果系统Debuger也不可用,将忽略消息。

RemoteSyslogAppender 通过UDP网络协议将日志写到Remote syslog service。

RemotingAppender 通过.NET Remoting将日志写到远程接收端。

RollingFileAppender 将日志以回滚文件的形式写到文件中。

SmtpAppender 将日志写到邮件中。

SmtpPickupDirAppender 将消息以文件的方式放入一个目录中,像IIS SMTP agent这样的SMTP代理就可以阅读或发送它们。

TelnetAppender 客户端通过Telnet来接受日志事件。

TraceAppender 将日志写到.NET trace 系统。

UdpAppender 将日志以无连接UDP数据报的形式送到远程宿主或用UdpClient的形式广播。

2.2 Filters

使用过滤器可以过滤掉Appender输出的内容。过滤器通常有以下几种:

DenyAllFilter 阻止所有的日志事件被记录

LevelMatchFilter 只有指定等级的日志事件才被记录

LevelRangeFilter 日志等级在指定范围内的事件才被记录

LoggerMatchFilter 与Logger名称匹配,才记录

PropertyFilter 消息匹配指定的属性值时才被记录

StringMathFilter 消息匹配指定的字符串才被记录

2.3 Layouts

Layout用于控制Appender的输出格式,可以是线性的也可以是XML。

一个Appender只能有一个Layout。

最常用的Layout应该是经典格式的PatternLayout,其次是SimpleLayout,RawTimeStampLayoutExceptionLayout。然后还有IRawLayout,XMLLayout等几个,使用较少。Layout可以自己实现,需要从log4net.Layout.LayoutSkeleton类继承,来输出一些特殊需要的格式,在后面扩展时就重新实现了一个Layout。

SimpleLayout简单输出格式,只输出日志级别与消息内容。

RawTimeStampLayout 用来格式化时间,在向数据库输出时会用到。

样式如“yyyy-MM-dd HH:mm:ss“

ExceptionLayout需要给Logger的方法传入Exception对象作为参数才起作用,否则就什么也不输出。输出的时候会包含Message和Trace。

PatterLayout使用最多的一个Layout,能输出的信息很多,使用方式可参见上面例子中的配置文件。PatterLayout的格式化字符串见文后附注8.1。

2.4 Loggers

Logger是直接和应用程序交互的组件。Logger只是产生日志,然后由它引用的Appender记录到指定的媒介,并由Layout控制输出格式。

Logger提供了多种方式来记录一个日志消息,也可以有多个Logger同时存在。每个实例化的Logger对象对被log4net作为命名实体(Named Entity)来维护。log4net使用继承体系,也就是说假如存在两个Logger,名字分别为a.b.c和a.b。那么a.b就是a.b.c的祖先。每个Logger都继承了它祖先的属性。所有的Logger都从Root继承,Root本身也是一个Logger。

日志的等级,它们由高到底分别为:

OFF >FATAL >ERROR >WARN > INFO > DEBUG  >ALL 

高于等级设定值方法(如何设置参见“配置文件详解”)都能写入日志,Off所有的写入方法都不写到日志里,ALL则相反。例如当我们设成Info时,logger.Debug就会被忽略而不写入文件,但是FATAL,ERROR,WARN,INFO会被写入,因为他们等级高于INFO

在具体写日志时,一般可以这样理解日志等级:

FATAL(致命错误):记录系统中出现的能使用系统完全失去功能,服务停止,系统崩溃等使系统无法继续运行下去的错误。例如,数据库无法连接,系统出现死循环。

ERROR(一般错误):记录系统中出现的导致系统不稳定,部分功能出现混乱或部分功能失效一类的错误。例如,数据字段为空,数据操作不可完成,操作出现异常等。

WARN(警告):记录系统中不影响系统继续运行,但不符合系统运行正常条件,有可能引起系统错误的信息。例如,记录内容为空,数据内容不正确等。

INFO(一般信息):记录系统运行中应该让用户知道的基本信息。例如,服务开始运行,功能已经开户等。

DEBUG (调试信息):记录系统用于调试的一切信息,内容或者是一些关键数据内容的输出。

Logger实现的ILog接口,ILog定义了5个方法(Debug,Inof,Warn,Error,Fatal)分别对不同的日志等级记录日志。这5个方法还有5个重载。以Debug为例说明一下,其它的和它差不多。

ILog中对Debug方法的定义如下:

void Debug(object message);

void Debug(object message,Exception ex);

还有一个布尔属性:

bool IsDebugEnabled {get; }

如果使用Debug(object message,Exception ex),则无论Layout中是否定义了%exception,默认配置下日志都会输出Exception。包括Exception的Message和Trace。如果使用Debug(object message),则日志是不会输出Exception。

最后还要说一个LogManager类,它用来管理所有的Logger。它的GetLogger静态方法,可以获得配置文件中相应的Logger:

log4net.ILog log = log4net.LogManager.GetLogger("logger-name");

2.5 Object Renders

它将告诉logger如何把一个对象转化为一个字符串记录到日志里。(ILog中定义的接口接收的参数是Object,而不是String。)

例如你想把Orange对象记录到日志中,但此时logger只会调用Orange默认的ToString方法而已。所以要定义一个OrangeRender类实现log4net.ObjectRender.IObjectRender接口,然后注册它(我们在本文中的扩展不使用这种方法,而是直接实现一个自定义的Layout)。这时logger就会知道如何把Orange记录到日志中了。

2.6 Repository

Repository主要用于日志对象组织结构的维护。

3、配置文件详解

3.1 配置文件构成

主要有两大部分,一是申明一个名为“log4net“的自定义配置节,如下所示:

  <configSections>

<sectionname="log4net"

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

  </configSections>

二是<log4net>节的具体配置,这是下面要重点说明的。

4.1<log4net>

所有的配置都要在<log4net>元素里定义。

支持的属性:

debug

可选,取值是true或false,默认是false。设置为true,开启log4net的内部调试。

update

可选,取值是Merge(合并)或Overwrite(覆盖),默认值是Merge。设置为Overwrite,在提交配置的时候会重置已经配置过的库。

threshold

可选,取值是repository(库)中注册的level,默认值是ALL。

支持的子元素:

appender

0或多个

logger

0或多个

renderer

0或多个

root

最多一个

param

0或多个

 

4.2 <root>

实际上就是一个根logger,所有其它logger都默认继承它,如果配置文件里没有显式定义,则框架使用根日志中定义的属性。root元素没有属性。

支持的子元素:

appender-ref

0个或多个,要引用的appender的名字。

level

最多一个。 只有在这个级别或之上的事件才会被记录。

param

0个或多个, 设置一些参数。

 

4.3 <logger>

支持的属性:

name

必须的,logger的名称

additivity

可选,取值是true或false,默认值是true。设置为false时将阻止父logger中的appender。

支持的子元素:

appender-ref

0个或多个,要引用的appender的名字。

level

最多一个。 只有在这个级别或之上的事件才会被记录。

param

0个或多个, 设置一些参数。

 

4.4 <appender>

定义日志的输出方式,只能作为 log4net 的子元素。name属性必须唯一,type属性必须指定。

支持的属性:

name

必须的,Appender对象的名称

type

必须的,Appender对象的输出类型

支持的子元素:

appender-ref

0个或多个,允许此appender引用其他appender,并不是所以appender类型都支持。

filter

0个或多个,定义此app使用的过滤器。

layout

最多一个。定义appender使用的输出格式。

param

0个或多个, 设置Appender类中对应的属性的值。

实际上<appender>所能包含的子元素远不止上面4个。

 

4.5 <layout>

布局,只能作为<appender>的子元素。

支持的属性:

type

必须的,Layout的类型

支持的子元素:

param

0个或多个, 设置一些参数。

 

4.6 <filter>

过滤器,只能作为<appender>的子元素。

支持的属性:

type

必须的,Filter的类型

支持的子元素:

param

0个或多个, 设置一些参数。

 

4.7 <param>

<param>元素可以是任何元素的子元素。

支持的属性:

name

必须的,取值是父对象的参数名。

value

可选的,value和type中,必须有一个属性被指定。value是一个能被转化为参数值的字符串。

type

可选的,value和type中,必须有一个属性被指定。type是一个类型名,如果type不是在log4net程序集中定义的,就需要使用全名。

支持的子元素:

param

0个或多个, 设置一些参数。

 

   最近做项目需要记录系统日志和用户操作日志,就想起来了log4net,但是业务需要需要加入自定义属性,并把自定义属性日志数据插入到数据库中,看好趁这个机会学习总结下。

详细步骤

一、首先下载:log4net.dll  下载地址:http://logging.apache.org/log4net/download_log4net.cgi

二、创建一个MVC项目,在项目中进行引用。这个不多说了。


三、在web.config,加入以下代码:

       

1  <configSections>
2     <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
3   </configSections>


四:配置XML(包含配置输出到本地日志文件,输出到数据库的日志文件(带自定义参数和不带自定义参数的)),配置中有详细注释。直接上代码了
 

 <log4net> 
    <!--错误日志(不带自定义字段数据输出到数据库)-->
    <appender name="ADONetAppender"  type="log4net.Appender.ADONetAppender,log4net">
      <!--BufferSize为缓冲区大小,只有日志记录超设定值才会一块写入到数据库-->
      <bufferSize value="0" />
      <!--引用-->
      <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      <!--连接数据库字符串-->
      <connectionString value="data source=.;initial catalog=Test;integrated security=false;persist security info=True;User ID=sa;Password=123456;" />
      <!--插入到表Log-->
      <commandText value="INSERT INTO ErrLog([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level,@logger, @message, @exception)" />
      <!--commandtext-->     
      <!--下面为参数-->
      <!--产生时间-->
      <parameter>
        <parameterName value="@log_date" />
        <dbType value="DateTime" />
        <layout type="log4net.Layout.RawTimeStampLayout" />
      </parameter>
      <!--日志线程-->
      <parameter>
        <parameterName value="@thread" />
        <dbType value="String" />
        <size value="100" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%t" />
        </layout>
      </parameter>
      <!--日志等级-->
      <parameter>
        <parameterName value="@log_level" />
        <dbType value="String" />
        <size value="200" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%p" />
        </layout>
      </parameter>   
      <!--logger-->
      <parameter>
        <parameterName value="@logger" />
        <dbType value="String" />
        <size value="500" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%logger" />
        </layout>
      </parameter>
      <!--异常信息-->
      <parameter>
        <parameterName value="@message" />
        <dbType value="String" />
        <size value="3000" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%m" />
        </layout>
      </parameter>
      <!--异常跟踪-->
      <parameter>
        <parameterName value="@exception" />
        <dbType value="String" />
        <size value="4000" />
        <layout type="log4net.Layout.ExceptionLayout" />
      </parameter>
    </appender>
    
    
    <!--错误日志(输出到本地文件)-->
    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
      <!--本地文件名称-->
      <file value="log\\LogError\\"/>
      <appendToFile value="true"/>
      <rollingStyle value="Date"/>
      <datePattern value="yyyy\\yyyyMM\\yyyyMMdd'.txt'"/>
      <staticLogFileName value="false"/>
      <param name="MaxSizeRollBackups" value="100"/>
      <layout type="log4net.Layout.PatternLayout">
        <!--每条日志末尾的文字说明
        输出格式
        样例:2008-03-26 13:42:32,111 [10] INFO  Log4NetDemo.MainClass [(null)] - info-->
        <conversionPattern value="%n记录时间:%date 线程ID:[%thread] 日志级别:%-5level 错误描述:%message"/>
      </layout>
    </appender>    

 <!--监控日志(本地日志不带自定义字段)-->
    <appender name="MonitorAppender" type="log4net.Appender.RollingFileAppender">
      <param name="File" value="Log\\LogMonitor\\" />
      <param name="AppendToFile" value="true" />
      <param name="MaxFileSize" value="10240" />
      <param name="MaxSizeRollBackups" value="100" />
      <param name="StaticLogFileName" value="false" />
      <param name="DatePattern" value="yyyy\\yyyyMM\\yyyyMMdd'.txt'" />
      <param name="RollingStyle" value="Date" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value=" %n记录时间:%date 线程ID:[%thread] 日志级别:%-5level 跟踪描述:%message"/>
      </layout>
    </appender>
    
    
 <!--监控日志(输出到数据日志带自定义字段)-->

    <appender name="MonitorADONetAppender" type="log4net.Appender.ADONetAppender,log4net">
      <!--BufferSize为缓冲区大小,只有日志记录超设定值才会一块写入到数据库-->
      <bufferSize value="0" />
      <!--引用-->
      <!--2.0这是对应sql2008 如是2000或2005另外配置-->
      <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      <!--连接数据库字符串-->
      <connectionString value="data source=.;initial catalog=Student;integrated security=false;persist security info=True;User ID=sa;Password=123456;" />
      <!--插入到表Log-->      
      <commandText value="INSERT INTO MonLog([Date],[Thread],[Level],[ComputerName],[UserName],[ConName],[ActionName],[ExecuteStartTime],[ExecuteEndTime],[FormCollections],[QueryCollections])
                   VALUES (@log_date, @thread, @log_level,@ComputerName,@UserName,@ConName,@ActionName,@ExecuteStartTime,@ExecuteEndTime,@FormCollections,@QueryCollections)" />
      
      <!--下面为参数-->
      <!--产生时间-->
      <parameter>
        <parameterName value="@log_date" />
        <dbType value="DateTime" />
        <layout type="log4net.Layout.RawTimeStampLayout" />
      </parameter>
      <!--日志线程-->
      <parameter>
        <parameterName value="@thread" />
        <dbType value="String" />
        <size value="100" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%t" />
        </layout>
      </parameter>
      <!--日志等级-->
      <parameter>
        <parameterName value="@log_level" />
        <dbType value="String" />
        <size value="200" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%p" />
        </layout>
      </parameter>

      <!--自定义参数-->
      <!--其中<layout>
        的type由原来的log4net.Layout.PatternLayout换为自定义的Test.Helper.MyLayout(Test.Helper为命名空间)。
        %property{Operator}输出的即为message类对象的属性Operator的值。
        数据库配置同样,相应的字段如果是自定义的,则输出选用自定义的<layout>。-->
          <parameter>
        <parameterName value="@ComputerName"/>
        <dbType value="String"/>
        <size value="50"/>
        <layout type="Test.Helper.MyLayout">
          <param name="ConversionPattern" value="%property{ComputerName}"/>
        </layout>
      </parameter>           
      <parameter>
        <parameterName value="@UserName"/>
        <dbType value="String"/>
        <size value="50"/>
        <layout type="Test.Helper.MyLayout">
          <param name="ConversionPattern" value="%property{username}"/>
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@ConName"/>
        <dbType value="String"/>
        <size value="50"/>
        <layout type="Test.Helper.MyLayout">
          <param name="ConversionPattern" value="%property{ControllerName}"/>
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@ActionName"/>
        <dbType value="String"/>
        <size value="50"/>
        <layout type="Test.Helper.MyLayout">
          <param name="ConversionPattern" value="%property{ActionName}"/>
        </layout>
      </parameter>
   
      <parameter>
        <parameterName value="@ExecuteStartTime"/>
        <dbType value="DateTime"/>
        <size value="150"/>
        <layout type="Test.Helper.MyLayout">
          <param name="ConversionPattern" value="%property{ExecuteStartTime}"/>
        </layout>
      </parameter>    
      <parameter>
        <parameterName value="@ExecuteEndTime"/>
        <dbType value="DateTime"/>
        <size value="150"/>
        <layout type="Test.Helper.MyLayout">
          <param name="ConversionPattern" value="%property{ExecuteEndTime}"/>
        </layout>
      </parameter>     
      <parameter>
        <parameterName value="@FormCollections"/>
        <dbType value="String"/>
        <size value="500"/>
        <layout type="Test.Helper.MyLayout">
          <param name="ConversionPattern" value="%property{FormCollections}"/>
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@QueryCollections"/>
        <dbType value="String"/>
        <size value="500"/>
        <layout type="Test.Helper.MyLayout">
          <param name="ConversionPattern" value="%property{QueryCollections}"/>
        </layout>
      </parameter>
    </appender>    
       
    <!--Logger相当于和程序连接的组件,将代码和xml配置连接-->
    <!--Error日志-->
    <logger name="logerror">
      <level value="ERROR" />
      <appender-ref ref="ADONetAppender"/>
      <appender-ref ref="RollingLogFileAppender" />
    </logger>
    <!--监控日志-->
    <logger name="logmonitor">
      <level value="Monitor" />
      <appender-ref ref="MonitorAppender" />
    </logger>

    <!--自定义字段组件(Errror输出到数据库中的)-->
    <logger name="MyLogger">
      <level value="INFO" />
      <appender-ref ref="MonitorADONetAppender" />
    </logger>

五:后台代码
以上都是log4net的配置,实现监视mvc 的action的信息,光配置xml肯定没有用。

先上一张图,看下项目文件夹:


下面一一说明的文件夹的类的作用:

步骤:
1.创建模型类,存放监视信息,和一些log4net自定义字段
 
代码:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
namespace Test.Models
{
    /// <summary>
    /// 监控日志对象
    /// </summary>
    public class MonitorLog
    {
        /// <summary>
        /// 操作人id
        /// </summary>
        public string username { get; set; }
        /// <summary>
        /// 控制器名称
        /// </summary>
        public string ControllerName{get;set; }
        /// <summary>
        /// Action 名称
        /// </summary>
        public string ActionName{get; set; }
        /// <summary>
        /// 请求时间
        /// </summary>
        public DateTime ExecuteStartTime {get;set; }
        /// <summary>
        /// 结束时间
        /// </summary>
        public DateTime ExecuteEndTime {get;set;}
        /// <summary>
        /// Form 表单数据
        /// </summary>
        public NameValueCollection FormCollections {get; set; }
        /// <summary>
        /// URL 参数
        /// </summary>
        public NameValueCollection QueryCollections {get;set; }
        /// <summary>
        /// 机器ip
        /// </summary>
        public string ComputerName { get; set; }     
        /// <summary>
        /// 监控类型
        /// 枚举类型
        /// </summary>
        public enum MonitorType{Action = 1,View = 2 }

        /// <summary>
        /// 获取监控指标日志
        /// </summary>
        /// <param name="mtype"></param>
        /// <returns></returns>
        public string GetLoginfo(MonitorType mtype = MonitorType.Action)
        {
            string ActionView = "Action执行时间监控:";
            string Name = "Action";
            if (mtype == MonitorType.View)
            {
                ActionView = "View视图生成时间监控:";
                Name = "View";
            }
            string Msg = @"{0}ControllerName:{1}Controller{8}Name:{2}开始时间:{3}结束时间:{4}总 时 间:{5}Form表单数据:{6}URL参数:{7}操作账号:{8}IP地址:{9}";
            return string.Format(Msg,ActionView, ControllerName,  ActionName,ExecuteStartTime, ExecuteEndTime,(ExecuteEndTime - ExecuteStartTime).TotalSeconds, GetCollections(FormCollections),
                GetCollections(QueryCollections), username,ControllerName, Name);
        }
        /// <summary>
        /// 获取Post 或Get 参数
        /// </summary>
        /// <param name="Collections"></param>
        /// <returns></returns>
        public string GetCollections(NameValueCollection Collections)
        {
            string Parameters = string.Empty;
            if (Collections == null || Collections.Count == 0)
            {
                return Parameters;
            }
            foreach (string key in Collections.Keys)
            {
                Parameters += string.Format("{0}={1}&", key, Collections[key]);
            }
            if (!string.IsNullOrWhiteSpace(Parameters) && Parameters.EndsWith("&"))
            {
                Parameters = Parameters.Substring(0, Parameters.Length - 1);
            }
            return Parameters;
        }      
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Test.Models
{
    /// <summary>
    /// 模拟用户输入的参数的模型类
    /// </summary>
    public class UserTest
    {
        public string Uid { get; set; }
        public string Pwd { get; set; }
    }
}
2.创建一个filtes文件夹(存放过滤器类)
创建一个如果中的类, 该类主要继承ActionFilterAttribute类 , 并重写其中的 OnActionExecuted、OnActionExecuting、OnResultExecuted、OnResultExecuting 几个方法实现对控制器和视图的监视。


代码:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Test.Helper;
using Test.Models;

namespace Test.Filtes
{
    /// <summary>
    /// 特性类
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    /// <summary>
    /// 监控程序
    /// 主要继承ActionFilterAttribute类
    /// 并重写其中的 OnActionExecuted、OnActionExecuting、OnResultExecuted、OnResultExecuting 几个方法实现。
    /// </summary>
    public class StatisticsTrackerAttribute : ActionFilterAttribute, IExceptionFilter
    {
        private readonly string Key = "_thisOnActionMonitorLog_";

        #region Action时间监控(执行时间)
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            MonitorLog MonLog = new MonitorLog();
            MonLog.ComputerName = filterContext.HttpContext.Request.UserHostAddress;
            MonLog.ExecuteStartTime = Convert.ToDateTime(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.ffff", DateTimeFormatInfo.InvariantInfo));
            MonLog.ControllerName = filterContext.RouteData.Values["controller"] as string;
            MonLog.ActionName = filterContext.RouteData.Values["action"] as string;
            filterContext.Controller.ViewData[Key] = MonLog;
        }
         #endregion


        #region Action时间监控(执行之后)
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            MonitorLog MonLog = filterContext.Controller.ViewData[Key] as MonitorLog;
            MonLog.ExecuteEndTime = DateTime.Now;
            MonLog.FormCollections = filterContext.HttpContext.Request.Form;           //form表单提交的数据
            MonLog.QueryCollections = filterContext.HttpContext.Request.QueryString;  // 参数
            //插入监视action日志信息
            LoggerHelper.Monitor(MonLog.GetLoginfo());

           LoggerHelper.MonitorSQL(MonLog);
        }
        #endregion

        #region View 视图生成时间监控
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            MonitorLog MonLog = filterContext.Controller.ViewData[Key] as MonitorLog;
            //生成时间
            MonLog.ExecuteStartTime = DateTime.Now;
        }
        #endregion

        #region View 视图结束时间监控
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            MonitorLog MonLog = filterContext.Controller.ViewData[Key] as MonitorLog;
            MonLog.ExecuteEndTime = DateTime.Now;
            //插入监视view 日志
            LoggerHelper.Monitor(MonLog.GetLoginfo(MonitorLog.MonitorType.View));

           LoggerHelper.MonitorSQL(MonLog);
            filterContext.Controller.ViewData.Remove(Key);
        }
        #endregion


        #region 错误日志
        public void OnException(ExceptionContext filterContext)
        {
            if (!filterContext.ExceptionHandled)
            {
                string ControllerName = string.Format("{0}Controller", filterContext.RouteData.Values["controller"] as string);
                string ActionName = filterContext.RouteData.Values["action"] as string;
                string ErrorMsg = string.Format("在执行 controller[{0}] 的 action[{1}] 时产生异常", ControllerName, ActionName);
                //插入错误日志
                 LoggerHelper.Error(ErrorMsg, filterContext.Exception);
                //LoggerHelper.Error(ErrorMsg);
                //表示异常
                filterContext.ExceptionHandled = true;
                filterContext.Result = new RedirectResult("/Home/ERRHTML");
                
                
            }
        }
        #endregion
    }
}

3.既然是应用log4net当然少不了对应log4net的类,新建文件夹

以上有四个类,异常类没有用到,当时准备自定义一个异常类处理异常的。下面一一将代码放上来,看代码。
代码:
LoggerHelper类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Test.Models;

namespace Test.Helper
{
    /// <summary>
    /// logger 组件服务类
    /// </summary>
    public class LoggerHelper
    {
        //根据log4net配置 声明不同的之都对象属性
        /// <summary>
        /// static 静态属性
        /// readonly 只读属性
        /// log4net.LogManager.GetLogger("loginfo") 使用的时候要和xml文件中的 <logger>匹配 读取的是<logger> 的 name 属性
        /// </summary>
        //根据logger name 声明一个封装类ILog对象  INFO 一般信息
        static readonly log4net.ILog logerror = log4net.LogManager.GetLogger("logerror");       //一般性错误信息
        static readonly log4net.ILog logmonitor = log4net.LogManager.GetLogger("logmonitor");   //监视信息
        static readonly log4net.ILog mylog = log4net.LogManager.GetLogger("MyLogger"); //存在自定义的字段。
        
        /// <summary>
        ///  获取一般错误性信息
        /// </summary>
        /// <param name="ErrorMsg"></param>
        /// <param name="ex"></param>
        public static void Error(string ErrorMsg, Exception ex = null)
        {
            //根据参数不同,调用error的 不同重载方法
            if (ex != null)
            {
                logerror.Error(ErrorMsg, ex);
            }
            else
            {
                logerror.Error(ErrorMsg);
            }
        }       
        /// <summary>
        /// 监视性消息(读取到本地)
        /// </summary>
        /// <param name="Msg"></param>
        public static void Monitor(string Msg)
        {
            logmonitor.Info(Msg);
        }
      /// <summary>
        /// 监视性消息(读取到数据库,带有自定义字段)
        /// </summary>
        /// <param name="ML">监视信息的模型对象</param>
       public static void MonitorSQL(MonitorLog ML) {
           mylog.Info(ML);
       }

    }
}

下面的两个类都是为自定义字段做的工作:
PatternLayoutConverter
MyMessagePatternConverter 类:
using System;
using System.Collections.Generic;
using System.Text;
using log4net.Layout.Pattern;
using log4net.Layout;
using log4net.Core;
using System.Reflection;
namespace LogComponent
{

    class MyMessagePatternConverter : PatternLayoutConverter
    {
        protected override void Convert(System.IO.TextWriter writer, log4net.Core.LoggingEvent loggingEvent)
        {
            if (Option != null)
            {

                WriteObject(writer, loggingEvent.Repository, LookupProperty(Option, loggingEvent));
            }
            else
            {

                WriteDictionary(writer, loggingEvent.Repository, loggingEvent.GetProperties());
            }
          
        }
        /// <summary>
        /// 通过反射获取传入的日志对象的某个属性的值
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        private object LookupProperty(string property, log4net.Core.LoggingEvent loggingEvent)
        {
            object propertyValue = string.Empty;
            PropertyInfo propertyInfo = loggingEvent.MessageObject.GetType().GetProperty(property);
            if (propertyInfo != null)
                propertyValue = propertyInfo.GetValue(loggingEvent.MessageObject, null);
            return propertyValue;
        }

    }
}

using log4net.Layout;
using LogComponent;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Test.Helper
{

    /// <summary>
    /// 自定义布局,这里的命名空间和类名和xml配置的自定义字段有关系,请注意。xml 配置上面有说明
    /// </summary>
    class MyLayout : PatternLayout
    {
        public MyLayout()
        {
            this.AddConverter("property", typeof(MyMessagePatternConverter));
        }
    }
}

4.既然是mvc 当然少不了控制器和视图


控制层:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Test.Filtes;
using Test.Helper;
using Test.Models;

namespace Test.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [StatisticsTracker] //表示该处被监视,当然那也可以全局监视,下面将会有说明
        public ActionResult Test(UserTest UT) {
            try {
            string AA=null;
            MonitorLog ML = null;
            ML.ActionName.Equals(AA);//此处有异常,测试用
            }
            catch(Exception ex){
              // LoggerHelper.Error(ex.Message);            
               throw ex;
            }
            return Json(new { },JsonRequestBehavior.DenyGet);
        }

        /// <summary>
        /// 错误视图,测试用,当过滤器方法action中发生异常的时候就会重定向的该视图
        /// </summary>
        /// <returns></returns>
        public ActionResult ERRHTML() {
            return View();
        }
    }
}


视图:
Index:
@{
    ViewBag.Title = "";
}
<h1>测试主页</h1>
<form action ="/Home/Test" method="post">
    <input type="text" name="uid"><br>
    <input type="password" name="pwd"/><br>
    <input type="submit" value="提交">
</form>

 
5.到这里基本写写完了,但是还有些重要的配置。
1.注册log4netxml配置

在这里加上一句话:
//注册log4net XML配置
log4net.Config.XmlConfigurator.Configure();
2.BS程序中在:

加入:[assembly: log4net.Config.XmlConfigurator()]

上面说到的全局监视配置:

在FilterConfig类中注册过滤器:
  public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
            //监控引用
            filters.Add(new StatisticsTrackerAttribute());
        }

6.创建数据表:
CREATE TABLE [dbo].[ErrLog](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Date] [datetime] NOT NULL,
    [Thread] [varchar](100) NULL,
    [Level] [varchar](100) NULL,
    [Logger] [varchar](200) NULL,
    [Exception] [nvarchar](4000) NULL,
    [Message] [nvarchar](3000) NULL
) ON [PRIMARY]

CREATE TABLE [dbo].[MonLog](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Date] [datetime] NOT NULL,
    [Thread] [varchar](100) NULL,
    [Level] [varchar](100) NULL,
    [ComputerName] [varchar](100) NULL,
    [UserName] [varchar](100) NULL,
    [ConName] [varchar](100) NULL,
    [ActionName] [varchar](100) NULL,
    [ExecuteStartTime] [datetime] NULL,
    [ExecuteEndTime] [datetime] NULL,
    [FormCollections] [varchar](2000) NULL,
    [QueryCollections] [varchar](2000) NULL
) ON [PRIMARY]



运行结果:
页面:



日志:
本地:


数据库:
错误信息不带自定义字段:


监视信息带自定义字段:










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值