Question

How do I dynamically reload the app.config in a .net Windows application? I need to turn logging on and off dynamically and not just based upon the value at application start.

ConfigurationManager.RefreshSection("appSettings") does not work and I've also tried explicitly opening the config file using OpenExeConfiguration but I always get the cached value at application startup and not the current value.

I've accepted the answer of creating a custom configuration section. As a side note and foolish mistake - if you're running from the IDE there's no point in updating the app.config file and expecting changes. Yuo have to modify the .exe.config file in the bin\debug folder. Doh!

Was it helpful?

Solution

You can refresh your own section the way you say:

ConfigurationManager.RefreshSection("yoursection/subsection");

Just move a logging true/false into a section and you'll be fine.

OTHER TIPS

If you are using log4Net, you can do what you asked:

Although it is possible to add your log4net configuration settings to your project’s app.config or web.config file, it is preferable to place them in a separate configuration file. Aside from the obvious benefit of maintainability, it has the added benefit that log4net can place a FileSystemWatcher object on your config file to monitor when it changes and update its settings dynamically.

To use a separate config file, add a file named Log4Net.config to your project and add the following attribute to your AssemblyInfo.cs file:

[assembly: log4net.Config.XmlConfigurator(ConfigFile="Log4Net.config", Watch = true)]

Note: for web applications, this assumes Log4Net.config resides in the web root. Ensure the log4net.config file is marked as “Copy To Output” -> “Copy Always” in Properties.

Following is the hack which you can put this will make config to read from disk.

You just need to save the config file in Modified Mode and then refresh this will make application to read the file agian from the disk.

ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).Save(ConfigurationSaveMode.Modified);
            ConfigurationManager.RefreshSection("appSettings");

Just a note, in WinForms, you can make programmatic changes to your app.config before your application loads (before Application.Start(new Form1())), as long as you use System.Xml instead of System.Configuration.ConfigurationManager

string configFile = Application.ExecutablePath + ".config";  //c:\path\exename.exe.config
XmlDocument xdoc = new XmlDocument();
xdoc.Load(configFile);
XmlNode node = xdoc.SelectSingleNode("/configuration/appSettings/add[@key='nodeToChange']/@value");
node.Value = "new value";
File.WriteAllText(setFile, xdoc.InnerXml);

I wrote this implementation to change the log level at runtime and persist the new treshold back to the app.config (actually the Application.exe.config).

The interface:

internal interface ILoggingConfiguration
{
  void SetLogLevel(string level);

  string GetLogLevel();
}

The implementation:

internal sealed class LoggingConfigurationImpl : ILoggingConfiguration
{
  #region Members

  private static readonly ILog _logger = 
    ObjectManager.Common.Logger.GetLogger();
  private const string DEFAULT_NAME_SPACE = "Default.Name.Space";

  #endregion

  #region Implementation of ILoggingConfiguration

  public void SetLogLevel(string level)
  {
    Level threshold = Log4NetUtils.ConvertToLevel(level);
    ILoggerRepository[] repositories = LogManager.GetAllRepositories();

    foreach (ILoggerRepository repository in repositories)
    {
      try
      {
        SetLogLevelOnRepository(repository, threshold);
      }
      catch (Exception ex)
      {
        _logger.ErrorFormat("Exception while changing log-level: {0}", ex);
      }
    }
    PersistLogLevel(level);
  }

  public string GetLogLevel()
  {
    ILoggerRepository repository = LogManager.GetRepository();
    Hierarchy hierarchy = (Hierarchy) repository;
    ILogger logger = hierarchy.GetLogger(DEFAULT_NAME_SPACE);
    return ((Logger) logger).Level.DisplayName;
  }

