Log4Net, How to Add a Custom Field to My Logging

Log4Net, how to add a custom field to my logging

1) Modify the command text: INSERT INTO Log4Net ([Date],[Thread],[Level],[Logger],[Message],[Exception],[MyColumn]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception, @CustomColumn)

2) Add the parameter definition for the custom column:

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

3) Then use one of log4net’s contexts to transfer values to the parameter:

// thread properties...
log4net.LogicalThreadContext.Properties["CustomColumn"] = "Custom value";
log.Info("Message");

// ...or global properties
log4net.GlobalContext.Properties["CustomColumn"] = "Custom value";

add custom column based on log in log4net

First, I created a custom AdoNetAppenderParameter:

public class IsCriticalAdoNetParameter : AdoNetAppenderParameter
{
public override void FormatValue(IDbCommand command, LoggingEvent loggingEvent)
{
// Lookup the parameter
IDbDataParameter param = (IDbDataParameter)command.Parameters[ParameterName];

param.Value = loggingEvent.Level > Level.Warn;
}
}

Next, I referenced it in my app.config's ADO Net Appender. Notice the change in the Insert statemenmt and the added <paramemter> tag. THe parameter's type attribute references the assembly/class name of your custom appender.

 <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
<bufferSize value="1"/>
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<connectionString value="Data Source=localhost\SQLEXPRESS;Initial Catalog=my_db;Integrated Security=True"/>
<commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception],[IsCritical]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception, @is_critical)"
/>
<parameter>
<parameterName value="@log_date"/>
<dbType value="DateTime"/>
<layout type="log4net.Layout.RawUtcTimeStampLayout"/>
</parameter>
<parameter>
<parameterName value="@thread"/>
<dbType value="String"/>
<size value="32"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%t"/>
</layout>
</parameter>
<parameter>
<parameterName value="@log_level"/>
<dbType value="String"/>
<size value="512"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%p"/>
</layout>
</parameter>
<parameter>
<parameterName value="@logger"/>
<dbType value="String"/>
<size value="512"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%c"/>
</layout>
</parameter>
<parameter>
<parameterName value="@message"/>
<dbType value="String"/>
<size value="4000"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%m"/>
</layout>
</parameter>
<parameter>
<parameterName value="@exception"/>
<dbType value="String"/>
<size value="2000"/>
<layout type="log4net.Layout.ExceptionLayout"/>
</parameter>
<parameter type="MyApp.Logging.IsCriticalAdoNetParameter">
<parameterName value="@is_critical"/>
<dbType value="Boolean"/>
<size value="10"/>
<layout type="log4net.Layout.PatternLayout" value=""/>
</parameter>
</appender>

You can use this concept to create your own appender parameters.

log4net cant insert data to custom column

Your code is not correct, it should be:

public static void Error(object msg, string userip)
{
log4net.LogicalThreadContext.Properties["ClientIp"] = userip;
if (log.IsErrorEnabled)
{
log.Error(msg); //<----Here is your error
}
}

And add in the ip column:

<parameter>
<parameterName value="@clientip" />
<dbType value="String" />
<size value="50" />
<layout type="log4net.Layout.PatternLayout" >
<conversionPattern value="%property{ClientIp}" />
</layout>
</parameter>

log4net database logging with custom parameters

After some experimentation, I finally got this to work. Ensuring that log4net's internal logging helped identify the errors and downloading the log4net source code and reviewing the AdoNetAppenderParameter class showed how the FormatValue() method should be used. So, here's the amended custom appender parameter:

public class UserAdoNetAppenderParameter : AdoNetAppenderParameter
{

public override void FormatValue(IDbCommand command, LoggingEvent loggingEvent)
{
string[] data = loggingEvent.RenderedMessage.Split('~');
string username = string.Empty;
if (data != null && data.Length >= 1)
username = data[0];

// Lookup the parameter
IDbDataParameter param = (IDbDataParameter)command.Parameters[ParameterName];

// Format the value
object formattedValue = username;

// If the value is null then convert to a DBNull
if (formattedValue == null)
{
formattedValue = DBNull.Value;
}

param.Value = formattedValue;
}

}

And to use this, I add it in the log4net config file like this:

<parameter type="MyAssembly.Logging.UserAdoNetAppenderParameter, MyAssembly">
<parameterName value="@username" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout" value="%message" />
</parameter>

And by convention, my log statements will be something like this:

if (log.IsDebugEnabled)
log.DebugFormat("{0}~{1}~{2}", username, someOtherParameter, message);

If you look at the class, it uses data[0] as the username, so it is dependant on following the convention. It does, however, get the username into its own parameter and into a separate field in the log database table, without resorting to stuffing it temporarily into the unsafe ThreadContext.

Log4Net and extra fields

You could use the "context" feature in log4net. Basically it allows you to set properties that you can then use in your log appender. You can set these properties at different scopes (Global, Thread etc.). In your case I think you could go (for instance, just after the user has logged in):

log4net.ThreadContext.Properties["userid"] = userid;

In your configuration file, you could then use this property and add it to the logging appender. I think it would be something like this for the AdoNetAppender

<parameter>
<parameterName value="@userid" />
<dbType value="guid" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{userid}" />
</layout>
</parameter>

Please note that I have not compiled any of the snippets above, so they might need some tweaking, but it should give you a general idea of how this could be solved.

You can read more about this here.

Ps. I think that the MDC.Set that is referred to in the first answer is obsolete.



Related Topics



Leave a reply



Submit