我们的应用程序使用Enterprise Library日志版本3.1来写入平面文件日志。在每次执行Logger.Write之后我们调用了Logger.Writer.Dispose(),这允许我们在应用程序仍在运行时删除或访问平面文件。虽然这不建议用于性能并且不是线程安全的,但我们需要能够从多个线程写入文件。我们不想花太多时间设置msmq并编写自定义日志记录,因此dispose方法效果最好。
升级到6.0版后,我们再也无法处理Logger.Writer而无法再次创建它。
我们需要做些什么来确定Logger.Writer是否已经处置并重新创建它?
这就是我创建Logger.Writer的方式:`
IConfigurationSource configurationSource = ConfigurationSourceFactory.Create();
LogWriterFactory logWriterFactory = new LogWriterFactory(configurationSource);
Logger.SetLogWriter(logWriterFactory.Create(), false);`
有没有办法查看LogWriter是否已设置/创建或检查它是否已被处置?
答案 0 :(得分:0)
我可以想出4种方法来做你想做的事情:
InvalidOperationException
实例为空时,抓住Logger.Writer
引发的LogWriter
。LogWriter
并检查该类型以确定LogWriter
是否为空。Logger
外观代码并添加所需的确切功能。writer
成员变量这些方法试图解决的主要障碍是,如果值为null,Logger facade不会授予对LogWriter
实例的访问权限。相反,它会抛出InvalidOperationException
。
选项1的缺点是,捕获每个日志记录调用的异常都是性能损失(尽管由于您的现有设计已经受到重创)并且被认为是代码味道。
代码可能如下所示:
Log("Test 1", "General");
Log("Test 2", "General");
static void Log(string message, string category)
{
bool isLogWriterDisposed = false;
try
{
// If Writer is null InvalidOperation will be thrown
var logWriter = Logger.Writer;
}
catch (InvalidOperationException e)
{
isLogWriterDisposed = true;
}
if (isLogWriterDisposed)
{
InitializeLogger();
}
// Write message
Logger.Write(message, category);
// Dispose the existing LogWriter and set it to null
Logger.Reset();
}
选项2可能如下所示:
public class NullLogWriter : LogWriter
{
/// <inheritdoc />
public NullLogWriter(LoggingConfiguration config) : base(config)
{
}
/// <inheritdoc />
public NullLogWriter(IEnumerable<ILogFilter> filters, IDictionary<string, LogSource> traceSources, LogSource errorsTraceSource, string defaultCategory) : base(filters, traceSources, errorsTraceSource, defaultCategory)
{
}
/// <inheritdoc />
public NullLogWriter(IEnumerable<ILogFilter> filters, IDictionary<string, LogSource> traceSources, LogSource allEventsTraceSource, LogSource notProcessedTraceSource, LogSource errorsTraceSource, string defaultCategory, bool tracingEnabled, bool logWarningsWhenNoCategoriesMatch) : base(filters, traceSources, allEventsTraceSource, notProcessedTraceSource, errorsTraceSource, defaultCategory, tracingEnabled, logWarningsWhenNoCategoriesMatch)
{
}
/// <inheritdoc />
public NullLogWriter(IEnumerable<ILogFilter> filters, IDictionary<string, LogSource> traceSources, LogSource allEventsTraceSource, LogSource notProcessedTraceSource, LogSource errorsTraceSource, string defaultCategory, bool tracingEnabled, bool logWarningsWhenNoCategoriesMatch, bool revertImpersonation) : base(filters, traceSources, allEventsTraceSource, notProcessedTraceSource, errorsTraceSource, defaultCategory, tracingEnabled, logWarningsWhenNoCategoriesMatch, revertImpersonation)
{
}
/// <inheritdoc />
public NullLogWriter(IEnumerable<ILogFilter> filters, IEnumerable<LogSource> traceSources, LogSource errorsTraceSource, string defaultCategory) : base(filters, traceSources, errorsTraceSource, defaultCategory)
{
}
/// <inheritdoc />
public NullLogWriter(IEnumerable<ILogFilter> filters, IEnumerable<LogSource> traceSources, LogSource allEventsTraceSource, LogSource notProcessedTraceSource, LogSource errorsTraceSource, string defaultCategory, bool tracingEnabled, bool logWarningsWhenNoCategoriesMatch) : base(filters, traceSources, allEventsTraceSource, notProcessedTraceSource, errorsTraceSource, defaultCategory, tracingEnabled, logWarningsWhenNoCategoriesMatch)
{
}
/// <inheritdoc />
public NullLogWriter(LogWriterStructureHolder structureHolder) : base(structureHolder)
{
}
}
class Program
{
private static readonly LogWriter NullLogWriter = new NullLogWriter(new LoggingConfiguration());
static void Main(string[] args)
{
Log("Test 1", "General");
Log("Test 2", "General");
}
static void Log(string message, string category)
{
if (HasLogWriterBeenDisposed())
{
InitializeLogger();
}
// Write message
Logger.Write(message, category);
// Set the logger to the null logger this disposes the current LogWriter
// Note that this call is not thread safe so would need to be synchronized in multi-threaded environment
Logger.SetLogWriter(NullLogWriter, false);
}
static bool HasLogWriterBeenDisposed()
{
try
{
// If logger is the NullLogWriter then it needs to be set
return Logger.Writer is NullLogWriter;
}
catch (InvalidOperationException e)
{
// If InvalidOperationException is thrown then logger is not set -- consider this to be disposed
return true;
}
}
static void InitializeLogger()
{
IConfigurationSource configurationSource = ConfigurationSourceFactory.Create();
LogWriterFactory logWriterFactory = new LogWriterFactory(configurationSource);
Logger.SetLogWriter(logWriterFactory.Create(), false);
}
}
它有一个额外的类,但不会在每次记录调用时抛出异常。
选项3可以直接访问私有writer
成员变量,因此可以检查它是否为null并返回true / false。
这种方法可能看起来与选项2类似,但是使用反射检查而不是对NullLogWriter进行检查。请注意,如果在中等信任下运行,则反射调用将失败。
Log("Test 1", "General");
Log("Test 2", "General");
static bool HasLogWriterBeenDisposed()
{
var fieldInfo = typeof(Logger).GetField("writer", BindingFlags.NonPublic | BindingFlags.Static);
return fieldInfo?.GetValue(null) == null;
}
static void Log(string message, string category)
{
if (HasLogWriterBeenDisposed())
{
InitializeLogger();
}
// Write message
Logger.Write(message, category);
// Dispose and set the logger to the null
Logger.Reset();
}
static void InitializeLogger()
{
IConfigurationSource configurationSource = ConfigurationSourceFactory.Create();
LogWriterFactory logWriterFactory = new LogWriterFactory(configurationSource);
Logger.SetLogWriter(logWriterFactory.Create(), false);
}
IMO,这些方法都不理想。真正的问题是原始(非推荐)设计迫使我们走这条道路之一。这些方法也不是线程安全的。如果我必须选择一种方法,我可能会使用选项2(NullLogWriter),因为它不依赖于代码重复,在每次调用时捕获异常,或者使用反射来访问私有成员变量,所有这些都是代码味道在某种程度上。
如果您想要的是一个不保持文件打开的线程安全实现,那么您可以坚持使用您正在进行的open / dispose方法(尽管效率低下),避免使用Enterprise Library Logger
static外观,而是使用您自己的静态类/单例,它可以完全按照您的需要处理逻辑。这样的事情应该有效:
public static class CustomLogger
{
private static readonly LogWriterFactory LogWriterFactory = new LogWriterFactory(ConfigurationSourceFactory.Create());
private static readonly object Locker = new object();
public static void Write(string message, string category)
{
lock (Locker)
{
using (var logWriter = LogWriterFactory.Create())
{
logWriter.Write(message, category);
}
}
}
}
CustomLogger.Write("Test 1", "General");
CustomLogger.Write("Test 2", "General");