在FileConfigurationSourceChanged上禁用日志记录-LogEnabledFilter [英] Disable logging on FileConfigurationSourceChanged - LogEnabledFilter

查看:148
本文介绍了在FileConfigurationSourceChanged上禁用日志记录-LogEnabledFilter的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望管理员通过在配置中更改LogEnabledFilter的enabled属性来在运行时启用/禁用日志记录.

SO上有多个线程可以解释解决方法,但是我希望这样. 我试图这样更改启用日志记录的筛选器":

private static void FileConfigurationSourceChanged(object sender, ConfigurationSourceChangedEventArgs e)
{
    var fcs = sender as FileConfigurationSource;

    System.Diagnostics.Debug.WriteLine("----------- FileConfigurationSourceChanged called --------");

    LoggingSettings currentLogSettings = e.ConfigurationSource.GetSection("loggingConfiguration") as LoggingSettings;
    var fdtl = currentLogSettings.TraceListeners.Where(tld => tld is FormattedDatabaseTraceListenerData).FirstOrDefault();
    var currentLogFileFilter = currentLogSettings.LogFilters.Where(lfd => { return lfd.Name == "Logging Enabled Filter"; }).FirstOrDefault();
    var filterNewValue = (bool)currentLogFileFilter.ElementInformation.Properties["enabled"].Value;

    var runtimeFilter = Logger.Writer.GetFilter<LogEnabledFilter>("Logging Enabled Filter");
    runtimeFilter.Enabled = filterNewValue;

   var test =  Logger.Writer.IsLoggingEnabled();

}

但是测试总是显示最初加载的配置值,它不会改变. 我认为,当更改配置中的值时,更改将自动传播到运行时配置.但是事实并非如此! 如上面的代码所示,以编程方式进行设置也不起作用.

是时候重建或关闭企业库了.

解决方案

您是对的,您发布的代码无效.该代码使用配置文件(FileConfigurationSource)作为配置企业库的方法.

让我们深入研究一下,看看程序化配置是否可以工作.

我们将使用Fluent API,因为它是编程配置的首选方法:

var builder = new ConfigurationSourceBuilder();

builder.ConfigureLogging()
    .WithOptions
    .DoNotRevertImpersonation()
    .FilterEnableOrDisable("EnableOrDisable").Enable()
    .LogToCategoryNamed("General")
    .WithOptions.SetAsDefaultCategory()
    .SendTo.FlatFile("FlatFile")
    .ToFile(@"fluent.log");

var configSource = new DictionaryConfigurationSource();
builder.UpdateConfigurationWithReplace(configSource);

var defaultWriter = new LogWriterFactory(configSource).Create();
defaultWriter.Write("Test1", "General");

var filter = defaultWriter.GetFilter<LogEnabledFilter>();
filter.Enabled = false;

defaultWriter.Write("Test2", "General");

如果您尝试使用此代码,则过滤器将不会更新-因此会出现另一个故障.

让我们尝试通过直接使用类来使用老派"编程配置:

var flatFileTraceListener = new FlatFileTraceListener(
    @"program.log", 
    "----------------------------------------", 
    "----------------------------------------"
    );

LogEnabledFilter enabledFilter = new LogEnabledFilter("Logging Enabled Filter", true);
// Build Configuration
var config = new LoggingConfiguration();

config.AddLogSource("General", SourceLevels.All, true)
    .AddTraceListener(flatFileTraceListener);

config.Filters.Add(enabledFilter);

LogWriter defaultWriter = new LogWriter(config);

defaultWriter.Write("Test1", "General");

var filter = defaultWriter.GetFilter<LogEnabledFilter>();
filter.Enabled = false;

defaultWriter.Write("Test2", "General");

成功!未记录第二条("Test2")消息.

那么,这是怎么回事?如果我们自己实例化过滤器并将其添加到配置中,则它可以工作,但是当依赖企业库配置时,不会更新过滤器值.

这导致一个假设:使用企业库配置时,每次都会返回新的过滤器实例,这就是为什么更改值对企业库所使用的内部实例没有影响的原因.

如果我们深入研究企业库代码,我们(最终)会点击 LoggingSettings 类和BuildLogWriter方法.这用于创建LogWriter.这是创建过滤器的地方:

var filters = this.LogFilters.Select(tfd => tfd.BuildFilter());

因此,此行使用已配置的LogFilterData并调用BuildFilter方法来实例化适用的过滤器.在这种情况下,配置类LogEnabledFilterData BuildFilter方法的BuildFilter方法返回LogEnabledFilter的实例:

return new LogEnabledFilter(this.Name, this.Enabled);

此代码的问题是this.LogFilters.Select返回创建了LogFilters的惰性求值枚举,并且此枚举传递到LogWriter中以用于所有过滤器操作.被引用时,将对枚举进行求值并创建一个新的Filter实例!这证实了最初的假设.

为了明确起见:每次调用LogWriter.Write()时,都会根据原始配置创建一个新的LogEnabledFilter.通过调用GetFilter()查询过滤器时,将基于原始配置创建新的LogEnabledFilter.由GetFilter()返回的对象的任何更改都不会影响内部配置,因为它是一个新的对象实例,并且无论如何,企业库在内部无论如何都会在下一个Write()调用上创建另一个新实例.

首先,这完全是错误的,但是每次调用Write()都可以多次调用创建新对象的效率也很低.

此问题的一个简单解决方案是通过调用ToList()来评估LogFilters枚举:

var filters = this.LogFilters.Select(tfd => tfd.BuildFilter()).ToList();

