Category Archives: configuration

Getting the Rolling Log File Setting from your app.config

I couldn’t find an easy way to retrieve the log file location setting from the rolling log file settings in my Logging Application Block settings and after writing a query at a few places, decided that I would have to do it by reading the XML and parsing through to the log setting.

It was not too difficult, but I figured I would post it here so I could remember what I did and for others to find…

Here is a simple form that finds and lists the rolling log file name (you must put a copy of your config file into the bin\debug folder and name it “configData.xml” where this little program runs in order for it to find it):

using System;
using System.Windows.Forms;
using System.Xml.XPath;

namespace GetTheRollingLogFiles
{
 /// <summary>
 /// Summary description for Form1.
 /// </summary>
 public class Form1 : System.Windows.Forms.Form
 {
 private System.Windows.Forms.Button button1;
 private System.Windows.Forms.ListBox listBox1;
 /// <summary>
 /// Required designer variable.
 /// </summary>
 private System.ComponentModel.Container components = null;

 public Form1()
 {
 // Required for Windows Form Designer support
 InitializeComponent();
 // TODO: Add any constructor code after InitializeComponent call
 }

 /// <summary>
 /// Clean up any resources being used.
 /// </summary>
 protected override void Dispose( bool disposing )
 {
 if( disposing )
 {
 if (components != null)
 {
 components.Dispose();
 }
 }
 base.Dispose( disposing );
 }

 #region Windows Form Designer generated code
 /// <summary>
 /// Required method for Designer support - do not modify
 /// the contents of this method with the code editor.
 /// </summary>
 private void InitializeComponent()
 {
 this.button1 = new System.Windows.Forms.Button();
 this.listBox1 = new System.Windows.Forms.ListBox();
 this.SuspendLayout();
 //
 // button1
 //
 this.button1.Location = new System.Drawing.Point(12, 12);
 this.button1.Name = "button1";
 this.button1.Size = new System.Drawing.Size(152, 24);
 this.button1.TabIndex = 0;
 this.button1.Text = "GO!";
 this.button1.Click += new System.EventHandler(this.button1_Click);
 //
 // listBox1
 //
 this.listBox1.Location = new System.Drawing.Point(12, 42);
 this.listBox1.Name = "listBox1";
 this.listBox1.Size = new System.Drawing.Size(428, 433);
 this.listBox1.TabIndex = 1;
 //
 // Form1
 //
 this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
 this.ClientSize = new System.Drawing.Size(455, 502);
 this.Controls.Add(this.listBox1);
 this.Controls.Add(this.button1);
 this.Name = "Form1";
 this.Text = "Form1";
 this.Load += new System.EventHandler(this.Form1_Load);
 this.ResumeLayout(false);

 }
 #endregion

 /// <summary>
 /// The main entry point for the application.
 /// </summary>
 [STAThread]
 static void Main()
 {
 Application.Run(new Form1());
 }

 private void button1_Click(object sender, System.EventArgs e)
 {
 listBox1.Items.Clear();

 string fileName = "configData.xml";
 XPathDocument doc = new XPathDocument(fileName);
 XPathNavigator nav = doc.CreateNavigator();

 // Compile a standard XPath expression
 XPathExpression expr;
 expr = nav.Compile("/configuration/loggingConfiguration/listeners/*");
 XPathNodeIterator iterator = nav.Select(expr);

 // Iterate on the node set
 listBox1.Items.Clear();
 try
 {
 while (iterator.MoveNext())
 {
 XPathNavigator nav2 = iterator.Current.Clone();
 string rollingLogFileName = "";
 listBox1.Items.Add(nav2.Name);
 listBox1.Items.Add("HasAttributes: " + nav2.HasAttributes.ToString());
 if (nav2.MoveToFirstAttribute())
 {
 bool isRollingLogConfig = false;
 string fileNameHolder = "";
 while (true)
 {
 listBox1.Items.Add(nav2.Name + ": " + nav2.Value);
 if ((nav2.Name.ToLower() == "type") && nav2.Value.Contains("RollingFlatFileTraceListener"))
 isRollingLogConfig = true;

 if (nav2.Name == "fileName")
 fileNameHolder = nav2.Value;

 if (!nav2.MoveToNextAttribute()) break;
 }
 if (isRollingLogConfig)
 {
 rollingLogFileName = fileNameHolder;
 listBox1.Items.Add("Rolling Log file is: " + rollingLogFileName);
 }
 }
 }
 }
 catch(Exception ex)
 {
 MessageBox.Show(ex.Message);
 }
 }

 private void Form1_Load(object sender, System.EventArgs e)
 {
 }
 }
}
Advertisements

Rolling Log Setting for Application Logging Block

I still have a catch-all acting as the conduit for the rolling log, but it is the first combination I found that worked and I have stuck with it for now. As I refine this and learn more, I will publish again. Comments are welcome.

Note that the Rolling log settings use the file name “LoggerTest.log”. I used the following values and got the results as described below:

rollFileExistsBehavior=”Increment”

When the log hits the size limit or begins a new day, it opens a new file and increments an integer at the end of the filename. Thus the main log file is as named above, but an older one would be named:

LoggerTest.2010-05-06.1.log

LoggerTest.2010-05-06.2.log

etc. Very handy…

rollInterval=”Day”

