问题
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).
回答1:
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.
回答2:
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>
回答3:
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.
来源:https://stackoverflow.com/questions/4062277/net-how-to-suppress-tracesource-header-sourcename-traceeventtype-id