TraceSource.TraceEvent() fails logging when Exception message contains non-printable characters
I have a call to TraceSource.TraceEvent()
that is sometimes not writing to the Azure Diagnostics logs.
public class WorkerRole : RoleEntryPoint
{
private TraceSource trace = new TraceSource(
"ImportService", SourceLevels.Information);
public override void Run()
{
...
try
{
...
}
catch (Exception ex)
{
bool hasMsg = !string.IsNullOrEmpty(ex.Message);
trace.TraceEvent(TraceEventType.Error, 0,
"ex has message: " + hasMsg.ToString()); // this gets logged
trace.TraceEvent(TraceEventType.Error, 0,
"Inner exception message: " + ex.Message); // this does not
}
}
}
In certain cases, and I can't tell which since I can't read the Exception message, the second call is not found in the WADLogsTable. Are there certain characters that are not allowed, either by TraceSource
or by DiagnosticMonitor
?
To further narrow this down, the Exception in question is actually the InnerException
of Exception: "There is an error in XML document (72, -499)".开发者_如何学Python The XML that causes the Exception contains invalid character entities, eg 
. Could it be that the Exception message contains some of these character entities and the TraceSource
fails to log them?
Edit: I was able to finally repro this in my dev environment and so I was able to examine the Exception in the debugger. The exception that won't log is an XmlException
:
'', hexadecimal value 0x11, is an invalid character. Line 72, position -499.
In between the quotes is the non-printable character - it shows up as a black triangle in the debugger. So, this leads me to believe that my suspicion is correct - Some piece of the logging mechanism doesn't like the non-printable character. So, which piece? Or, more importantly, since it looks like I need to start sanitizing all of my strings when tracing, which characters should I look for to remove?
Is there some built in function that will sanitize a string, removing non-printable characters?
Interesting. It looks like you'll need to HTML encode the exception string. This will turn quotes into e.g. "
and your ASCII non-printing character into 
, or similar.
So:
trace.TraceEvent(TraceEventType.Error, 0,
"ex has message: " + HttpUtility.HtmlEncode(hasMsg.ToString()));
trace.TraceEvent(TraceEventType.Error, 0,
"Inner exception message: " + HttpUtility.HtmlEncode(ex.Message));
should work just fine.
Frustratingly, HttpUtility
is in System.Web, you'll need to add a reference to System.Web.dll to get this to go.
The answer to another question helped me to figure out a solution. I added a couple of extension methods for convenience:
public static string RemoveControlChars(this string s)
{
return Regex.Replace(s, @"(?![\r\n])\p{Cc}", "");
}
public static void TraceEvent(this TraceSource trace,
TraceEventType eventType, MyEvtEnum eventId, string message)
{
trace.TraceEvent(eventType, (int)eventId, message.RemoveControlChars());
}
I like the added benefit of not having to cast MyEvtEnum
to int
every time I call TraceEvent
and it adds a natural overload, so this feels like a double win.
It bothers me that I have to do this at all. One of the primary uses of a diagnostics system is to log Exceptions. Such a diagnostics system should be able to handle any string that an Exception message might contain. I also lose line breaks, which is frustrating. Edit: Losing line breaks was a side effect of RemoveControlChars()
. I didn't realize that \r
and \n
are included as "control characters". I've updated my regular expression to not replace \r
and \n
characters.
I don't like accepting my own answer, so if you have an alternate solution or an improvement to mine, please post it and if it's better, I'll accept it.
精彩评论