This is the amount of time to allow to go by while writing to a single log. Once passed, it will open a new one.
Set to “Midnight”, it would roll over at exactly midnight and each day would be in a separate log.

rollSizeKB=”1000″

This is the size limit, so once the log goes beyond 1MB in size, it will create another.

timeStampPattern=”yyyy-MM-dd”

This is the pattern to use when creating a log “archive.” The latest log file is always named according to the setting in the configuration. I have not found a setting for controlling a limit on the number of files that are kept, so I might have to write a cleanup function. I was very surprised that there is no purge setting for this application block. I have built something that should work for that, and once I have done some testing will post it.

<loggingConfiguration name="Logging Application Block" tracingEnabled="true" defaultCategory="Default Category" logWarningsWhenNoCategoriesMatch="true">
 <listeners>
 <add source="Enterprise Library Logging" formatter="Text Formatter"
 log="Application" machineName="" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
 traceOutputOptions="None" filter="All" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
 name="Formatted EventLog TraceListener" />
 <add fileName="D:\Temp\LoggerTest.log"
 formatter="One Line Formatter" rollFileExistsBehavior="Increment"
 rollInterval="Midnight" rollSizeKB="10000" timeStampPattern="yyyy-MM-dd"
 listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.RollingFlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
 traceOutputOptions="None" filter="All" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
 name="Rolling Flat File" />
 </listeners>
 <formatters>
 <add template="Timestamp: {timestamp(local)}

Message: {message}

Category: {category}

Priority: {priority}

EventId: {eventid}

Severity: {severity}

Title:{title}

Machine: {machine}

Application Domain: {appDomain}

Process Id: {processId}

Process Name: {processName}

Win32 Thread Id: {win32ThreadId}

Thread Name: {threadName}

Extended Properties: {dictionary({key} - {value}

)}"
 type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
 name="Text Formatter" />
 <add template="{timestamp(local)}	Cat: {category}	Pri: {priority}	EId: {eventid}	Sev: {severity}	{message}	Title:{title}	Machine: {machine}	Application Domain: {appDomain}	Process Id: {processId}	Process Name: {processName}	Win32 Thread Id: {win32ThreadId}	Thread Name: {threadName}	Extended Properties: {dictionary({key} - {value}

)}"
 type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
 name="One Line Formatter" />
 </formatters>
 <logFilters>
 <add name="LogEnabled Filter"
 type="Microsoft.Practices.EnterpriseLibrary.Logging.Filters.LogEnabledFilter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
 enabled="true" />
 </logFilters>
 <categorySources>
 <add switchValue="Warning" name="Default Category">
 <listeners>
 <add name="Formatted EventLog TraceListener" />
 </listeners>
 </add>
 </categorySources>
 <specialSources>
 <allEvents switchValue="All" name="All Events">
 <listeners>
 <add name="Rolling Flat File" />
 </listeners>
 </allEvents>
 <errors switchValue="All" name="Logging Errors &amp; Warnings">
 <listeners>
 <add name="Formatted EventLog TraceListener" />
 </listeners>
 </errors>
 </specialSources>
 </loggingConfiguration>

Custom Configuration in .Net

In working through some custom configuration issues I found the following articles very helpful…

ConfigurationPropertyAttribute Class (System.Configuration) (MSDN)
Unraveling the Mysteries of .NET 2.0 Configuration – CodeProject
C# ConfigurationManager.GetSection could not load file or assembly – Stack Overflow
Advanced .NET Configuration techniques

I also was pleased to see how much strong typing and validation could be built into the configuration manager, saving the trouble of building it all. Much of this is not obvious and can be difficult to sort out via web search, and yet config files are often leaned on pretty heavily.

I was stuck trying to get a set of multiple elements without the named collection. The last article helped clear that up.

Here is a sample of what can be done:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;

namespace MyConfiguration
{
public class MyConfig : ConfigurationSection
{
/// <summary>Gets the timespan value - not required to be set and will default to 1 minute. If set must be in range 30 seconds to 5 minutes.</summary>
[ConfigurationProperty("TimespanValue", DefaultValue = "0:01:0", IsRequired = false)]
[TimeSpanValidator(MinValueString = "0:0:30", MaxValueString = "0:05:0", ExcludeRange = false)]
public TimeSpan TimespanValue
{
get { return (TimeSpan)this["TimespanValue"]; }
set { this["TimespanValue"] = value.ToString(); }
}

/// <summary>Gets required string value. If not set in config will raise a very clear exception telling user what value is missing.</summary>
[ConfigurationProperty("RequiredString", DefaultValue = "", IsRequired = true)]
public string RequiredString
{
get { return (string)this["RequiredString"]; }
set { this["RequiredString"] = value; }
}

/// <summary>Gets int value. If not set in config will default to 2. If set must be in range 0 to 8.</summary>
[ConfigurationProperty("MyIntValue", DefaultValue = "2")]
[IntegerValidator(MinValue = 0, MaxValue = 8)]
public int MyIntValue
{
get { return (int)this["MyIntValue"]; }
set { this["MyIntValue"] = value; }
}

}

}

namespace MyConfiguration
{
public class MyApplicationMethods
{
MyConfig config = new MyConfig();

public MyApplicationMethods()
{
string myString = config.RequiredString;
int myInt = config.MyIntValue;
TimeSpan ts = config.TimespanValue;
}
}
}