我不久前使用 log4net 设置了日志记录,只是没有想到特定的场景。
目前我有一个全局配置文件。有一个 RollingFileAppender 和一个 AdonetAppender。
当数据库上只有一个程序实例运行时,一切都很好。 有时,两个略有不同的实例可能会在同一数据库上运行。附加程序记录来自两个实例的消息,但通常(在公共(public)代码中)无法判断消息源自哪个实例。当然,我可以将实例名称添加到消息或其他内容中,但这似乎是最后的手段。
我的大脑就像一团雾。我认为解决方案应该相当简单,但我只是想不出最好的解决方案。
我可以创建另一个附加程序,并在运行时根据实例名称“切换”正在使用的附加程序吗?
这是配置文件:
<?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 上下文属性
。
使用的范围取决于您的场景,请参阅 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
首先确保您要记录的表包含用于存储 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')
在值为“Instance2”的实例2的xml
配置文件中执行相同的操作。
选项 3:单个命名记录器
如果您在整个应用程序中仅使用单个 ILog
实例,请使用 appIdentifier
的值按名称检索它。
RollingFileAppender
的 %logger
标记和 AdoNetAppender
的 @logger
标记将包含值 '分别为“实例1”或“实例2”。
我不喜欢使用单个记录器,但我经常看到这种做法。
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 上下文属性
。
可以在 RollingFileAppender
的输出文件的路径中包含此类 Log4net 上下文属性
。
这样做将分别生成文件“Instance1.log”或“Instance2.log”。
(您也可以将其应用于(子)文件夹名称,而不是更改文件名。)
如果您遇到或担心锁定和/或性能问题,那么在同时运行的多个应用程序实例之间不共享相同的输出文件可能是个好主意。
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
...
<file type="log4net.Util.PatternString" value="D:\Medupi logs\%property{appIdentifier}.log" />
...
</appender>
关于c# - Log4net 在同一数据库上有两个不同的实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51293685/