Category Archives: .Net

log4net PatternLayout and PatternConverter

I have the following scenario:

I’m using Unity dependency injection container and interception techniques to log some WCF operation calls. I need to log the input parameters and also the result of these operations so I’ve implemented a CallHandler that I can apply to whatever methods that I need to intercept.

In this CallHandler I’ve defined a class called LogEntry and I’m logging with log4net. Something like this:


LogEntry logEntry = new LogEntry();
....
// set some base properties
logEntry.MethodName = input.MethodBase.Name;
logEntry.TypeName = input.Target.GetType().FullName;
...
// set input parameters
logEntry.Input = ....;
...
// set return value
logEntry.ReturnValue = result.ReturnValue;
.....
// set total method duration
logEntry.CallTime = stopwatch.Elapsed.TotalMilliseconds;
.....

// use log4net logger to log this information
logger.Debug(logEntry);

So this LogEntry class has some properties that will hold the information that I want to log. Now I need to capture these properties and configure the log4net appenders properly.

I’m using log4net PatternLayout and PatternConverter features. Let’s start with PatternLayout


///<summary>
/// Extended version of <see cref="PatternLayout"/>
/// </summary>


public class ExtendedPatternLayout : PatternLayout
{
 ///<summary>
 /// Initializes a new instance of the <see cref="ExtendedPatternLayout"/> class.
 /// </summary>;


 public ExtendedPatternLayout()
 {
   this.AddConverter(new ConverterInfo()
   {
     Name = "logEntry",
     Type = typeof(LogEntryConverter),
   });
 }
}

This layout just adds a new Converter named “logEntry” implemented by the class LogEntryConverter. There is no reference to LogEntry class here. And the LogEntryConverter class is here:


 /// <summary>
 /// Custom <see cref="PatternConverter"/> to handle <see cref="LogEntry"/>
 /// </summary>
 public class LogEntryConverter : PatternConverter
 {
   /// <summary>
   /// Converts the specified writer.
   /// </summary>
   /// <param name="writer">The writer.</param>
   /// <param name="state">The state.</param>
   protected override void Convert(System.IO.TextWriter writer, object state)
   {
       var loggingEvent = state as LoggingEvent;
       var logEntry = loggingEvent.MessageObject as LogEntry;

       if (logEntry != null)
       {
          switch (this.Option.ToLower())
          {
             case "calltime":
               writer.Write(logEntry.CallTime.HasValue ? logEntry.CallTime.Value : 0);
               break;
             case "methodname":
               writer.Write(logEntry.MethodName);
               break;
             case "returnvalue":
               writer.Write(SerializationService.SerializeToJSON(logEntry.ReturnValue));
               break;
             ..........
             ..........
             default:
               writer.Write(string.Empty);
               break;
          }
       }
    }
 }

I’ve made the code smaller but you need to check for null references!

I’m using a JSON serialization technique to grab the representation of the method’s return value.

I need to override the Convert method and access state object that is a log4net LoggingEvent object. This object has my logged object from the CallHandler which is a LogEntry object. Based on the Option property of the converter I can choose which property to write.

And finally I can configure my log4net like this


<layout type="ExtendedPatternLayout">
  <conversionPattern value="%logEntry{CallTime}" />
</layout>

You can see the conversionPattern follows this syntax: %convertername{option}

nettiers – GetBy Index problem with StoredProcedure long name

Today I’ve discovered a problem with CodeSmith nettiers templates because it was possible to generate two SPs with the same name, for different entities.

This happens when there are two indexes for two different tables that have the same column names and the column names reach the maximum lenght (128 for SQL). In this case the generated SP name doesn’t contain the table name as a prefix.

Here is the original nettiers code for the GetProcNameForGetByIX method in CommonSqlCode.cs:

// get the key names one at a time until we run out of space
stringbuilder names = new stringbuilder(maxlen);
 
string keyname;
for(int x = 0; x < keys.length; x++)
{
  keyname = getpropertyname(keys[x]);
  if (names.length + keyname.length <= maxlen)
    names.append(keyname);
  else
    break;
}
return names.tostring();

