星期四, 8月 16, 2012

整合 NLog 到 Elmah

在專案上,log 功能一直是每個專案都要必備的東西,log 做的好,系統問題煩惱少,在 log 功能實作方面,Bibby 我是選用 thrid party 元件 NLog 來處理我需要的功能,NLog 的好用不用我多說,可以參考好友 Kevin一系列文章,介紹非常詳細了,看完了就可以成為 NLog 達人了,那今天要來說啥呢?當然是有特別的需求啦,Nlog 使用上方便,很自由也很詳細,不過問題是要如何很快速的看到 NLog 所記錄的內容,這點比較困擾我,所以問了身旁的一些好朋友,看有沒有比較不錯的方法,好朋友 Dino 給我一些建議說可以參考一下這個連結,看完了這個連結後,又不小心看到 debug 使用的 Elmah 顯示畫面,心理馬上有個譜了..那就雙璧合一水乳交融來一下吧!

我們直接實作開始,Bibby 我這邊 Elmah 是使用 SQLite 來處理的(參考這篇),所以這邊我的 NLog 也會以 SQLite 為主,不過我還是把 MSSQL 的寫好了,如果要用就把註解拿掉就行了(我是好心人 XD)..

第一步,實作 TargetWithLayout

[Target("Elmah")]
public sealed class ElmahTarget : TargetWithLayout
{
    static ElmahTarget()
    {
        ConfigurationItemFactory.Default.Targets
                .RegisterDefinition("Elmah", typeof(ElmahTarget));
    }
 
    public ElmahTarget()
    {
    }
 
    public string ConnectionStringName { get; set; }
 
    protected override void Write(LogEventInfo logEvent)
    {
        var connectionString = 
            ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString;
 
        using (var connection = new SQLiteConnection(connectionString))
        {
            connection.Open();
 
            var application = System.Web.Hosting.HostingEnvironment.ApplicationID;
            var time = logEvent.TimeStamp.ToUniversalTime();
            var eventData = from p in logEvent.GetType().GetProperties()
                            where p.PropertyType.IsValueType || p.PropertyType.IsEnum
                            select new XElement("item",
                                                new XAttribute("name", p.Name),
                                                new XElement("value",
                                                    new XAttribute("string",
                                                        p.GetValue(logEvent, null).ToString())));
            var variables = from k in HttpContext.Current.Request.ServerVariables.AllKeys
                            let v = HttpContext.Current.Request.ServerVariables[k]
                            select new XElement("item",
                                                new XAttribute("name", k),
                                                new XElement("value",
                                                                new XAttribute("string", v)));
            var xdoc = new XElement("error",
                                    new XAttribute("host", System.Environment.MachineName),
                                    new XAttribute("type", logEvent.Exception == null ?
                                                            "" : logEvent.Exception.ToString()),
                                    new XAttribute("message", logEvent.FormattedMessage),
                                    new XAttribute("source", logEvent.LoggerName),
                                    new XAttribute("detail", logEvent.StackTrace == null ?
                                                            "" : logEvent.StackTrace.ToString()),
                                    new XAttribute("time", time.ToString("s") + "Z"),
                                    new XElement("eventData", eventData),
                                    new XElement("serverVariables", variables));
            var user = string.Empty;
            if (HttpContext.Current != null && HttpContext.Current.User.Identity.IsAuthenticated)
            {
                user = HttpContext.Current.User.Identity.Name;
            }
 
            var SQL = @"
insert into [error]
(Application,Host,Type,Source,Message,User,AllXml,StatusCode,TimeUtc)
values
(@Application,@Host,@Type,@Source,@Message,@User,@AllXml,@StatusCode,@TimeUtc)
";
            var cmd = new SQLiteCommand(SQL, connection);
            cmd.CommandType = CommandType.Text;
            //cmd.Parameters.Add("@ErrorId", DbType.).Value = Guid.NewGuid();
            //cmd.Parameters.Add("@ErrorId", DbType.Int32).Value = errorId;
            cmd.Parameters.Add("@Application", DbType.String).Value = application;
            cmd.Parameters.Add("@Host", DbType.String).Value = System.Environment.MachineName;
            cmd.Parameters.Add("@Type", DbType.String).Value = "NLog-" + logEvent.Level.Name;
            cmd.Parameters.Add("@Source", DbType.String).Value = logEvent.LoggerName;
            cmd.Parameters.Add("@Message", DbType.String).Value = logEvent.FormattedMessage;
            cmd.Parameters.Add("@User", DbType.String).Value = user;
            cmd.Parameters.Add("@AllXml", DbType.String).Value = xdoc.ToString();
            cmd.Parameters.Add("@StatusCode", DbType.Int32).Value = 0;
            cmd.Parameters.Add("@TimeUtc", DbType.DateTime).Value = time;
 
            cmd.ExecuteNonQuery();
        }
 
    }
 
