0

I want to use a ConnectionString that I will define during runtime. I found alot of example, but I can't make it work.

I created a custom AdoNetAppender:

 public class AdoNetMultiTenantAppender : AdoNetAppender
{
    public new string ConnectionString
    {
        get
        {
            return base.ConnectionString;
        }

        set
        {
            base.ConnectionString = Tenant.Current.DataSource.ConnectionString; // Return the connection string
        }
    }
}

I have the following configuration:

<appender name="AdoNetMultiTenantAppender" type="MyNameSpace.AdoNetMultiTenantAppender">
    <bufferSize value="1" />
    <connectionstring value="" />
    <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    <commandText value="[...]" />
    <parameter>
        [...]
    </parameter>
</appender>

my logger is define like this in the config file:

  <logger name="ProcessLogger" additivity="false">
    <level value="INFO"/>
    <appender-ref ref="AdoNetMultiTenantAppender"/>
  </logger>

And finally, to get my Logger on my code i'm doing:

[...]    
private static readonly ILog Logger = LogManager.GetLogger("ProcessLogger");
[...]
ProcessLogger.Logger.Info(message);
[...]

When i'm trying to "hard code" the Connection string into my config it's working. But I can't do this because I need a different ConnectionString depending on some variables. The property ConnectionString that I put in my custom appender is just never call. Any idea where i'm missing something?

2 Answers2

2

What version of log4net are you using?

In any case, a new ConnectionString property in your derived class isn't going to be called automatically from the existing infrastructure.

You might have more success by overriding a suitable virtual member: for example, if you're using log4net 1.2.11 you could override the CreateConnection or ResolveConnectionString method.

UPDATE in respone to comment:

But the CreateConnection is only used once, so I can't use the same appender for multiple ConnectionString

Yes, the log4net AdoNetAppender has an unusual design, in that a connection object is left open and reused for each logging request. The contrasts with the usual recommended approach, which is to create/open a db connection as late as possible, and close it immediately after use. The recommended approach allows an application to take advantage for ADO.NET's built-in connection pooling.

One thing you could try might be to override SendBuffer(LoggingEvent[] events), and in your override call the base class then close the connection. This will force the connection to be reopened each time SendBuffer is called.

I'm not sure if this will give you everything you want though - you talk of a Multitenant application using multiple connection strings. In this case the array of LoggingEvent passed to SendBuffer may contain events that should be sent to different connections. Perhaps you would need to use some attribute of LoggingEvent to split the input array by target connection, then for each target connection, open the connection, call base.SendBuffer then close the connection.

Joe
  • 122,218
  • 32
  • 205
  • 338
  • Thanks, i'm using 1.2.13 and I tried to override the CreateConnection and I found that the real problem was the moment when the ConnectionString is create. In fact I want to be able to use different ConnectionString because I have a Multitenant application. But the CreateConnection is only used once, so I can't use the same appender for multiple ConnectionString. – Marc-André Bilodeau-Lamontagne Jan 29 '15 at 14:16
  • Thanks again. It's exactly what I was about to do. I am overriding the SendBuffer and use the message as an object with all the data I need to log on the good tenant. Thanks again! – Marc-André Bilodeau-Lamontagne Jan 29 '15 at 15:28
1

This will work on an existing AdoNetAppender:

public static void SetConnectionString(string connectionString)
{
    Hierarchy logHierarchy = log4net.LogManager.GetRepository() as Hierarchy;

    if (logHierarchy == null)
    {
        throw new InvalidOperationException("Can't set connection string as hierarchy is null. Has logging been initialised?");
    }

    // Assumes there is only one appender to be configured
    var appender = logHierarchy.GetAppenders().OfType<AdoNetAppender>().SingleOrDefault();

    if (appender == null)
    {
        throw new InvalidOperationException("Can't set connection string as can't locate a database appender");
    }

    appender.ConnectionString = connectionString;
    appender.ActivateOptions();
}

Note that the AdoNetAppender will complain on configuration that the connection string value has not been set, but it doesn't matter, and you probably won't notice anyway unless you turn on breaking on all exceptions.

stuartd
  • 70,509
  • 14
  • 132
  • 163