I’ve upgraded the method to receive the index name and the new code is:

if (string.IsNullOrEmpty(indexName))
 {
   throw new Exception("Need index name");
 }
 
 string name = string.Concat(prefix, indexName);
 
 if (name.Length > maxLen)
 {
   throw new Exception("SP name continues to be greater than maxlen");
 }
 
 return name;

Then I needed to pass the index name on all the places that this method was call. Just use find in files and search for that method.

Add WCF Message Id in every log4net message

I needed to have the WCF message identifier in every log message because I needed to correlate the log messages from a particular WCF call.

The best way to achieve this with log4net is to use context properties.

Since I’m already using Unity and Interception on my WCF Services, I’ve decided to implement a custom Call Hander


namespace CustomCallHandlers
{
 using System;
 using Microsoft.Practices.Unity.InterceptionExtension;

 /// <summary>
 /// Call Handler to setup wcf message id in log4net context
 /// </summary>
 public class ServiceCallHandler : ICallHandler
 {
 /// <summary>
 /// The message identifier
 /// </summary>
 private const string MessageIdKey = "MessageId";

 /// <summary>
 /// Initializes a new instance of the <see cref="ServiceCallHandler"/> class.
 /// </summary>
 /// <param name="order">The call handler's execution order.</param>
 public ServiceCallHandler(int order)
 {
  this.Order = order;
 }

 /// <summary>
 /// Gets or sets the order.
 /// </summary>
 public int Order { get; set; }

 /// <summary>
 /// Invokes the specified input.
 /// </summary>
 /// <param name="input">The input.</param>
 /// <param name="getNext">The get next.</param>
 /// <returns>The method return for the nexte invocation</returns>
 public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
 {
  if ((System.ServiceModel.OperationContext.Current != null) &&
      (System.ServiceModel.OperationContext.Current.IncomingMessageHeaders != null))
  {
   if (System.ServiceModel.OperationContext.Current.IncomingMessageHeaders.MessageId != null)
   {
    string messageId = System.ServiceModel.OperationContext.Current.IncomingMessageHeaders.MessageId.ToString();
    log4net.ThreadContext.Properties[MessageIdKey] = messageId == "(null)" ? Guid.NewGuid().ToString() : messageId;
   }
  }

  IMethodReturn resultado = getNext()(input, getNext);

  // Cleaning
  log4net.ThreadContext.Properties[MessageIdKey] = null;

  return resultado;
  }
 }
}

If you want to see how to configure log4net appenders to write custom properties check this post.

 

 

Custom properties log4net

I had the need to add some custom properties in my log4net messages.

I used the log4net contexts like this:


log4net.ThreadContext.Properties[MessageIdKey] = messageId;

And then updated my rolling file appender:


<!-- Rolling File Appender -->
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="log.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date{dd-MM-yyyy HH:mm:ss,fff} (%logger) [%2thread]     [%property{MessageId}] %5level %message%newline" />
</layout>
</appender>

and my AdoNetAppender:


<!-- ADO Appender -->
 <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
 <bufferSize value="5" />
 <lossy value="false" />
 <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
 <connectionString value="data source=localhost;initial catalog=MyDb;integrated security=true;" />
 <commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception],[MessageId]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception, @messageId)" />

 ......

 <parameterName value="@messageId" />
 <dbType value="String" />
 <size value="255" />
 <layout type="log4net.Layout.PatternLayout">
 <conversionPattern value="%property{MessageId}" />
 </layout>
 </parameter>

 </appender>

The Controls collection cannot be modified because the control contains code blocks (i.e. <% … %>)

Because I have pages with the markup <%= someVariable%> like

<script type="text/javascript">
            
  function ConfirmCallBack(arg)
  {
    if (arg)
     <%= this._nextPostBack%>
  }
</script>

I get the following error: "The Controls collection cannot be modified because the control contains code blocks (i.e. <% … %><% … %>)."

