【问题标题】:Event and error logging in Asp.net MVC 5 projectAsp.net MVC 5 项目中的事件和错误日志记录
【发布时间】:2014-04-14 07:01:28
【问题描述】:

我正在考虑在我的站点中实现日志记录机制,我希望进行基本的用户操作日志记录。我不想记录他们点击的每一个按钮,但我确实想记录他们所做的更改操作。

是否有任何库或文章/教程可以帮助我为我的 asp.net 站点实现良好且高效的日志记录机制。我不确定 MVC5 中是否有任何可能用于日志记录的更改,因为我知道用户身份验证和授权已从 4 更改为 5。

我确信有一个动态库可以在许多不同的情况下工作。

很高兴拥有:

  • 异步功能
  • 可扩展
  • 使用简单

我正在考虑创建一个自定义过滤器或属性,然后记录用户的操作,但这只是我的想法,我在这里询问标准/行业方法是什么。

【问题讨论】:

    标签: asp.net logging asp.net-mvc-5


    【解决方案1】:

    没有行业标准。 我使用了过滤器,或者我已经覆盖了基本控制器类上的“onActionExecuting”方法来记录控制器/动作事件。

    编辑::

    试图提供更多帮助,但这真的很模糊。 如果您担心错误和类似的事情,请使用 elmah。 对于其他日志记录,请使用 Nlog 或 Log4Net。

    如果您尝试进行额外的日志记录,例如审核或类似的事情,您可以使用这些的任意组合,或自定义的东西。在我的网站中,我创建了一个表格,通过创建类似这样的对象来存储网站上的每次点击:

        public class audit
    {
        public int ID { get; set; }
        public DateTime AuditDate { get; set; }
        public string ControllerName { get; set; }
        public string ActionName { get; set; }
        public Dictionary<string, object> values
    }
    

    在我的基础构造函数中,我覆盖了 OnActionExecuting 事件:

        protected override void OnActionExecuting(ActionExecutingContext ctx)
        {
            checkForLogging(ctx);
    
            //do not omit this line
            base.OnActionExecuting(ctx);
        }
    

    假设我想使用我的新审计对象记录所有获取请求

      private void checkForLogging(ActionExecutingContext ctx)
        {
            //we leave logging of posts up to the actual methods because they're more complex...
            if (ctx.HttpContext.Request.RequestType == "GET")
            {
                    logging(ctx.ActionDescriptor.ActionName, ctx.ActionDescriptor.ControllerDescriptor.ControllerName, ctx.ActionParameters);                
            }
        }
    

    这就是我用动作名称、控制器名称和传递给方法的所有参数填充我的日志记录对象所需的所有信息。您可以将其保存到数据库、日志文件或您真正想要的任何内容。

    关键是它非常重要。这只是一种方法,它可能对您有帮助,也可能对您没有帮助。也许更多地定义您想要记录的内容以及何时执行?

    您可以创建自定义属性并使用它装饰方法,然后在 OnActionExecuting 方法触发时检查该属性是否存在。然后,您可以获取该过滤器(如果存在)并从中读取并使用它来驱动您的日志记录...

    【讨论】:

    • 我需要比这更多的信息来选择我的答案,
    • 我试图扩展一点,但你需要更清楚你想要做什么......
    • logging(ctx.ActionDescriptor.Ac,,,, name logging 不存在, EROR
    • 日志记录不存在,它是一种假设的方法,它将获取输入并将它们保存到数据库或控制台或文件或任何你想要的东西。这并不难实现。
    【解决方案2】:

    也许这个例子会有所帮助。 我对日志记录的关注点在于 CREATE、EDIT、DELETE 操作。

    我正在使用 MVC 5 Code-first EF 6.1 (VS 2013) , 对于这个例子,我指的是一个名为“WorkFlow”的实体的创建操作

    我实际上是从 SSRS 中查看这些日志,但您可以为 WriteUsageLog 添加控制器和视图,然后从 MVC 应用程序中查看它们

    1. MODEL:创建一个名为“WriteUsageLog”的模型实体,用于保存日志记录
    2. CONTROLLER:从 WorkFlowController 中提取或重构“Create”操作的 HttpPost 重载到一个名为“WorkFlowController”的部分类中(这些部分是为了避免在我使用向导创建控制器时被删除和重建)
    3. CONTROLLER 文件夹中的其他类:然后在名为“General_Object_Extensions”和“General_ActiveDirectory_Extensions”的类中需要一些辅助函数(注意:这些并不是真正的“扩展”)
    4. 将以下行添加到 DBContext:

      公共 DbSet WriteUsageLogs { get;放; }

      这个例子的好处是:

    5. 我正在记录以下内容:

      • Active Directory 中的用户名
      • 创建日志记录的日期时间
      • 计算机名称
      • 还有一个包含所有实体属性值的注释
    6. 我正在将日志记录在一个表中,我可以使用 MVC 控制器或最好从 SQL Server Report Server 访问它。我可以在哪里监控我的所有 MVC 应用程序

    /Models/WriteUsageLog.cs

    using System;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace MileageReimbursement.Models
    {
        public class WriteUsageLog
        {
            public WriteUsageLog()
            {
                this.DateTimeCreated = DateTime.Now; // auto-populates DateTimeCreated field
            }
    
            [Key]
            public int WriteUsageLogID { get; set; }
    
            [Column(TypeName = "nvarchar(max)")]
            public string Note { get; set; }
    
            public string UserLogIn { get; set; }
            public string ComputerName { get; set; }
    
            public DateTime DateTimeCreated { get; private set; }  //private set to for auto-populates DateTimeCreated field
    
        }
    }
    

    /Controllers/ControllerPartials.cs

    using System.Linq;
    using System.Web.Mvc;
    using MileageReimbursement.Models;
    
    //These partials are to avoid be deleted and rebuilt when I use the wizard to create Controllers
    
    namespace MileageReimbursement.Controllers
    {
        public partial class WorkFlowController : Controller
        {
    
            [HttpPost]
            [ValidateAntiForgeryToken]
            public ActionResult Create([Bind(Include = "whatever")] WorkFlow workFlow)
            {
                ...
    
                if (ModelState.IsValid) 
                {
                    db.WorkFlows.Add(workFlow);
                    db.SaveChanges();
                    //===================================================================================================================
                    string sX = workFlow.GetStringWith_RecordProperties();
                    //===================================================================================================================
                    var logRecord = new WriteUsageLog();
                    logRecord.Note = "New WorkFlow Record Added - " + sX; 
    
                    logRecord.UserLogIn = General_ActiveDirectory_Extensions.fn_sUser();
                    string IP = Request.UserHostName;
                    logRecord.ComputerName = General_functions.fn_ComputerName(IP);
    
    
                    db.WriteUsageLogs.Add(logRecord);
                    db.SaveChanges();
                    //===================================================================================================================
    
                    return RedirectToAction("Index");
                }
                else   // OR the user is directed back to the validation error messages and given an opportunity to correct them
                {
                   ...
                    return View(workFlow);  //This sends the user back to the CREATE view to deal with their errors
                }
            }
        }
    }
    

    /Controllers/ControllerExtensions.cs

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.DirectoryServices.AccountManagement;
    using System.Linq;
    using System.Net;
    using System.Net.Mail;
    using System.Reflection;
    using System.Security.Cryptography;
    using System.Text;
    using System.Web;
    
    
    namespace MileageReimbursement.Controllers
        {
    
        public static class General_ActiveDirectory_Extensions
            {
    
            public static string fn_sUser()
                {
                char cX = '\\';
                string sUser = General_functions.fn_ReturnPortionOfStringAfterLastOccuranceOfACharacter(HttpContext.Current.User.Identity.Name, cX);
                return sUser;   //returns just the short logon name Example for 'accessiicarewnc\ggarson', it returns 'ggarson'   
                }
    
    
            }   //General_ActiveDirectory_Extensions
    
        public static class General_Object_Extensions
            {
    
            public static string GetStringWith_RecordProperties(this object Record)
                {
    
                string sX = null;
                Dictionary<string, object> _record = GetDictionary_WithPropertiesForOneRecord(Record);
                int iPropertyCounter = 0;
    
                foreach (var KeyValuePair in _record)
                    {
    
                    iPropertyCounter += 1;
                    object thePropertyValue = _record[KeyValuePair.Key];
                    if (thePropertyValue != null)
                        {
                        sX = sX + iPropertyCounter + ") Property: {" + KeyValuePair.Key + "} = [" + thePropertyValue + "] \r\n";
                        }
                    else
                        {
                        sX = sX + iPropertyCounter + ") Property: {" + KeyValuePair.Key + "} = [{NULL}] \r\n";
                        }
    
                    }
    
                return sX;
                }
    
            public static Dictionary<string, object> GetDictionary_WithPropertiesForOneRecord(object atype)
                {
                if (atype == null) return new Dictionary<string, object>();
                Type t = atype.GetType();
                PropertyInfo[] props = t.GetProperties();
                Dictionary<string, object> dict = new Dictionary<string, object>();
                foreach (PropertyInfo prp in props)
                    {
                    object value = prp.GetValue(atype, new object[] { });
                    dict.Add(prp.Name, value);
                    }
                return dict;
                }
    
            }   //General_Object_Extensions
    
        public static class General_functions
            {
            public static string fn_ComputerName(string IP)
                {
                //USAGE
                //From: http://stackoverflow.com/questions/1444592/determine-clients-computer-name
                //string IP = Request.UserHostName;
                //string compName = CompNameHelper.DetermineCompName(IP);
    
    
                IPAddress myIP = IPAddress.Parse(IP);
                IPHostEntry GetIPHost = Dns.GetHostEntry(myIP);
                List<string> compName = GetIPHost.HostName.ToString().Split('.').ToList();
                return compName.First();
                }
    
            static public string fn_ReturnPortionOfStringAfterLastOccuranceOfACharacter(string strInput, char cBreakCharacter)
                {
                // NOTE: for path backslash "/", set cBreakCharacter = '\\'
                string strX = null;
    
                //1] how long is the string
                int iStrLenth = strInput.Length;
    
                //2] How far from the end does the last occurance of the character occur
    
                int iLenthFromTheLeftOfTheLastOccurance = strInput.LastIndexOf(cBreakCharacter);
    
                int iLenthFromTheRightToUse = 0;
                iLenthFromTheRightToUse = iStrLenth - iLenthFromTheLeftOfTheLastOccurance;
    
                //3] Get the Portion of the string, that occurs after the last occurance of the character
                strX = fn_ReturnLastXLettersOfString(iLenthFromTheRightToUse, strInput);
    
                return strX;
    
                }
    
    
            static private string fn_ReturnLastXLettersOfString(int iNoOfCharToReturn, string strIn)
                {
                int iLenth = 0;
                string strX = null;
                int iNoOfCharacters = iNoOfCharToReturn;
    
                iLenth = strIn.Length;
                if (iLenth >= iNoOfCharacters)
                    {
                    strX = strIn.Substring(iLenth - iNoOfCharacters + 1);
    
                    }
                else
                    {
                    strX = strIn;
                    }
    
    
                return strX;
                }
    
    
            }   //General_functions
        }
    

    【讨论】:

    • 在创建它工作正常,它是否也在更新工作,并且在数据库中提供完整查询是否安全?!谢谢你用得很充分
    • 以及为什么我需要在每个控制器中包含部分内容
    【解决方案3】:

    我同意 Log4Net 和 NLog 似乎是我加入的不同项目中最常用的两个产品。

    如果您正在寻找一种出色的工具,可用于日志记录、错误处理和其他任何对 AOP 有益的事情,我强烈推荐 PostSharp (http://www.postsharp.net/)。您集中设置日志记录/错误处理,然后装饰方法。它是一个有据可查且受支持的产品。他们有一个免费的社区许可证——对个人来说也是免费的。他们还拥有产品的专业版和终极版,如果您作为一个团队使用它会更有意义。

    我不在 PostSharp 工作 :-) 我过去只是使用过它并且非常喜欢它。

    【讨论】:

      猜你喜欢
      • 2011-06-03
      • 2010-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-05
      • 2015-10-30
      • 1970-01-01
      • 2011-02-27
      相关资源
      最近更新 更多