8

我有一个 TraceSource 对象,用于记录 VB.Net 应用程序的初始化。它附加了几个 TraceListener:

  • 控制台跟踪监听器
  • TextWriterTraceListener
  • 事件日志跟踪监听器

对于前两个,我希望条目输出是“原始的”——也就是说,没有标准标题:

SourceName TraceEventType: Id :

我已经实现了一个包装器,它在 TraceEventType 设置为 Verbose 时执行此操作:

If _buffer.EventType = TraceEventType.Verbose Then
    For Each listener As TraceListener In _traceSource.Listeners
        listener.Write(_buffer.Text)
    Next
Else
    _traceSource.TraceEvent(_buffer.EventType, id, _buffer.Text)
End If

我可以对所有跟踪执行此操作,但是 EventLog 中的所有条目都将使用 Level = Information 列出。所以我希望能够指定跟踪消息的严重性,但我在 TraceSource 或 TraceListeners 上找不到任何允许我这样做的方法。据我所知, TraceListener 具有以下写入选项:

  • 写()
  • 写线()
  • 跟踪数据()
  • 跟踪事件()
  • 跟踪传输()

最后 3 个允许提供 TraceEventType (它正确地标记了 EventLog 条目,但是到控制台和日志文件的结果输出然后包含前缀并最终像这样(例如):

Bootstrapper Warning: 0 : Failed to validate assembly

有没有办法覆盖 ConsoleTraceListener 和 TextWriterTraceListener 如何格式化其输出以不包含此标头,同时能够使用 TraceEventType (用于 EventLog)标记条目?

这是迄今为止我想出的最好的:

For Each listener As TraceListener In _traceSource.Listeners
    If listener.GetType Is GetType(ConsoleTraceListener) OrElse listener.GetType Is GetType(TextWriterTraceListener) Then
        listener.Write(_buffer.Text)
    Else
        listener.TraceEvent(Nothing, _traceSource.Name, _buffer.EventType, id, _buffer.Text)
    End If
Next

这似乎可行,但在 Microsoft 的TraceListener.TraceEvent 方法的文档中,它说:

Important: This method is not intended to be called directly by application code but by members of the Debug, Trace, and TraceSource classes to write trace data to output.

..所以我不确定这是否是一件好事?

编辑:

我刚刚意识到,如果我在这里执行上一个示例的操作,则根本不需要 TraceSource,因为无论如何它都被绕过了。但这也意味着我必须实现自己的过滤和切换机制(但这可能是一个可以让它以我想要的方式工作的代价)。

4

3 回答 3

2

另一个具有可格式化侦听器的类似项目是Essential Diagnostics,它实际上最初是受 Ukadc.Diagnostics 启发的。

但是,您已经表明您不想要外部依赖项,但是您仍然有几个选择,而无需重写框架的某些部分:

(A) .NET Framework 中设计的扩展点不是重写 TraceSource,而是编写您自己的 TraceListener。

如果您编写自己的跟踪侦听器“ConsoleWithoutPrefixListener”和“FileWithoutPrefixListener”,那么您可以覆盖 TraceEvent() 方法以将消息转发到 TraceWrite()(并删除前缀)。

事实上,ConsoleTraceListener 或 TextWriterTraceListener 都不是密封的,所以我认为您可以从它们继承并使用 TraceEvent() 方法(加上构造函数)的一行覆盖来实现这一点。

(B) 另一种选择是让 EventLogTraceListener 针对源进行配置,但在(而不是跟踪源)下配置其他两个侦听器。

这样做的缺点是,在您的代码中,您每次都需要记录两次,例如:

_traceSource.TraceEvent(_buffer.EventType, id, _buffer.Text) Trace.TraceWrite(_buffer.Text)

如果您想编写一些带前缀的消息而一些不带前缀的消息,那么您将需要两个跟踪源:一个配置了所有三个侦听器,一个配置了事件日志侦听器。

然后,在您的包装器中写入源 A(全部三个)或源 B + Trace 静态方法。

(C) 就个人而言,我的指导是不要使用跟踪来写入事件日志——如果问题足够重要以写入事件日志,您通常不希望用户能够通过配置关闭它们。

在这种情况下,您的包装器直接写入事件日志(EventLog.WriteEntry 或其他),然后您的代码写入文件和控制台的跟踪源和/或跟踪静态方法。

请注意,要正确写入事件日志,您需要考虑权限。要创建事件日志源,您需要以管理员身份运行。作为开发人员,您通常可能拥有管理员权限,因此您需要在没有管理员权限的人的上下文中正确测试这一点。

另请注意,只有初始创建需要管理员权限,并且在您编写第一条消息时会自动完成,因此如果您已经作为开发人员管理员完成了它,您需要找到一台干净的机器进行测试。

因此,通常您需要将 EventLogInstaller 作为代码的一部分,由 InstallUtil(或等效的 MSI 或其他)运行,在安装期间创建事件日志源(因为安装管理员完成)。然后,当程序运行时,源就存在了。

那么,这与写入跟踪有什么关系——好吧,如果你唯一要做的就是在配置中配置 EventLogTraceListener,那么对于普通用户来说,它就不起作用了;它将尝试将事件写入源(在 initializeData 属性中),然后尝试创建源,如果不以管理员身份运行将失败。

如果您确实为事件源添加了安装程序,那么如果有人更改了配置文件,您仍然会遇到问题。

因此,我建议直接在代码中创建 EventLogInstaller 和 EventLog,以确保名称匹配,而不是通过跟踪基础结构。

于 2013-04-21T07:06:07.777 回答
1

这是我的完整解决方案,灵感来自@Sly的回答。

在使用可以从或(或任何需要的侦听器风格)TraceEvent()继承的方法时抑制标题信息,如下所示;ConsoleTraceListenerTextWriterTraceListener

namespace Custom.Lib {
    public class ConsoleTraceListener : System.Diagnostics.ConsoleTraceListener {

        // overridding this method here will suppress header information
        // your TraceEvent() messages will still reach the listener
        public override void Write(string message) {
            //base.Write(message);
        }

    }
}

注意当尝试覆盖该 TraceEvent 方法时,我注意到此时标题信息尚未添加到消息字符串中。相反,我选择 Write(string) 关闭似乎没有任何其他连锁反应的呼叫,但它确实感觉有点“骇人听闻”,如果有人有“更清洁的方法”,我愿意接受。

使用此客户侦听器的配置应如下所示;

  <system.diagnostics>
    <sources>
      <source name="AppTrace" switchName="sourceSwitch" switchType="System.Diagnostics.SourceSwitch">
        <listeners>
          <add name="consoleListener"/>
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="sourceSwitch" value="Information"/>
    </switches>
    <sharedListeners>
      <add name="consoleListener" type="Custom.Lib.ConsoleTraceListener, Custom.Lib" initializeData=""/>
    </sharedListeners>
  </system.diagnostics>
于 2015-01-14T13:43:45.463 回答
0

查看codeplex上的Ukadc.Diagnostics项目。它是 System.Diagnostics 的一个插件,使您能够根据需要格式化日志/跟踪输出(类似于您可以使用 log4net 和 NLog 执行的操作)。您通过配置使用它,因此您的代码不会直接依赖于库。该库带有用于格式化的可配置对象和利用格式化所需的自定义 TraceListener。该库还使您可以轻松编写自己的格式化“令牌”和您自己的 TraceListener。

例如,您可以将 Ukadc.Diagnostics ConsoleTraceListener 配置为使用如下格式语句:

{DateTime} {Source} {EventType} {Message}

记录的每条消息都会导致日期/时间、源名称、事件类型和消息。

试试看,我想你会喜欢的。我自己也使用了一些(主要用于原型设计,还没有用于“真正的”产品)并且取得了很好的成功。

请注意,对于某些标记(如 DateTime),您还可以应用适合该类型的标准格式(例如,对于 DateTime,您可以指定写入日期/时间的格式)。

Ukadc.Diagnostics 附带的文件跟踪侦听器还允许使用令牌系统指定其文件名。

于 2010-10-31T19:40:41.597 回答