To correct it I had to change from

<%= this._nextPostBack%>

to

<%# this._nextPostBack%>

and in the PageLoad event add the call to Page’s DataBind method

protected void Page_Load(object sender, EventArgs e)
{
  ...
  Page.DataBind();
}

Codesmith, netTiers and reserved words

Started today using Codesmith tools today after a year or so since the last time that I used this great generation template based tool.

I had to generate all the layers of an existent portal and I decided to use the netTiers templates to accomplish this task.

And here are the problems that I had to solve:

  • In the Entities templates:

I have a table named Entity that conflicts with the auxiliary class of the netTiers templates. It seems that the templates already generate a class called Entity and because there is a table called Entity there was a conflict.

I solved that using the alias text file feature of the templates that allows me to define a alias for any table that I have. So I just inserted a new line in my alias text file like this:

Entity:MyEntity

With this alias, netTiers generated a class for my table named MyEntity that didn't conflict with his own class Entity.

  • In the Data templates:

I had a table with two foreign keys. But the problem was that the foreign keys were on the same column of the table but referencing two different tables.

The problem was that netTiers generated two methods with the same name and signature GetByColumnForeingKey.

I had to change the schema and use two columns, each one referencing a different column in a different table.

  • In the Domain templates:

I have views in my database and I had compilation problems with the generated classes for these views.

For each view there are two generated files/classes:

MyView.cs and MyViewServiceBase.generated.cs

Where MyView inherits from MyViewBase. Because MyViewBase doesn't exist there was a compilation problem. I just had to strip the "Service" word from the netTiers templates so the file/class generated was MyViewBase.cs instead of MyViewServiceBase.cs

Another problem was with a table with a "Type" column. This gives this error:

Error    1    An object reference is required for the non-static field, method, or property 'TableBase.Type.get'    Domain\TableBase.generated.cs    1394    57    Domain

The generated code was

DeepLoad(entity, false, DeepLoadType.ExcludeChildren, Type.EmptyTypes);

I had to change the template so to genreate

DeepLoad(entity, false, DeepLoadType.ExcludeChildren, System.Type.EmptyTypes);

  • In the Data.SqlClient templates:

There was an ambiguous table named Parameter. netTiers already creates a class named Parameter so I just had to add another line in my alias text file

Parameter:MyParameter

  • And finally in the web templates:

There was an ambiguous table named Page. I just had to add another line in my alias text file.

Page:MyPage

Add a custom section to web.config

If you have created a custom section like this, and if you want to add it programmatically to web.config then you have to:

  1. Use the WebConfigurationManager class and open the web

Configuration config = WebConfigurationManager.OpenWebConfiguration(path, site);

From MSDN

path

The virtual path to the configuration file.

site

The name of the application Web site, as displayed in IIS configuration.

 

  1. Create a ConfigurationSectionGroup and add it to the Configuration object

ConfigurationSectionGroup sectionGroup = new ConfigurationSectionGroup();

config.SectionGroups.Add(“GROUP_NAME”, sectionGroup);

 

  1. Create a new instance of your section (MyCustomSection) and add it to the section group

MyCustomSection section = new MyCustomSection ();

sectionGroup.Sections.Add(“SECTION_NAME”, section);

 

  1. Fill your custom section properties

section.Property1 = “0123456789”;

section.Property2 = “Lorem ipsum dolor sit amet”;

 

  1. And save the configuration

config.Save();

If you take a look at your web.config you will see that it was modified programmatically as you specified. I use this technique in the setup process of sites. Cool!

How to create a custom section for an application config file

First we need to create a custom ConfigurationElement. This configuration element only has a value attribute.

public class MyConfigurationElement : ConfigurationElement

{

/// <summary>

/// Initializes a new instance of the <see cref="ValueConfigurationElement"/> class.

/// </summary>

public MyConfigurationElement ()

{}

 

/// <summary>

/// Gets or sets the value.

/// </summary>

/// <value>The value.</value>

[ConfigurationProperty("value", IsRequired = true)]

public string Value

{

get { return this["value"] as string; }

set { this["value"] = value as string; }

}

}

 

