在运行时修改app.config文件中的<system.diagnostics>部分

4
我需要在运行时修改app.config中的<configuration><system.diagnostics> 部分,以便我可以:
  1. <sharedListeners> 元素下添加CustomTraceListener,这需要特殊的initializeData,只能在运行时确定。

  2. CustomTraceListener共享侦听器添加到现有源的<source><listeners>元素下。

  3. CustomTraceListener持久化到其他装载其跟踪源和侦听器配置的程序集中的配置文件中。

app.config中相关部分目前看起来是这样的:
<system.diagnostics>
  <sources>
    <source name="mysource" switchName="..." switchType="...">
      <listeners>
        <add name="console" />
        <add name="customtracelistener" /> /// need to add new listener here
      </listeners>
    </source>
  </sources>
  <sharedListeners>  
    <add name="console" type="..." initializeData="..." />
    <add name="customtracelistener" type="..." initializeData="..."> /// need to add custom trace listener here 
      <filter type="..." initializeData="Warning"/> /// with a filter
    </add>
  </sharedListeners>
  <switches>
    <add name="..." value="..." />
  </switches>
</system.diagnostics>

使用 ConfigurationManager 我可以轻松地执行以下操作:

Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection diagnostics = config.GetSection("system.diagnostics");

当我这样做时,diagnostics 是一个 System.Diagnostics.SystemDiagnosticsSection 类型。有趣的是,我无法将 diagnostics 强制转换为 SystemDiagnosticsSection 类型,因为我找不到它在哪个命名空间中。无论如何,ConfigurationSection 似乎没有任何方法可用于将数据写入该部分。
我也无法将其强制转换为 NameValueConfigurationCollection,因为 diagnostics 基类型是 ConfigurationSection。我听说过这种技术,但似乎我无法使用它。
我必须回归使用普通的 XML 来完成这个任务吗?我真的不想重新发明轮子。
3个回答

5
您可以通过 ConfigurationManager 找到 app.exe.config 文件的路径,然后将该配置文件作为 XDocument 加载。
string configPath = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;

XDocument config = XDocument.Load(configPath);
XElement diagnostics = config.Descendants().FirstOrDefault(e => e.Name == "system.diagnostics");

if (diagnostics == default(XElement))
{
    /// <system.diagnostics> element was not found in config
}
else
{
    /// make changes within <system.diagnostics> here...
}

config.Save(configPath);

Trace.Refresh();  /// reload the trace configuration

在所需更改完成后,保存XDocument回磁盘,并调用Trace.Refresh()重新加载跟踪配置。

请参阅此处有关Trace.Refresh方法的MSDN文档


如何在WCF服务中实现?config.Save重启IIS站点? - Kiquenet

2

就我的经验而言,如果应用程序在受保护的目录下部署,例如在启用了UAC的MS操作系统中的Program Files中,我建议您不要从应用程序中进行app.config更改。

有时,要更新配置文件,您需要一些管理员权限。

坏处是,在Visual Studio / Debug或某些测试过程中,所有内容都可以正常运行,但在生产环境中部署后,您可能会遇到一些问题...


0
如果您在运行时直接更改app.config文件中的<configuration><system.diagnostics>部分,则需要重新启动应用程序或调用Trace.Refresh()以使更改生效。
另一个选项是在应用程序启动时以编程方式添加TraceListeners,例如:Trace.Listeners.Add(new TextWriterTraceListener("output.log", "myListener")); 请参见https://msdn.microsoft.com/en-us/library/sk36c28t(v=vs.110).aspx
要添加具有与您问题中的initializeData值相同的过滤器,可以使用TraceListener.Filter属性。
为了在多个应用程序之间共享设置,您可以在<system.diagnostics>元素上使用configSource属性,并将该元素放置在单独的配置文件中。这样做的缺点是该文件需要与app.config位于同一文件夹中。因此,对一个文件的更改要么需要复制并粘贴到其他位置,要么以其他方式共享。
另一种选择是保存一个包含跟踪侦听器信息的自定义配置文件,该文件位于所有应用程序都可以访问的位置,然后在每个应用程序启动时加载该文件,并像上面那样配置跟踪侦听器。

更新

为了在整个应用程序中共享日志记录,您可以创建一个实现单例模式的类来返回您的TraceSource实例或包装您的日志记录活动。这样,您就不必传递相同的实例。

public class Logger
{
    private static Logger _logger;
        private TraceSource _ts;

        private Logger()
        {
        // Configure TraceSource here as required, e.g.
            _ts = new TraceSource("StackOverflow", SourceLevels.All);
            _ts.Listeners.Add(new TextWriterTraceListener(@"c:\temp\tracefile.log"));
        }

        public static Logger Get()
        {
            if (_logger == null)
            {
                _logger = new Logger();
            }

            return _logger;
        }

        public void TraceInfo(string message)
        {
            _ts.TraceInformation(message);
            _ts.Flush();
        }
}

// To use
Logger.Get().TraceInfo("This is a trace message");

你可以扩展这个功能,以便封装实际要记录的消息,这样做日志记录的代码就不需要知道具体细节,而且你只需要定义一个地方来管理你的事件,例如:
public void TraceApplicationStarting()
{
    _ts.TraceEvent(TraceEventType.Verbose, 1, "Application Starting");
}

嗨,David,感谢您的回复。但是,当您说无法将更改应用于正在运行的应用程序时,您是不正确的 - 您只需调用Trace.Refresh(),配置将重新加载。本周我已经能够将配置文件作为XDocument加载并进行所需更改,将app.exe.config文件保存回磁盘,并动态重新加载跟踪配置。不幸的是,我仍然没有找到通过ConfigurationManager直接执行此操作的方法。 - khargoosh
我认为您也误解了我的第三点 - 我不想在应用程序之间共享跟踪配置,只想在单个应用程序引用的程序集之间共享。通过这样做,您可以在dll中的类中实例化跟踪源,并按照app.exe.config中指定的方式加载应用程序的跟踪配置。如果您仅以编程方式添加新的TraceListener而不修改配置文件,则程序集对此没有任何可见性,除非您将TraceSource传递给每个类实例。 - khargoosh
你是如何编写跟踪消息的?通过编程方式添加新的跟踪侦听器适用于所有引用的程序集。我刚刚通过一个简单的控制台应用程序引用了一个独立项目,并从主应用程序和引用项目中调用了 Trace.TraceInformation,而且没有在 app.config 中进行任何配置,确认了这一点。检查引用程序集中的 Trace.TraceListeners 集合,可以看到我通过编程方式添加的侦听器。 - David
看起来你正在使用静态的Trace类,但我正在使用TraceSource类的实例 - 正如MSDN 这里所推荐的。使用TraceSource需要一个实例,因此我必须在每个需要跟踪的类中创建一个实例(从文件加载配置),或者将一个实例传递给每个类,但这不太实用。 - khargoosh
请参考答案更新,以了解通过代码库共享的方法。 - David
谢谢David,使用单例可能是一个选择。但是这个单例实现不是线程安全的。 - khargoosh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接