这仅评估一次枚举,确保仅创建一个过滤器实例.然后,问题中发布的GetFilter()和更新过滤器值方法将起作用.

I want Administrators to enable/disable logging at runtime by changing the enabled property of the LogEnabledFilter in the config.

There are several threads on SO that explain workarounds, but I want it this way. I tried to change the Logging Enabled Filter like this:

private static void FileConfigurationSourceChanged(object sender, ConfigurationSourceChangedEventArgs e)
{
    var fcs = sender as FileConfigurationSource;

    System.Diagnostics.Debug.WriteLine("----------- FileConfigurationSourceChanged called --------");

    LoggingSettings currentLogSettings = e.ConfigurationSource.GetSection("loggingConfiguration") as LoggingSettings;
    var fdtl = currentLogSettings.TraceListeners.Where(tld => tld is FormattedDatabaseTraceListenerData).FirstOrDefault();
    var currentLogFileFilter = currentLogSettings.LogFilters.Where(lfd => { return lfd.Name == "Logging Enabled Filter"; }).FirstOrDefault();
    var filterNewValue = (bool)currentLogFileFilter.ElementInformation.Properties["enabled"].Value;

    var runtimeFilter = Logger.Writer.GetFilter<LogEnabledFilter>("Logging Enabled Filter");
    runtimeFilter.Enabled = filterNewValue;

   var test =  Logger.Writer.IsLoggingEnabled();

}

But test reveals always the initially loaded config value, it does not change. I thought, that when changing the value in the config the changes will be propagated automatically to the runtime configuration. But this isn't the case! Setting it programmatically as shown in the code above, doesn't work either.

It's time to rebuild Enterprise Library or shut it down.

解决方案

You are right that the code you posted does not work. That code is using a config file (FileConfigurationSource) as the method to configure Enterprise Library.

Let's dig a bit deeper and see if programmatic configuration will work.

We will use the Fluent API since it is the preferred method for programmatic configuration:

var builder = new ConfigurationSourceBuilder();

builder.ConfigureLogging()
    .WithOptions
    .DoNotRevertImpersonation()
    .FilterEnableOrDisable("EnableOrDisable").Enable()
    .LogToCategoryNamed("General")
    .WithOptions.SetAsDefaultCategory()
    .SendTo.FlatFile("FlatFile")
    .ToFile(@"fluent.log");

var configSource = new DictionaryConfigurationSource();
builder.UpdateConfigurationWithReplace(configSource);

var defaultWriter = new LogWriterFactory(configSource).Create();
defaultWriter.Write("Test1", "General");

var filter = defaultWriter.GetFilter<LogEnabledFilter>();
filter.Enabled = false;

defaultWriter.Write("Test2", "General");

If you try this code the filter will not be updated -- so another failure.

Let's try to use the "old school" programmatic configuration by using the classes directly:

var flatFileTraceListener = new FlatFileTraceListener(
    @"program.log", 
    "----------------------------------------", 
    "----------------------------------------"
    );

LogEnabledFilter enabledFilter = new LogEnabledFilter("Logging Enabled Filter", true);
// Build Configuration
var config = new LoggingConfiguration();

config.AddLogSource("General", SourceLevels.All, true)
    .AddTraceListener(flatFileTraceListener);

config.Filters.Add(enabledFilter);

LogWriter defaultWriter = new LogWriter(config);

defaultWriter.Write("Test1", "General");

var filter = defaultWriter.GetFilter<LogEnabledFilter>();
filter.Enabled = false;

defaultWriter.Write("Test2", "General");

Success! The second ("Test2") message was not logged.

So, what is going on here? If we instantiate the filter ourselves and add it to the configuration it works but when relying on the Enterprise Library configuration the filter value is not updated.

This leads to a hypothesis: when using Enterprise Library configuration new filter instances are being returned each time which is why changing the value has no effect on the internal instance being used by Enterprise Library.

If we dig into the Enterprise Library code we (eventually) hit on LoggingSettings class and the BuildLogWriter method. This is used to create the LogWriter. Here's where the filters are created:

var filters = this.LogFilters.Select(tfd => tfd.BuildFilter());

So this line is using the configured LogFilterData and calling the BuildFilter method to instantiate the applicable filter. In this case the BuildFilter method of the configuration class LogEnabledFilterData BuildFilter method returns an instance of the LogEnabledFilter:

return new LogEnabledFilter(this.Name, this.Enabled);

The issue with this code is that this.LogFilters.Select returns a lazy evaluated enumeration that creates LogFilters and this enumeration is passed into the LogWriter to be used for all filter manipulation. Every time the filters are referenced the enumeration is evaluated and a new Filter instance is created! This confirms the original hypothesis.

To make it explicit: every time LogWriter.Write() is called a new LogEnabledFilter is created based on the original configuration. When the filters are queried by calling GetFilter() a new LogEnabledFilter is created based on the original configuration. Any changes to the object returned by GetFilter() have no affect on the internal configuration since it's a new object instance and, anyway, internally Enterprise Library will create another new instance on the next Write() call anyway.

Firstly, this is just plain wrong but it is also inefficient to create new objects on every call to Write() which could be invoked many times..

An easy fix for this issue is to evaluate the LogFilters enumeration by calling ToList():

var filters = this.LogFilters.Select(tfd => tfd.BuildFilter()).ToList();

This evaluates the enumeration only once ensuring that only one filter instance is created. Then the GetFilter() and update filter value approach posted in the question will work.

这篇关于在FileConfigurationSourceChanged上禁用日志记录-LogEnabledFilter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