Отключить ведение журнала на FileConfigurationSourceChanged - LogEnabledFilter

Я хочу, чтобы администраторы включали / отключали ведение журнала во время выполнения, изменив свойство enabled для LogEnabledFilter в config.

В 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();

}

Но тест всегда показывает изначально загруженное значение конфигурации, оно не меняется. Я думал, что при изменении значения в конфиге изменения будут автоматически распространяться на конфигурацию времени выполнения. Но это не так! Программная установка, как показано в приведенном выше коде, тоже не работает.

Пришло время перестроить Enterprise Library или выключить ее.


person Legends    schedule 16.09.2016    source источник


Ответы (2)


Вы правы, что опубликованный вами код не работает. Этот код использует файл конфигурации (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») сообщение не было зарегистрировано.

И так, что здесь происходит? Если мы создаем экземпляр фильтра сами и добавляем его в конфигурацию, он работает, но при использовании конфигурации Enterprise Library значение фильтра не обновляется.

Это приводит к гипотезе: при использовании конфигурации Enterprise Library новые экземпляры фильтров возвращаются каждый раз, поэтому изменение значения не влияет на внутренний экземпляр, используемый Enterprise Library.

Если мы углубимся в код корпоративной библиотеки, мы (в конце концов) найдем LoggingSettings и метод BuildLogWriter. Это используется для создания LogWriter. Вот где создаются фильтры:

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

Таким образом, эта строка использует настроенный LogFilterData и вызывает метод BuildFilter для создания экземпляра применимого фильтра. В этом случае метод BuildFilter класса конфигурации LogEnabledFilterData BuildFilter метод возвращает экземпляр LogEnabledFilter:

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

Проблема с этим кодом заключается в том, что this.LogFilters.Select возвращает ленивое вычисленное перечисление, которое создает LogFilters, и это перечисление передается в LogWriter, чтобы использовать его для всех манипуляций с фильтрами. Каждый раз, когда на фильтры ссылаются, перечисление оценивается и создан новый экземпляр фильтра! Это подтверждает исходную гипотезу.

Чтобы сделать это явным: каждый раз, когда вызывается LogWriter.Write (), создается новый LogEnabledFilter на основе исходной конфигурации. Когда фильтры запрашиваются вызовом GetFilter(), создается новый LogEnabledFilter на основе исходной конфигурации. Любые изменения объекта, возвращаемого GetFilter(), не влияют на внутреннюю конфигурацию, поскольку это новый экземпляр объекта, и, в любом случае, внутренняя Enterprise Library все равно создаст другой новый экземпляр при следующем Write() вызове.

Во-первых, это просто неправильно, но также неэффективно создавать новые объекты при каждом вызове Write(), который может быть вызван много раз ..

Простое решение этой проблемы - оценить перечисление LogFilters, вызвав ToList():

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

Это оценивает перечисление только один раз, гарантируя, что будет создан только один экземпляр фильтра. Тогда подход GetFilter() и обновления значения фильтра, указанный в вопросе, будет работать.

person Randy supports Monica    schedule 17.09.2016
comment
Рэнди, спасибо за подробное объяснение! Я загрузил последний исходный код для EL6 и внедрил это исправление, но я не могу собрать исходный код с помощью BuildLibrary.bat в моей системе. Не могли бы вы взглянуть на этот вопрос < / а> - person Legends; 17.09.2016
comment
Готово. Я заставил его работать, используя ваше исправление. Большое спасибо! - person Legends; 17.09.2016
comment
К вашему сведению, после исправления я повторно запустил тесты Unit и BVT и не увидел никаких поломок из-за изменения. - person Randy supports Monica; 18.09.2016

Обновление:

Рэнди Леви предоставил исправление в своем ответе выше. Внедрите исправление и перекомпилируйте корпоративную библиотеку.

Вот ответ Рэнди Леви:

Да, вы можете отключить ведение журнала, установив параметр LogEnabledFiter. Основной способ сделать это - вручную отредактировать файл конфигурации - это основное предназначение этой функции (руководство разработчиков ссылается на администраторов, настраивающих этот параметр). Другие аналогичные подходы к настройке фильтра - это программное изменение исходной файловой конфигурации (которая по сути является реконфигурацией блока) или программная реконфигурация блока (например, с использованием свободного интерфейса). Ни один из программных подходов я бы не назвал простыми - Рэнди Леви 39 минут назад


Если вы попытаетесь получить фильтр и отключить его, я не думаю, что это повлияет на вас без перенастройки. Таким образом, следующий код все еще завершает запись в журнал: var enabledFilter = logWriter.GetFilter (); enabledFilter.Enabled = false; logWriter.Write («ТЕСТ»); Один подход, не связанный с EntLib, - это просто управлять включением / отключением самостоятельно с помощью свойства bool и вспомогательного класса. Но я думаю, что приоритетный подход - это довольно простая альтернатива.

Вывод:

В вашем пользовательском классе Logger реализуйте свойство IsLoggenabled и измените / проверьте его во время выполнения.

Это не сработает:

var runtimeFilter = Logger.Writer.GetFilter<LogEnabledFilter>("Logging Enabled Filter");
runtimeFilter.Enabled = false/true;
person Legends    schedule 16.09.2016