.Net:如何抑制 TraceSource 标头(“SourceName TraceEventType:Id:”)?

发布于 2024-09-29 13:57:36 字数 1968 浏览 7 评论 0原文

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

  • ConsoleTraceListener
  • TextWriterTraceListener
  • EventLogTraceListener

对于前两个,我希望条目输出为“原始” - 即没有标准标头:

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 有以下写入选项:

  • Write()
  • WriteLine()
  • TraceData()
  • TraceEvent()
  • TraceTransfer()

最后 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 方法,它说:

重要:此方法不打算由应用程序代码直接调用,而是由 Debug、Trace 和 TraceSource 类的成员调用,以将跟踪数据写入输出。

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

编辑:

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

I have a TraceSource object that I use to log the initialization of a VB.Net application. It has several TraceListeners attached:

  • ConsoleTraceListener
  • TextWriterTraceListener
  • EventLogTraceListener

For the first two I want the entry output to be "raw" - that is, without the standard header:

SourceName TraceEventType: Id :

I have implemented a wrapper that does this when the TraceEventType is set to 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

I could do this for all the tracing, but then all entries in the EventLog would be listed with Level = Information. So I want to be able to specify the severity of the trace message, but I can't find any method on the TraceSource or the TraceListeners that allows me to do this. As far as I can tell, TraceListener has these options for writing to it:

  • Write()
  • WriteLine()
  • TraceData()
  • TraceEvent()
  • TraceTransfer()

The last 3 allows for providing a TraceEventType (which correctly labels the EventLog entries, but the resulting output to the console and the log file then includes the prefixes and ends up like this (for example):

Bootstrapper Warning: 0 : Failed to validate assembly

Is there a way to override how the ConsoleTraceListener and TextWriterTraceListener format their output to not include this header, while at the same time being able to tag the entries with a TraceEventType (for the EventLog)?

This is the best I have come up with so far:

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

This seems to work, but in the documentation on the TraceListener.TraceEvent Method from Microsoft, it says:

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.

..so I'm not sure if it's a good thing to do?

Edit:

I just realized that if I do something like my last example here, I don't need the TraceSource at all, since it's being bypassed anyway. But it also means I have to implement my own filtering and switching mechanisms (but that is maybe an OK price to pay to get it to work the way I want).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情丝乱 2024-10-06 13:57:36

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

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

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

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

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

(二)
另一种替代方法是保留针对源配置的 EventLogTraceListener,但在(而不是跟踪源)下配置其他两个侦听器。

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

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

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

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

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

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

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

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

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

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

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

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

Another similar project that has formattable listeners that you could use is Essential Diagnostics, which was actually originally inspired by Ukadc.Diagnostics.

You have indicated, however, that you don't want external dependencies, but you still have several options without re-writing parts of the Framework:

(A)
Rather than rewrite TraceSource, the designed extension point in the .NET Framework is to write your own TraceListener.

If you write your own trace listeners "ConsoleWithoutPrefixListener" and "FileWithoutPrefixListener", then you can override the TraceEvent() methods to just forward the message to TraceWrite() (and drop the prefixes).

In fact neither ConsoleTraceListener or TextWriterTraceListener are sealed, so I think you could inherit from them and get this working with a one line override of the TraceEvent() method (plus the constructor).

(B)
Another alternative would be to leave EventLogTraceListener configurated against the source but configure the other two listeners under (rather than trace source).

The drawback of this is that in your code you need to log twice every time, e.g.:

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

If you want to write some messages with prefixes and some without then you will need two trace sources: one that is configured with all three listeners, and one with only the event log listener.

Then, in your wrapper either write to source A (all three) or source B + Trace static methods.

(C)
Personally, my guidance would be not to use tracing for writing to the event log -- if issues are important enough to write to the event log you generally don't want the user to be able to turn them off via configuration.

In this case your wrapper writes to the event log directly (EventLog.WriteEntry or whatever) and then your code writes to the trace source and/or Trace static methods for the file and console.

Note that to get writing to Event Log working correctly you need to take into account permissions. To create an event log source you need to be running as administrator. As a developer you probably normally have admin permissions, so you need to properly test this in the context of someone who doesn't.

Also note that it is only the initial creation that needs admin permissions, and this is automatically done when you write the first message, so if you have already done it as a developer admin you need to find a clean machine to test on.

Because of this, normally you need to have an EventLogInstaller as part of your code, which is run by InstallUtil (or equivalent MSI or whatever) that creates the event log source during install (because install is done by an administrator). Then, when the program runs the source exists.

So, what does this have to do with writing to traces -- well, if the only thing you do is configure the EventLogTraceListener in your config then for normal users it won't work; it will try and write events to the source (in the initializeData attribute), which will then try to create source and if not running as admin will fail.

If you do add an installer for the event source, then you still have a problem if someone changes the config file.

Because of this, I would recommend that both the EventLogInstaller and EventLog are created directly in code, to ensure the names match, and not go through the tracing infrastructure.

旧时光的容颜 2024-10-06 13:57:36

这是我的完整解决方案,灵感来自 @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);
        }

    }
}

NB 当尝试重写 TraceEvent 方法时,我注意到此时标头信息尚未添加到消息字符串中。相反,我选择静默对 Write(string) 的调用,这似乎没有任何其他连锁反应,但确实感觉有点“hackish”,如果任何人都有“更干净的方法”,我对此持开放态度。

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

  <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>

Here is my full solution to this, inspired by @Sly's answer.

To suppress the header information when using the TraceEvent() method you can inherit from ConsoleTraceListener or TextWriterTraceListener (or whatever flavour of listener is required) like so;

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);
        }

    }
}

N.B. When attempting to override the TraceEvent method I noticed that the header information had not been added to the message string at this point. Instead I opted to silence the call to Write(string) which doesn't seem to have any other knock-on effects but it does feel a little 'hackish', if anyone has a 'cleaner approach' I'm open to it.

The configuration to use this customer listener should then look something like this;

  <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>
南巷近海 2024-10-06 13:57:36

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

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

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

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

尝试一下,我想你会喜欢的。我自己也用过它(主要是用于原型设计,还没有用于“真正的”产品)并取得了良好的成功。

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

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

Look at the Ukadc.Diagnostics project on codeplex. It is an addon to System.Diagnostics that gives you the ability to format your logging/tracing output however you would like (similar to what you can do with log4net and NLog). You use it through configuration, so your code will not take a direct dependency on the library. The library comes with configurable objects for formatting and the custom TraceListeners required to take advantage of the formatting. The library also makes it easy for you to write your own formatting "tokens" and your own TraceListener.

For example, you could configure the Ukadc.Diagnostics ConsoleTraceListener to use a formatting statement something like this:

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

Every message logged would cause the date/time, the source name, the event type, and the message.

Try it out, I think that you will like it. I have used it some myself (mostly for prototyping, not for a "real" product yet) and have had good success.

Note that for some tokens (like DateTime), you can also apply standard formats that are appropriate for the type (e.g. for DateTime you can specify the format in which the date/time should be written).

The file trace listener that comes with Ukadc.Diagnostics also allows its filename to be specified using the token system.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文