  private void SetLogLevelOnRepository(ILoggerRepository repository,
                                       Level threshold)
  {
    repository.Threshold = threshold;
    Hierarchy hierarchy = (Hierarchy)repository;
    ILogger[] loggers = hierarchy.GetCurrentLoggers();
    foreach (ILogger logger in loggers)
    {
      try
      {
        SetLogLevelOnLogger(threshold, logger);
      }
      catch (Exception ex)
      {
        _logger.ErrorFormat("Exception while changing log-level for 
            logger: {0}{1}{2}", logger, Environment.NewLine, ex);
      }
    }
  }

  private void SetLogLevelOnLogger(Level threshold, ILogger logger)
  {
    ((Logger)logger).Level = threshold;
  }

  private void PersistLogLevel(string level)
  {
    XmlDocument config = new XmlDocument();
    config.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
    string xpath =
      String.Format("configuration/log4net/logger[@name='{0}']/level",
        DEFAULT_NAME_SPACE);
    XmlNode rootLoggerNode = config.SelectSingleNode(xpath);

    try
    {
      rootLoggerNode.Attributes["value"].Value = level;
      config.Save(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);

      ConfigurationManager.RefreshSection("log4net");
    }
    catch(Exception ex)
    {
      _logger.ErrorFormat("error while persisting new log-level: {0}", ex);
    }
  }

  #endregion
}

The helper class Log4NetUtils:

public sealed class Log4NetUtils
{
  private static readonly ILoggerRepository _loggerRepository =
     LoggerManager.GetAllRepositories().First();

  public static Level ConvertToLevel(string level)
  {
    return _loggerRepository.LevelMap[level];
  }
}

The XAML code:

<ComboBox Name="cbxLogLevel" Text="{Binding LogLevel}">
  <ComboBoxItem Content="DEBUG" />
  <ComboBoxItem Content="INFO" />
  <ComboBoxItem Content="WARN" />
  <ComboBoxItem Content="ERROR" />
</ComboBox>
<Button Name="btnChangeLogLevel" 
        Command="{Binding SetLogLevelCommand}"
        CommandParameter="{Binding ElementName=cbxLogLevel, Path=Text}" >
            Change log level
</Button>

I don't think there's any way to do this, unless you write your own config file reader using XML. Why not just turn logging on or off at the start of your app based on the config file setting, and then just turn it on or off dynamically while the program is running?

I think I read in the log4net documentation that this is not possible.

Try having the logging in an external logfile that can be watched with a filesystemwatcher

Update: Found it again.. http://logging.apache.org/log4net/release/manual/configuration.html#.config%20Files

There is no way to reload the app.config during runtime.

I would recommend using another XML file and not app.config. You could even watch the file for changes and automatically reload it when it changes.

Actually using an:

Application.restart();

Has worked pretty good for me.

Regards

Jorge

I have tried using the RefreshSection method and got it to work using the following code sample:

class Program
    {
        static void Main(string[] args)
        {
            string value = string.Empty, key = "mySetting";
            Program program = new Program();

            program.GetValue(program, key);
            Console.WriteLine("--------------------------------------------------------------");
            Console.WriteLine("Press any key to exit...");
            Console.ReadLine();
        }

        /// <summary>
        /// Gets the value of the specified key from app.config file.
        /// </summary>
        /// <param name="program">The instance of the program.</param>
        /// <param name="key">The key.</param>
        private void GetValue(Program program, string key)
        {
            string value;
            if (ConfigurationManager.AppSettings.AllKeys.Contains(key))
            {
                Console.WriteLine("--------------------------------------------------------------");
                Console.WriteLine("Key found, evaluating value...");
                value = ConfigurationManager.AppSettings[key];
                Console.WriteLine("Value read from app.confg for Key = {0} is {1}", key, value);
                Console.WriteLine("--------------------------------------------------------------");

                //// Update the value
                program.UpdateAppSettings(key, "newValue");
                //// Re-read from config file
                value = ConfigurationManager.AppSettings[key];
                Console.WriteLine("New Value read from app.confg for Key = {0} is {1}", key, value);
            }
            else
            {
                Console.WriteLine("Specified key not found in app.config");
            }
        }

        /// <summary>
        /// Updates the app settings.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        public void UpdateAppSettings(string key, string value)
        {
            Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

            if (configuration.AppSettings.Settings.AllKeys.Contains(key))
            {
                configuration.AppSettings.Settings[key].Value = value;
            }

            configuration.Save(ConfigurationSaveMode.Modified);
            ConfigurationManager.RefreshSection("appSettings");
        }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top