同一个数据库上有两个不同实例的 Log4net
Log4net with two different instances on same database
不久前我使用 log4net 设置了日志记录,只是没有想到特定的场景。
目前我有一个全局配置文件。有一个 RollingFileAppender 和一个 AdonetAppender。
当数据库中只有一个程序实例 运行ning 时,一切都很好。
有时,两个略有不同的实例可能在同一个数据库上 运行。附加程序记录来自两个实例的消息,但通常(在公共代码中)无法分辨消息来自哪个实例。当然,我可以将实例名称添加到消息中或其他任何内容,但这似乎是最后的选择。
我的大脑像一团雾。我认为解决方案应该很简单,但我想不出什么是最好的。
我可以创建另一个 appender,并且 "switch" 在 运行 期间根据实例名称使用哪个 appender?
这是配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<log4net debug="true">
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<file value="D:\Medupi logs\CalcEngineLog.txt"/>
<appendToFile value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="2"/>
<maximumFileSize value="4MB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %level %logger %location - %message%newline%exception"/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG"/>
<levelMax value="FATAL"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<!-- <threshold value="Warn" /> -->
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="" />
<commandText value="INSERT INTO [Calculation Engine Log] ([Date],[Thread],[Level],[Logger],[Location],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @location, @message, @exception)" />
<parameter>
<parameterName value="@log_date" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<parameter>
<parameterName value="@thread" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%thread" />
</layout>
</parameter>
<parameter>
<parameterName value="@log_level" />
<dbType value="String" />
<size value="50" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level" />
</layout>
</parameter>
<parameter>
<parameterName value="@logger" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger" />
</layout>
</parameter>
<parameter>
<parameterName value="@location" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%location" />
</layout>
</parameter>
<parameter>
<parameterName value="@message" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message" />
</layout>
</parameter>
<parameter>
<parameterName value="@exception" />
<dbType value="String" />
<size value="2000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
</appender>
<root>
<level value="Debug"/>
<appender-ref ref="AdoNetAppender"/>
<appender-ref ref="RollingFileAppender"/>
<appender-ref ref="DebugAppender"/>
</root>
</log4net>
在启动项目中是这样配置的:
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
static void Main()
{
if (!log4net.LogManager.GetRepository().Configured)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\Program Files\Symplexity\bin\Log4NetSettingsGlobal.xml"));
}
...
}
附加信息
在生产中,启动了一个 Windows 服务,这会检查一个配置文件,其实例必须在集群上 运行。
每个实例都有一个单独的进程:
foreach (var cluster in clusters)
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
CreateNoWindow = true,
FileName = processFileName,
WindowStyle = ProcessWindowStyle.Hidden,
Arguments = string.Format("\"{0}\" \"{1}\"", cluster.Name, _ConfigPath)
};
_ClusterProcesses.Add(Process.Start(startInfo));
}
我假设我可以将 appIdentifier(根据评论)添加到 startInfo
?
的 Arguments
首先确定 2 个应用程序实例。
最简单的方法是使用唯一的命令行参数启动每个应用程序实例。
通过例如启动第一个实例。 .cmd
文件为 MyApplication.exe instance1
,第二个文件为 MyApplication.exe instance2
.
从方法 Main
.
的 args
参数中读取此参数值
static void Main(String[] args)
{
String appIdentifier = args[0];
// ...
}
现在您有多种选择。
选项 1:两个实例的共享 Log4net 配置
将 appIdentifier
分配给 Log4net context property
。
使用的范围取决于您的场景,请参阅 the Log4net documentation.
log4net.GlobalContext.Properties["appIdentifier"] = appIdentifier;
RollingFileAppender
通过 %property{appIdentifier}
在 RollingFileAppender
的 layout
中包含此上下文 属性。
每个记录的行现在将包含适当的 appIdentifier
值 'instance1' 或 'instance2'.
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="... %property{appIdentifier} ..."/>
</layout>
AdoNetAppender
首先确保您要登录的 table 包含一个用于存储 appIdentifier
值的列,
例如。列 appIdentifier VARCHAR(25)
.
使用额外参数扩展 AdoNetAppender
的配置以匹配 appIdentifier
属性。
<parameter>
<parameterName value="@appIdentifier"/>
<dbType value="String" />
<size value="25" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{appIdentifier}" />
</layout>
</parameter>
用相应的列名和参数扩展 SQL INSERT
语句。
INSERT INTO [Calculation Engine Log] ([Date],[Thread],[Level],[Logger],[Location],[Message],[Exception],[appIdentifier])
VALUES (@log_date, @thread, @log_level, @logger, @location, @message, @exception, @appIdentifier)
选项 2:每个实例一个单独的 Log4net 配置文件
这不像选项 1 那样漂亮...但仍然是一个选项。
通过将文件名与 appIdentifier
命令行参数匹配,为每个实例创建一个单独的 xml
配置文件。
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\Program Files\Symplexity\bin\" + appIdentifier + '.xml'));
RollingFileAppender
为每个实例配置单独的输出文件路径。
例如1
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
...
<file value="D:\Medupi logs\Instance1.txt"/>
...
</appender>
例如2
<file value="D:\Medupi logs\Instance2.txt"/>
AdoNetAppender
参见选项 1,了解如何设置列来存储 appIdentifier
。
在 SQL INSERT
语句中包含一个常量值作为标识符。
INSERT INTO [Calculation Engine Log] ([Date],[Thread],[Level],[Logger],[Location],[Message],[Exception],[appIdentifier])
VALUES (@log_date, @thread, @log_level, @logger, @location, @message, @exception, 'Instance1')
在 xml
配置文件中为 instance2 执行相同的操作,值为 'Instance2'。
选项 3:单个命名记录器
如果您在整个应用程序中只使用一个 ILog
实例,请使用 appIdentifier
.
的值按名称检索它
RollingFileAppender
的 %logger
标记和 AdoNetAppender
的 @logger
标记将分别包含值 'Instance1' 或 'Instance2'。
我不喜欢使用单个记录器,但我经常看到这种做法。
private static log4net.ILog log;
static void Main(String args)
{
String appIdentifier = args[0];
if (!log4net.LogManager.GetRepository().Configured)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\Program Files\Symplexity\bin\Log4NetSettingsGlobal.xml"));
log = log4net.LogManager.GetLogger(appIdentifier);
}
// ...
}
选项 4:RollingFileAppender 的参数化文件名
这只是 RollingFileAppender
的一个替代选项,将其与上述的选项之一结合AdoNetAppender
.
按照选项 1 中的说明设置 Log4net context property
。
可以在 RollingFileAppender
.
的输出文件路径中包含这样的 Log4net context properties
这样做将分别生成一个文件 'Instance1.log' 或 'Instance2.log'。
(除了改变文件名,您还可以将其应用于(子)文件夹名称。)
不在多个应用程序实例之间共享相同的输出文件 运行 如果您有或担心锁定 and/or 并发可能是个好主意性能问题。
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
...
<file type="log4net.Util.PatternString" value="D:\Medupi logs\%property{appIdentifier}.log" />
...
</appender>
不久前我使用 log4net 设置了日志记录,只是没有想到特定的场景。
目前我有一个全局配置文件。有一个 RollingFileAppender 和一个 AdonetAppender。
当数据库中只有一个程序实例 运行ning 时,一切都很好。 有时,两个略有不同的实例可能在同一个数据库上 运行。附加程序记录来自两个实例的消息,但通常(在公共代码中)无法分辨消息来自哪个实例。当然,我可以将实例名称添加到消息中或其他任何内容,但这似乎是最后的选择。
我的大脑像一团雾。我认为解决方案应该很简单,但我想不出什么是最好的。
我可以创建另一个 appender,并且 "switch" 在 运行 期间根据实例名称使用哪个 appender?
这是配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<log4net debug="true">
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<file value="D:\Medupi logs\CalcEngineLog.txt"/>
<appendToFile value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="2"/>
<maximumFileSize value="4MB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %level %logger %location - %message%newline%exception"/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="DEBUG"/>
<levelMax value="FATAL"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<!-- <threshold value="Warn" /> -->
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="" />
<commandText value="INSERT INTO [Calculation Engine Log] ([Date],[Thread],[Level],[Logger],[Location],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @location, @message, @exception)" />
<parameter>
<parameterName value="@log_date" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<parameter>
<parameterName value="@thread" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%thread" />
</layout>
</parameter>
<parameter>
<parameterName value="@log_level" />
<dbType value="String" />
<size value="50" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level" />
</layout>
</parameter>
<parameter>
<parameterName value="@logger" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger" />
</layout>
</parameter>
<parameter>
<parameterName value="@location" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%location" />
</layout>
</parameter>
<parameter>
<parameterName value="@message" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message" />
</layout>
</parameter>
<parameter>
<parameterName value="@exception" />
<dbType value="String" />
<size value="2000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
</appender>
<root>
<level value="Debug"/>
<appender-ref ref="AdoNetAppender"/>
<appender-ref ref="RollingFileAppender"/>
<appender-ref ref="DebugAppender"/>
</root>
</log4net>
在启动项目中是这样配置的:
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
static void Main()
{
if (!log4net.LogManager.GetRepository().Configured)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\Program Files\Symplexity\bin\Log4NetSettingsGlobal.xml"));
}
...
}
附加信息
在生产中,启动了一个 Windows 服务,这会检查一个配置文件,其实例必须在集群上 运行。
每个实例都有一个单独的进程:
foreach (var cluster in clusters)
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
CreateNoWindow = true,
FileName = processFileName,
WindowStyle = ProcessWindowStyle.Hidden,
Arguments = string.Format("\"{0}\" \"{1}\"", cluster.Name, _ConfigPath)
};
_ClusterProcesses.Add(Process.Start(startInfo));
}
我假设我可以将 appIdentifier(根据评论)添加到 startInfo
?
Arguments
首先确定 2 个应用程序实例。
最简单的方法是使用唯一的命令行参数启动每个应用程序实例。
通过例如启动第一个实例。 .cmd
文件为 MyApplication.exe instance1
,第二个文件为 MyApplication.exe instance2
.
从方法 Main
.
args
参数中读取此参数值
static void Main(String[] args)
{
String appIdentifier = args[0];
// ...
}
现在您有多种选择。
选项 1:两个实例的共享 Log4net 配置
将 appIdentifier
分配给 Log4net context property
。
使用的范围取决于您的场景,请参阅 the Log4net documentation.
log4net.GlobalContext.Properties["appIdentifier"] = appIdentifier;
RollingFileAppender
通过 %property{appIdentifier}
在 RollingFileAppender
的 layout
中包含此上下文 属性。
每个记录的行现在将包含适当的 appIdentifier
值 'instance1' 或 'instance2'.
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="... %property{appIdentifier} ..."/>
</layout>
AdoNetAppender
首先确保您要登录的 table 包含一个用于存储 appIdentifier
值的列,
例如。列 appIdentifier VARCHAR(25)
.
使用额外参数扩展 AdoNetAppender
的配置以匹配 appIdentifier
属性。
<parameter>
<parameterName value="@appIdentifier"/>
<dbType value="String" />
<size value="25" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{appIdentifier}" />
</layout>
</parameter>
用相应的列名和参数扩展 SQL INSERT
语句。
INSERT INTO [Calculation Engine Log] ([Date],[Thread],[Level],[Logger],[Location],[Message],[Exception],[appIdentifier])
VALUES (@log_date, @thread, @log_level, @logger, @location, @message, @exception, @appIdentifier)
选项 2:每个实例一个单独的 Log4net 配置文件
这不像选项 1 那样漂亮...但仍然是一个选项。
通过将文件名与 appIdentifier
命令行参数匹配,为每个实例创建一个单独的 xml
配置文件。
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\Program Files\Symplexity\bin\" + appIdentifier + '.xml'));
RollingFileAppender
为每个实例配置单独的输出文件路径。
例如1
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
...
<file value="D:\Medupi logs\Instance1.txt"/>
...
</appender>
例如2
<file value="D:\Medupi logs\Instance2.txt"/>
AdoNetAppender
参见选项 1,了解如何设置列来存储 appIdentifier
。
在 SQL INSERT
语句中包含一个常量值作为标识符。
INSERT INTO [Calculation Engine Log] ([Date],[Thread],[Level],[Logger],[Location],[Message],[Exception],[appIdentifier])
VALUES (@log_date, @thread, @log_level, @logger, @location, @message, @exception, 'Instance1')
在 xml
配置文件中为 instance2 执行相同的操作,值为 'Instance2'。
选项 3:单个命名记录器
如果您在整个应用程序中只使用一个 ILog
实例,请使用 appIdentifier
.
RollingFileAppender
的 %logger
标记和 AdoNetAppender
的 @logger
标记将分别包含值 'Instance1' 或 'Instance2'。
我不喜欢使用单个记录器,但我经常看到这种做法。
private static log4net.ILog log;
static void Main(String args)
{
String appIdentifier = args[0];
if (!log4net.LogManager.GetRepository().Configured)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("C:\Program Files\Symplexity\bin\Log4NetSettingsGlobal.xml"));
log = log4net.LogManager.GetLogger(appIdentifier);
}
// ...
}
选项 4:RollingFileAppender 的参数化文件名
这只是 RollingFileAppender
的一个替代选项,将其与上述的选项之一结合AdoNetAppender
.
按照选项 1 中的说明设置 Log4net context property
。
可以在 RollingFileAppender
.
Log4net context properties
这样做将分别生成一个文件 'Instance1.log' 或 'Instance2.log'。
(除了改变文件名,您还可以将其应用于(子)文件夹名称。)
不在多个应用程序实例之间共享相同的输出文件 运行 如果您有或担心锁定 and/or 并发可能是个好主意性能问题。
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
...
<file type="log4net.Util.PatternString" value="D:\Medupi logs\%property{appIdentifier}.log" />
...
</appender>