Second we need to create the custom ConfigurationSection :

public class MyCustomSection : ConfigurationSection

{

/// <summary>

/// Gets or sets the property 1.

/// </summary>

/// <value>The property 1.</value>

[ConfigurationProperty("Property1", IsRequired = true)]

public MyConfigurationElement Property1

{

get { return (MyConfigurationElement)this["Property1"]; }

set { this["Property1"] = value; }

}

 

/// <summary>

/// Gets or sets the Property 2.

/// </summary>

/// <value>The Property 2.</value>

[ConfigurationProperty("Property2", IsRequired = true)]

public MyConfigurationElement Property2

{

get { return (MyConfigurationElement)this["Property2"]; }

set { this["Property2"] = value; }

}

 

}

 

This custom section has 2 MyConfigurationElements: Property1 and Property2

And finally, the application config

<sectionGroup name="CustomGroup" type="System.Configuration.ConfigurationSectionGroup, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" >

<section name="CustomSection" type=" MyCustomSection, Version=1.0.0.0, Culture=neutral " />

</sectionGroup>

 

<CustomGroup>

<CustomSection>

<Property1 value="0123456789" />

<Property2 value="Lorem ipsum dolor sit amet" />

</CustomSection>

</CustomGroup>

 

See here how you can add this section programmatically to your web.config

Different web.configs

In the project that I'm currently working I had a problem because my connection strings are different from the other developers: I want to work locally in my SQL Express so the connections strings are different and since we are developing with Visual Source Safe 2005 as the version control system it is not possible to change the file where the connections strings are stored.

The easiest way was not to keep the web.config file under source control but of course that wasn't the desired way because we have lots of settings that we want to keep under source control.

After googling a while I found that the appSettings section in the web.config file of an ASP.NET web application has an attribute – file – that can point to an external file where we can store additional settings.

So my initial web.config file is:

<appSettings

<add
key="DB_1_ConnectionStringName"
value="DB_1" />

<add
key="DB_2_ConnectionStringName"
value="DB_2" />

</appSettings>

 

<connectionStrings>

<add
name="DB_1"
connectionString="server=db_server;database=db_1; … "
providerName="System.Data.SqlClient" />

<add
name="DB_2"
connectionString="server=db_server;database=DB_2; … "
providerName="System.Data.SqlClient" />

</connectionStrings>

Of course, when I want to work locally in my SQL Express instance this doesn't work. So I changed the web.config file like this:

<appSettings file="custom.config"

<add
key="DB_1_ConnectionStringName"
value="DB_1" />

<add
key="DB_2_ConnectionStringName"
value="DB_2" />

</appSettings>

 

<connectionStrings>

<add
name="DB_1"
connectionString="server=db_server;database=db_1; … "
providerName="System.Data.SqlClient" />

<add
name="DB_2"
connectionString="server=db_server;database=DB_2; … "
providerName="System.Data.SqlClient" />

 

<add
name="DB_1_LOCAL"
connectionString="server=.\SQLEXPRESS;database=db_1; … "
providerName="System.Data.SqlClient" />

<add
name="DB_2_LOCAL "
connectionString="server=db_.\SQLEXPRESS;database=DB_2; … "
providerName="System.Data.SqlClient" />

 

</connectionStrings>

And my custom.config file is:

<appSettings

<add
key="DB_1_ConnectionStringName"
value="DB_1_LOCAL" />

<add
key="DB_2_ConnectionStringName"
value="DB_2_LOCAL" />

</appSettings>

 

What happens is that the settings in the custom.config file override the settings in the web.config file.

The other developers don't need to have the custom.config files because "the runtime ignores the attribute, if the specified file cannot be found" [msdn].

So I have only the custom.config file that it isn't under source control but that's no problem because I only redefine the name of the connection strings. The actual connection strings are still stored in the web.config file and under source control.