1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.Runtime.Diagnostics
8 using System.Collections;
9 using System.ComponentModel;
10 using System.Diagnostics;
11 using System.Globalization;
13 using System.Security;
16 using System.Xml.XPath;
17 using System.Diagnostics.CodeAnalysis;
18 using System.Security.Permissions;
20 abstract class DiagnosticTraceBase
23 protected const string DefaultTraceListenerName = "Default";
24 protected const string TraceRecordVersion = "http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord";
26 protected static string AppDomainFriendlyName = AppDomain.CurrentDomain.FriendlyName;
27 const ushort TracingEventLogCategory = 4;
30 bool tracingEnabled = true;
34 protected string TraceSourceName;
35 TraceSource traceSource;
36 [Fx.Tag.SecurityNote(Critical = "This determines the event source name.")]
38 string eventSourceName;
40 public DiagnosticTraceBase(string traceSourceName)
42 this.thisLock = new object();
43 this.TraceSourceName = traceSourceName;
44 this.LastFailure = DateTime.MinValue;
47 protected DateTime LastFailure { get; set; }
49 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
50 Justification = "SecurityCritical method. Does not expose critical resources returned by methods with Link Demands")]
51 [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking TraceSource.Listeners which has a Link Demand for UnmanagedCode permission.",
52 Miscellaneous = "Asserting Unmanaged Code causes traceSource.Listeners to be successfully initiated and cached. But the Listeners property has a LinkDemand for UnmanagedCode, so it can't be read by partially trusted assemblies in heterogeneous appdomains")]
54 [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
55 static void UnsafeRemoveDefaultTraceListener(TraceSource traceSource)
57 traceSource.Listeners.Remove(DiagnosticTraceBase.DefaultTraceListenerName);
60 public TraceSource TraceSource
64 return this.traceSource;
69 SetTraceSource(value);
73 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
74 Justification = "Does not expose critical resources returned by methods with Link Demands")]
75 [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking TraceSource.Listeners which has a Link Demand for UnmanagedCode permission.",
76 Safe = "Safe because are only retrieving the count of listeners and removing the default trace listener - we aren't leaking any critical resources.")]
77 [SecuritySafeCritical]
78 protected void SetTraceSource(TraceSource traceSource)
80 if (traceSource != null)
82 UnsafeRemoveDefaultTraceListener(traceSource);
83 this.traceSource = traceSource;
84 this.haveListeners = this.traceSource.Listeners.Count > 0;
88 public bool HaveListeners
92 return this.haveListeners;
96 SourceLevels FixLevel(SourceLevels level)
98 //the bit fixing below is meant to keep the trace level legal even if somebody uses numbers in config
99 if (((level & ~SourceLevels.Information) & SourceLevels.Verbose) != 0)
101 level |= SourceLevels.Verbose;
103 else if (((level & ~SourceLevels.Warning) & SourceLevels.Information) != 0)
105 level |= SourceLevels.Information;
107 else if (((level & ~SourceLevels.Error) & SourceLevels.Warning) != 0)
109 level |= SourceLevels.Warning;
111 if (((level & ~SourceLevels.Critical) & SourceLevels.Error) != 0)
113 level |= SourceLevels.Error;
115 if ((level & SourceLevels.Critical) != 0)
117 level |= SourceLevels.Critical;
120 // If only the ActivityTracing flag is set, then
121 // we really have Off. Do not do ActivityTracing then.
122 if (level == SourceLevels.ActivityTracing)
124 level = SourceLevels.Off;
130 protected virtual void OnSetLevel(SourceLevels level)
134 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
135 Justification = "Does not expose critical resources returned by methods with Link Demands")]
136 [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking TraceSource.Listeners and SourceSwitch.Level which have Link Demands for UnmanagedCode permission.")]
138 void SetLevel(SourceLevels level)
140 SourceLevels fixedLevel = FixLevel(level);
141 this.level = fixedLevel;
143 if (this.TraceSource != null)
145 // Need this for setup from places like TransactionBridge.
146 this.haveListeners = this.TraceSource.Listeners.Count > 0;
149 #pragma warning disable 618
150 this.tracingEnabled = this.HaveListeners && (level != SourceLevels.Off);
151 #pragma warning restore 618
152 this.TraceSource.Switch.Level = level;
156 [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking SetLevel.")]
158 void SetLevelThreadSafe(SourceLevels level)
166 public SourceLevels Level
170 if (this.TraceSource != null && (this.TraceSource.Switch.Level != this.level))
172 this.level = this.TraceSource.Switch.Level;
178 [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking SetLevelTheadSafe.")]
182 SetLevelThreadSafe(value);
186 protected string EventSourceName
188 [Fx.Tag.SecurityNote(Critical = "Access critical eventSourceName field",
189 Safe = "Doesn't leak info\\resources")]
190 [SecuritySafeCritical]
193 return this.eventSourceName;
196 [Fx.Tag.SecurityNote(Critical = "This determines the event source name.")]
200 this.eventSourceName = value;
204 public bool TracingEnabled
208 return this.tracingEnabled && this.traceSource != null;
212 protected static string ProcessName
214 [Fx.Tag.SecurityNote(Critical = "Satisfies a LinkDemand for 'PermissionSetAttribute' on type 'Process' when calling method GetCurrentProcess",
215 Safe = "Does not leak any resource and has been reviewed")]
216 [SecuritySafeCritical]
219 string retval = null;
220 using (Process process = Process.GetCurrentProcess())
222 retval = process.ProcessName;
228 protected static int ProcessId
230 [Fx.Tag.SecurityNote(Critical = "Satisfies a LinkDemand for 'PermissionSetAttribute' on type 'Process' when calling method GetCurrentProcess",
231 Safe = "Does not leak any resource and has been reviewed")]
232 [SecuritySafeCritical]
236 using (Process process = Process.GetCurrentProcess())
244 public virtual bool ShouldTrace(TraceEventLevel level)
246 return ShouldTraceToTraceSource(level);
249 public bool ShouldTrace(TraceEventType type)
251 return this.TracingEnabled && this.HaveListeners &&
252 (this.TraceSource != null) &&
253 0 != ((int)type & (int)this.Level);
256 public bool ShouldTraceToTraceSource(TraceEventLevel level)
258 return ShouldTrace(TraceLevelHelper.GetTraceEventType(level));
261 //only used for exceptions, perf is not important
262 public static string XmlEncode(string text)
264 if (string.IsNullOrEmpty(text))
269 int len = text.Length;
270 StringBuilder encodedText = new StringBuilder(len + 8); //perf optimization, expecting no more than 2 > characters
272 for (int i = 0; i < len; ++i)
278 encodedText.Append("<");
281 encodedText.Append(">");
284 encodedText.Append("&");
287 encodedText.Append(ch);
291 return encodedText.ToString();
294 [Fx.Tag.SecurityNote(Critical = "Sets global event handlers for the AppDomain",
295 Safe = "Doesn't leak resources\\Information")]
296 [SecuritySafeCritical]
297 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
298 Justification = "SecuritySafeCritical method, Does not expose critical resources returned by methods with Link Demands")]
299 protected void AddDomainEventHandlersForCleanup()
301 AppDomain currentDomain = AppDomain.CurrentDomain;
302 if (this.TraceSource != null)
304 this.haveListeners = this.TraceSource.Listeners.Count > 0;
307 this.tracingEnabled = this.haveListeners;
308 if (this.TracingEnabled)
310 currentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
311 this.SetLevel(this.TraceSource.Switch.Level);
312 currentDomain.DomainUnload += new EventHandler(ExitOrUnloadEventHandler);
313 currentDomain.ProcessExit += new EventHandler(ExitOrUnloadEventHandler);
317 void ExitOrUnloadEventHandler(object sender, EventArgs e)
322 protected abstract void OnUnhandledException(Exception exception);
324 protected void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
326 Exception e = (Exception)args.ExceptionObject;
327 OnUnhandledException(e);
331 protected static string CreateSourceString(object source)
333 var traceSourceStringProvider = source as ITraceSourceStringProvider;
334 if (traceSourceStringProvider != null)
336 return traceSourceStringProvider.GetSourceString();
339 return CreateDefaultSourceString(source);
342 internal static string CreateDefaultSourceString(object source)
346 throw new ArgumentNullException("source");
349 return String.Format(CultureInfo.CurrentCulture, "{0}/{1}", source.GetType().ToString(), source.GetHashCode());
352 protected static void AddExceptionToTraceString(XmlWriter xml, Exception exception)
354 xml.WriteElementString(DiagnosticStrings.ExceptionTypeTag, XmlEncode(exception.GetType().AssemblyQualifiedName));
355 xml.WriteElementString(DiagnosticStrings.MessageTag, XmlEncode(exception.Message));
356 xml.WriteElementString(DiagnosticStrings.StackTraceTag, XmlEncode(StackTraceString(exception)));
357 xml.WriteElementString(DiagnosticStrings.ExceptionStringTag, XmlEncode(exception.ToString()));
358 Win32Exception win32Exception = exception as Win32Exception;
359 if (win32Exception != null)
361 xml.WriteElementString(DiagnosticStrings.NativeErrorCodeTag, win32Exception.NativeErrorCode.ToString("X", CultureInfo.InvariantCulture));
364 if (exception.Data != null && exception.Data.Count > 0)
366 xml.WriteStartElement(DiagnosticStrings.DataItemsTag);
367 foreach (object dataItem in exception.Data.Keys)
369 xml.WriteStartElement(DiagnosticStrings.DataTag);
370 xml.WriteElementString(DiagnosticStrings.KeyTag, XmlEncode(dataItem.ToString()));
371 xml.WriteElementString(DiagnosticStrings.ValueTag, XmlEncode(exception.Data[dataItem].ToString()));
372 xml.WriteEndElement();
374 xml.WriteEndElement();
376 if (exception.InnerException != null)
378 xml.WriteStartElement(DiagnosticStrings.InnerExceptionTag);
379 AddExceptionToTraceString(xml, exception.InnerException);
380 xml.WriteEndElement();
384 protected static string StackTraceString(Exception exception)
386 string retval = exception.StackTrace;
387 if (string.IsNullOrEmpty(retval))
389 // This means that the exception hasn't been thrown yet. We need to manufacture the stack then.
390 StackTrace stackTrace = new StackTrace(false);
391 // Figure out how many frames should be throw away
392 System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames();
395 bool breakLoop = false;
396 foreach (StackFrame frame in stackFrames)
398 string methodName = frame.GetMethod().Name;
401 case "StackTraceString":
402 case "AddExceptionToTraceString":
405 case "TraceException":
406 case "GetAdditionalPayload":
410 if (methodName.StartsWith("ThrowHelper", StringComparison.Ordinal))
426 stackTrace = new StackTrace(frameCount, false);
427 retval = stackTrace.ToString();
432 //CSDMain:109153, Duplicate code from System.ServiceModel.Diagnostics
433 [Fx.Tag.SecurityNote(Critical = "Calls unsafe methods, UnsafeCreateEventLogger and UnsafeLogEvent.",
434 Safe = "Event identities cannot be spoofed as they are constants determined inside the method, Demands the same permission that is asserted by the unsafe method.")]
435 [SecuritySafeCritical]
436 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.SecureAsserts,
437 Justification = "Should not demand permission that is asserted by the EtwProvider ctor.")]
438 protected void LogTraceFailure(string traceString, Exception exception)
440 const int FailureBlackoutDuration = 10;
441 TimeSpan FailureBlackout = TimeSpan.FromMinutes(FailureBlackoutDuration);
446 if (DateTime.UtcNow.Subtract(this.LastFailure) >= FailureBlackout)
448 this.LastFailure = DateTime.UtcNow;
449 #pragma warning disable 618
450 EventLogger logger = EventLogger.UnsafeCreateEventLogger(this.eventSourceName, this);
451 #pragma warning restore 618
452 if (exception == null)
454 logger.UnsafeLogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToTraceEvent, false,
459 logger.UnsafeLogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToTraceEventWithException, false,
460 traceString, exception.ToString());
465 catch (Exception eventLoggerException)
467 if (Fx.IsFatal(eventLoggerException))
474 protected abstract void OnShutdownTracing();
476 void ShutdownTracing()
478 if (!this.calledShutdown)
480 this.calledShutdown = true;
485 #pragma warning suppress 56500 //[....]; Taken care of by FxCop
486 catch (Exception exception)
488 if (Fx.IsFatal(exception))
494 LogTraceFailure(null, exception);
499 protected bool CalledShutdown
503 return this.calledShutdown;
507 public static Guid ActivityId
509 [Fx.Tag.SecurityNote(Critical = "gets the CorrelationManager, which does a LinkDemand for UnmanagedCode",
510 Safe = "only uses the CM to get the ActivityId, which is not protected data, doesn't leak the CM")]
511 [SecuritySafeCritical]
512 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
513 Justification = "SecuritySafeCriticial method")]
516 object id = Trace.CorrelationManager.ActivityId;
517 return id == null ? Guid.Empty : (Guid)id;
520 [Fx.Tag.SecurityNote(Critical = "gets the CorrelationManager, which does a LinkDemand for UnmanagedCode",
521 Safe = "only uses the CM to get the ActivityId, which is not protected data, doesn't leak the CM")]
522 [SecuritySafeCritical]
525 Trace.CorrelationManager.ActivityId = value;
529 #pragma warning restore 56500
531 protected static string LookupSeverity(TraceEventType type)
536 case TraceEventType.Critical:
539 case TraceEventType.Error:
542 case TraceEventType.Warning:
545 case TraceEventType.Information:
548 case TraceEventType.Verbose:
551 case TraceEventType.Start:
554 case TraceEventType.Stop:
557 case TraceEventType.Suspend:
560 case TraceEventType.Transfer:
568 #pragma warning disable 618
569 Fx.Assert(s == type.ToString(), "Return value should equal the name of the enum");
570 #pragma warning restore 618
574 public abstract bool IsEnabled();
575 public abstract void TraceEventLogEvent(TraceEventType type, TraceRecord traceRecord);