    //for ms-sql
    //protected override void Write(LogEventInfo logEvent)
    //{
    //    var connectionString = 
    //          ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString;
        //using (var connection = new SqlConnection(connectionString))
        //{
        //    connection.Open();
 
        //    var application = System.Web.Hosting.HostingEnvironment.ApplicationID;
 
        //    var time = logEvent.TimeStamp.ToUniversalTime();
 
 
        //    var eventData = from p in logEvent.GetType().GetProperties()
        //                    where p.PropertyType.IsValueType || p.PropertyType.IsEnum
        //                    select new XElement("item",
        //                                        new XAttribute("name", p.Name),
        //                                        new XElement("value",
        //                                                     new XAttribute("string", 
        //                                       p.GetValue(logEvent, null).ToString())));
 
        //    var variables = from k in HttpContext.Current.Request.ServerVariables.AllKeys
        //                    let v = HttpContext.Current.Request.ServerVariables[k]
        //                    select new XElement("item",
        //                                        new XAttribute("name", k),
        //                                        new XElement("value",
        //                                                     new XAttribute("string", v)));
 
 
 
        //    var xdoc = new XElement("error",
        //                            new XAttribute("host", System.Environment.MachineName),
        //                            new XAttribute("type", logEvent.Exception == null ?
        //                                              "" : logEvent.Exception.ToString()),
        //                            new XAttribute("message", logEvent.FormattedMessage),
        //                            new XAttribute("source", logEvent.LoggerName),
        //                            new XAttribute("detail", logEvent.StackTrace == null ?
        //                                                      "" : logEvent.StackTrace.ToString()),
        //                            new XAttribute("time", time.ToString("s") + "Z"),
        //                            new XElement("eventData", eventData),
        //                            new XElement("serverVariables", variables));
 
        //    var user = string.Empty;
 
        //    if (HttpContext.Current != null && HttpContext.Current.User.Identity.IsAuthenticated)
        //    {
        //        user = HttpContext.Current.User.Identity.Name;
        //    }
 
        //    var cmd = new SqlCommand("ELMAH_LogError", connection);
        //    cmd.CommandType = CommandType.StoredProcedure;
        //    cmd.Parameters.Add("ErrorId", SqlDbType.UniqueIdentifier).Value = Guid.NewGuid();
        //    cmd.Parameters.Add("Application", SqlDbType.NVarChar).Value = application;
        //    cmd.Parameters.Add("Host", SqlDbType.NVarChar).Value = System.Environment.MachineName;
        //    cmd.Parameters.Add("Type", SqlDbType.NVarChar).Value = "NLog-" + logEvent.Level.Name;
        //    cmd.Parameters.Add("Source", SqlDbType.NVarChar).Value = logEvent.LoggerName;
        //    cmd.Parameters.Add("Message", SqlDbType.NVarChar).Value = logEvent.FormattedMessage;
        //    cmd.Parameters.Add("User", SqlDbType.NVarChar).Value = user;
        //    cmd.Parameters.Add("AllXml", SqlDbType.NVarChar).Value = xdoc.ToString();
        //    cmd.Parameters.Add("StatusCode", SqlDbType.Int).Value = 0;
        //    cmd.Parameters.Add("TimeUtc", SqlDbType.DateTime).Value = time;
 
        //    cmd.ExecuteNonQuery();
        //}
    //} 
}

第二步,在 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">
  <extensions>
    <add assembly="NlogElmah" />
  </extensions>
  
  <targets>
    <target name="elmah" xsi:type="Elmah" connectionStringName="elmahDB" />
  </targets>
 
  <rules>
    <logger name="*" minlevel="Trace" writeTo="elmah" />
  </rules>
</nlog>

這樣就大功完成了,NLog 的設定檔也非常簡單,沒有啥需要設定的,因為都寫在 code 裡面了 XD…

我們來看一下結果,看是不是有如想像中的結果..

clip_image001
clip_image002

很好,如我所想像的,這樣可以很簡單的去把 NLog 的結果讀出來,有問題再一起討論吧..^^..收工!!

參考:
http://nlog-project.org/wiki/How_to_write_a_Target
http://bibby.be/2011/11/bibbyelmahmssqlmssqlxmlxmlxmlxmlelmahsq.html

2 則留言:

匿名 提到...

請問一下,這個ElmahTarge seal class要放在哪個檔案下?? 謝謝~

陳琮暉 提到...

自己建立一個class就可以了阿!