在带有 log4net 的自定义 AdoNetAppender 中使用不同的 ConnectionString
Using different ConnectionString in a Custom AdoNetAppender with log4net
我想使用我将在运行时定义的 ConnectionString。我找到了很多例子,但我不能让它工作。
我创建了一个自定义的 AdoNetAppender:
public class AdoNetMultiTenantAppender : AdoNetAppender
{
public new string ConnectionString
{
get
{
return base.ConnectionString;
}
set
{
base.ConnectionString = Tenant.Current.DataSource.ConnectionString; // Return the connection string
}
}
}
我有以下配置:
<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>
我的记录器在配置文件中是这样定义的:
<logger name="ProcessLogger" additivity="false">
<level value="INFO"/>
<appender-ref ref="AdoNetMultiTenantAppender"/>
</logger>
最后,为了让我的 Logger 进入我正在做的代码:
[...]
private static readonly ILog Logger = LogManager.GetLogger("ProcessLogger");
[...]
ProcessLogger.Logger.Info(message);
[...]
当我尝试 "hard code" 连接字符串进入我的配置时,它正在工作。但我不能这样做,因为我需要根据某些变量使用不同的 ConnectionString。我放在自定义附加程序中的 属性 ConnectionString 永远不会调用。知道我遗漏了什么吗?
您使用的是什么版本的 log4net?
在任何情况下,派生的 class 中的 new
ConnectionString 属性 都不会从现有基础结构中自动调用。
您可以通过覆盖合适的虚拟成员获得更多成功:例如,如果您使用的是 log4net 1.2.11,则可以覆盖 CreateConnection
或 ResolveConnectionString
方法。
更新 回复评论:
But the CreateConnection is only used once, so I can't use the same appender for multiple ConnectionString
是的,log4net AdoNetAppender
有一个不寻常的设计,因为连接对象保持打开状态并为每个日志记录请求重用。这与通常推荐的方法形成对比,后者是 create/open 尽可能晚地建立数据库连接,并在使用后立即关闭它。推荐的方法允许应用程序利用 ADO.NET 的内置连接池。
您可以尝试的一件事是覆盖 SendBuffer(LoggingEvent[] events)
,并在您的覆盖中调用基础 class,然后关闭连接。这将在每次调用 SendBuffer
时强制重新打开连接。
我不确定这是否会为您提供您想要的一切 - 您谈到了使用多个连接字符串的多租户应用程序。在这种情况下,传递给 SendBuffer
的 LoggingEvent
数组可能包含应该发送到不同连接的事件。也许您需要使用 LoggingEvent
的某些属性按目标连接拆分输入数组,然后为每个目标连接打开连接,调用 base.SendBuffer
然后关闭连接。
这将适用于现有的 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();
}
请注意,AdoNetAppender 会在配置中抱怨未设置连接字符串值,但这并不重要,除非您打开所有异常中断,否则您可能不会注意到。
我想使用我将在运行时定义的 ConnectionString。我找到了很多例子,但我不能让它工作。
我创建了一个自定义的 AdoNetAppender:
public class AdoNetMultiTenantAppender : AdoNetAppender
{
public new string ConnectionString
{
get
{
return base.ConnectionString;
}
set
{
base.ConnectionString = Tenant.Current.DataSource.ConnectionString; // Return the connection string
}
}
}
我有以下配置:
<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>
我的记录器在配置文件中是这样定义的:
<logger name="ProcessLogger" additivity="false">
<level value="INFO"/>
<appender-ref ref="AdoNetMultiTenantAppender"/>
</logger>
最后,为了让我的 Logger 进入我正在做的代码:
[...]
private static readonly ILog Logger = LogManager.GetLogger("ProcessLogger");
[...]
ProcessLogger.Logger.Info(message);
[...]
当我尝试 "hard code" 连接字符串进入我的配置时,它正在工作。但我不能这样做,因为我需要根据某些变量使用不同的 ConnectionString。我放在自定义附加程序中的 属性 ConnectionString 永远不会调用。知道我遗漏了什么吗?
您使用的是什么版本的 log4net?
在任何情况下,派生的 class 中的 new
ConnectionString 属性 都不会从现有基础结构中自动调用。
您可以通过覆盖合适的虚拟成员获得更多成功:例如,如果您使用的是 log4net 1.2.11,则可以覆盖 CreateConnection
或 ResolveConnectionString
方法。
更新 回复评论:
But the CreateConnection is only used once, so I can't use the same appender for multiple ConnectionString
是的,log4net AdoNetAppender
有一个不寻常的设计,因为连接对象保持打开状态并为每个日志记录请求重用。这与通常推荐的方法形成对比,后者是 create/open 尽可能晚地建立数据库连接,并在使用后立即关闭它。推荐的方法允许应用程序利用 ADO.NET 的内置连接池。
您可以尝试的一件事是覆盖 SendBuffer(LoggingEvent[] events)
,并在您的覆盖中调用基础 class,然后关闭连接。这将在每次调用 SendBuffer
时强制重新打开连接。
我不确定这是否会为您提供您想要的一切 - 您谈到了使用多个连接字符串的多租户应用程序。在这种情况下,传递给 SendBuffer
的 LoggingEvent
数组可能包含应该发送到不同连接的事件。也许您需要使用 LoggingEvent
的某些属性按目标连接拆分输入数组,然后为每个目标连接打开连接,调用 base.SendBuffer
然后关闭连接。
这将适用于现有的 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();
}
请注意,AdoNetAppender 会在配置中抱怨未设置连接字符串值,但这并不重要,除非您打开所有异常中断,否则您可能不会注意到。