擴展自訂的Layout Renderer來記錄所有的Request.ServerVariable變數資料
雖然NLog提供的Layout Renderers已經相當多種了,
而且如果要記錄ASP.NET Request ServerVariable的資料,可以使用:${aspnet-request : serverVariable=String} ,
但是Request.ServerVariable有相當多種,除非你相當有耐心,不然一個一個變數去加入NLog.Config中也是一件累人的事情,
所以如果可以像ELMAH一般,預設就是把所有的Request.ServerVariable給記錄下來,
我們可以擴展自定義的Layout Renderer來記錄這所有的Request.ServerVariable資料。
如何擴展自訂Layout Renderer
首先這邊我介紹一篇教學文章,這篇文章是教我們如何去設計自己的Layout Renderer,
Dflying Chen @ cnblogs - NLog文章系列——如何寫自定義佈局生成器(Layout Renderer)
相信瀏覽過這篇文章之後,應該對擴展自訂的Layout Renderer就有了初步的概念。
設計${web_variables}
而我們的需求是要去記錄所有的Request.ServerVariable,而因為變數資料有相當多,最好的方式就是將資料以XML文件格式給記錄起來,
這樣日後追查資料的內容就可以很方便的查看資料內容了,
於是在網路上找到了另一篇文章,說明如何設計一個Layout Renderer去記錄Request.ServerVariable並存成XML格式,
Darren’s Blog - Logging in MVC Part 3 – NLog
WebVariablesRender.cs的程式碼如下:
1: [LayoutRenderer("web_variables")]
2: public class WebVariablesRenderer : LayoutRenderer3: {4:5: ///
6: /// Initializes a new instance of the class.
7: ///
8: public WebVariablesRenderer()
9: {10: this.Format = "";
11: this.Culture = CultureInfo.InvariantCulture;
12: }13:14: protected override int GetEstimatedBufferSize(LogEventInfo ev)15: {16: // This will be XML of an unknown size
17: return 10000;
18: }19:20: ///
21: /// Gets or sets the culture used for rendering.
22: ///
23: ///
24: public CultureInfo Culture { get; set; }25:26: ///
27: /// Gets or sets the date format. Can be any argument accepted by DateTime.ToString(format).
28: ///
29: ///
30: [DefaultParameter]31: public string Format { get; set; }32:33: ///
34: /// Renders the current date and appends it to the specified .
35: ///
36: /// <param name="builder">The to append the rendered data to.
37: /// <param name="logEvent">Logging event.
38: protected override void Append(StringBuilder builder, LogEventInfo logEvent)39: {40: StringBuilder sb = new StringBuilder();
41: XmlWriter writer = XmlWriter.Create(sb);42:43: writer.WriteStartElement("error");
44:45: // -----------------------------------------
46: // Server Variables
47: // -----------------------------------------
48: writer.WriteStartElement("serverVariables");
49:50: foreach (string key in HttpContext.Current.Request.ServerVariables.AllKeys)51: {52: writer.WriteStartElement("item");
53: writer.WriteAttributeString("name", key);
54:55: writer.WriteStartElement("value");
56: writer.WriteAttributeString("string", HttpContext.Current.Request.ServerVariables[key].ToString());
57: writer.WriteEndElement();58:59: writer.WriteEndElement();60: }61:62: writer.WriteEndElement();63:64: // -----------------------------------------
65: // Cookies
66: // -----------------------------------------
67: writer.WriteStartElement("cookies");
68:69: foreach (string key in HttpContext.Current.Request.Cookies.AllKeys)70: {71: writer.WriteStartElement("item");
72: writer.WriteAttributeString("name", key);
73:74: writer.WriteStartElement("value");
75: writer.WriteAttributeString("string", HttpContext.Current.Request.Cookies[key].Value.ToString());
76: writer.WriteEndElement();77:78: writer.WriteEndElement();79: }80:81: writer.WriteEndElement();82: // -----------------------------------------
83:84: writer.WriteEndElement();85: // -----------------------------------------
86:87: writer.Flush();88: writer.Close();89:90: string xml = sb.ToString();
91:92: builder.Append(xml);93: }94:95: }
因為我們的NLog版本已經使用到2.0,所以在上面的程式中Line:14 ~ 18 的部份,原本需要覆寫GetEstimatedBufferSize()的地方,
NLog 2.0已經不需要去override了,原本GetEstimatedBufferSize()這個方法是用來設定Layout Renderer的緩衝區數,
就是看你所記錄的資料大小,於建立的時候先給一個估計的量,
NLog Logging Library - LayoutRenderer.GetEstimatedBufferSize方法。
目前不需要去覆寫GetEstimatedBufferSize()方法
以下則是整理過後的程式碼:
[LayoutRenderer("web_variables")]
public class WebVariablesRenderer : LayoutRenderer{/// <summary>
/// Initializes a new instance of the <see cref="WebVariablesRenderer"/> class.
/// </summary>
public WebVariablesRenderer()
{this.Format = string.Empty;this.Culture = CultureInfo.InvariantCulture;
}/// <summary>
/// Gets or sets the culture used for rendering..
/// </summary>
/// <value>
/// The culture.
/// </value>
public CultureInfo Culture { get; set; }/// <summary>
/// Gets or sets the date format. Can be any argument accepted by DateTime.ToString(format).
/// </summary>
/// <value>
/// The format.
/// </value>
[DefaultParameter]public string Format { get; set; }/// <summary>
/// Renders the current date and appends it to the specified .
/// </summary>summary>
/// <param name="builder">The to append the rendered data to.
/// <param name="logEvent">Logging event.
protected override void Append(StringBuilder builder, LogEventInfo logEvent){StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb);writer.WriteStartElement("error");
// -----------------------------------------
// Server Variables
// -----------------------------------------
writer.WriteStartElement("serverVariables");
foreach (string key in HttpContext.Current.Request.ServerVariables.AllKeys){writer.WriteStartElement("item");
writer.WriteAttributeString("name", key);
writer.WriteStartElement("value");
writer.WriteAttributeString("string", HttpContext.Current.Request.ServerVariables[key].ToString());
writer.WriteEndElement();writer.WriteEndElement();}writer.WriteEndElement();// -----------------------------------------
// Cookies
// -----------------------------------------
writer.WriteStartElement("cookies");
foreach (string key in HttpContext.Current.Request.Cookies.AllKeys){writer.WriteStartElement("item");
writer.WriteAttributeString("name", key);
writer.WriteStartElement("value");
writer.WriteAttributeString("string", HttpContext.Current.Request.Cookies[key].Value.ToString());
writer.WriteEndElement();writer.WriteEndElement();}writer.WriteEndElement();// -----------------------------------------
writer.WriteEndElement();// -----------------------------------------
writer.Flush();writer.Close();string xml = sb.ToString();
builder.Append(xml);}}
加入Global.asax中
在「Darren’s Blog - Logging in MVC Part 3 – NLog」中也有說到,
如何在Global.asax的Application_Start()方法中去註冊我們所擴展的自訂Layout Renderer方法,
protected void Application_Start(){AreaRegistration.RegisterAllAreas();RegisterRoutes(RouteTable.Routes);ControllerBuilder.Current.SetControllerFactory(new ErrorHandlingControllerFactory());
// Register custom NLog Layout renderers
LayoutRendererFactory.AddLayoutRenderer("utc_date", typeof(MySampleApp.Services.Logging.NLog.UtcDateRenderer));LayoutRendererFactory.AddLayoutRenderer("web_variables", typeof(MySampleApp.Services.Logging.NLog.WebVariablesRenderer));}
但使用 NLog 2.0 會遇到一個問題,那就是 LayoutRendererFactory 這個類別不見了,而官方文件中也不知道哪裡有說明替代的方案為何?
最後是在 NLog Fourm 裡面找到一篇討論「NLog Fourm - NLogFactories not found」,NLog作者的回答如下:
所以我就改成了以下的方式:
protected void Application_Start(){AreaRegistration.RegisterAllAreas();RegisterGlobalFilters(GlobalFilters.Filters);RegisterRoutes(RouteTable.Routes);ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("web_variables", typeof(WebVariablesRenderer));}
一般的文字檔要儲存XML文件也是可以,但是我會建議你要使用 ${web_variables} ,Target 建議使用Database,在日後的維護與查詢Log資料時會比較方便。
而存在資料庫的話,欄位名稱你可以自訂,而資料類別我則是使用「nvarchar(MAX)」,
有些人會在SQL Server中存放大量文字資料會使用ntext的資料類型,但是未來的MS SQL Server版本將會移除這個資料類型,
所以就改用「nvarchar(MAX)」來存放我們所記錄到的Request.ServerVariable XML資料。
參考連結:MSDN - ntext、text 和 image (Transact-SQL)
NLog.Config的修改
看看Database Target的修改情況:
<target name="database" type="Database"><dbprovider>mssql</dbprovider><connectionString>Data Source=localhost;Initial Catalog=NLogDatabase;Persist Security Info=True;User ID=nloguser;Password=nlogpassword;MultipleActiveResultSets=True;</connectionString><commandText>insert into NLog (time_stamp, level, host, url, type, source, logger, message, stacktrace, Detail, allxml) Values(@time_stamp, @level, @host, @url, @type, @source, @logger, @message, @stacktrace, @detail, @allxml);</commandText><parameter name="@time_stamp" layout="${date}" /><parameter name="@level" layout="${level}" /><parameter name="@host" layout="${machinename}" /><parameter name="@url" layout="${aspnet-request:serverVariable=url}" /><parameter name="@type" layout="${exception:format=type}" /><parameter name="@source" layout="${callsite:className=true}" /><parameter name="@logger" layout="${logger}" /><parameter name="@message" layout="${message}" /><parameter name="@stacktrace" layout="${exception:stacktrace}" /><parameter name="@detail" layout="${exception:format=tostring}" /><parameter name="@allxml" layout="${web_variables}" /></target>
看起來沒有什麼變化,除了說我增加一個parameter其名稱為「@allxml」,而${web_variables}資料將會存放到欄位[allxml]中。
實際執行網站後,來看看資料庫的紀錄結果,
可以看到Request.ServerVariables資料都已經存成XML的格式了,
為了檢查是否是記錄完整,所以將allxml的資料複製起來後貼到XML Editor來看看,
所以在這篇文章中介紹了如何擴展自訂的Layout Renderer,並且因應NLog 2.0的改變而作一些必要的修改,
並且參考網路上的既有範例,增加了 ${web_variables} 的Layout Renderer來記錄Request.ServerVariables,
以方便日後的偵錯與資料追蹤。
參考連結:
Dflying Chen @ cnblogs - NLog文章系列——如何寫自定義佈局生成器(Layout Renderer)
Darren’s Blog - Logging in MVC Part 3 – NLog
NLog Fourm - NLogFactories not found :NLog 2.0 要改使用ConfigurationItemFactory而非LayoutRendererFactory.
以上
沒有留言:
張貼留言