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 #if MONO_FEATURE_MULTIPLE_APPDOMAINS
313 currentDomain.DomainUnload += new EventHandler(ExitOrUnloadEventHandler);
315 currentDomain.ProcessExit += new EventHandler(ExitOrUnloadEventHandler);
319 void ExitOrUnloadEventHandler(object sender, EventArgs e)
324 protected abstract void OnUnhandledException(Exception exception);
326 protected void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
328 Exception e = (Exception)args.ExceptionObject;
329 OnUnhandledException(e);
333 protected static string CreateSourceString(object source)
335 var traceSourceStringProvider = source as ITraceSourceStringProvider;
336 if (traceSourceStringProvider != null)
338 return traceSourceStringProvider.GetSourceString();
341 return CreateDefaultSourceString(source);
344 internal static string CreateDefaultSourceString(object source)
348 throw new ArgumentNullException("source");
351 return String.Format(CultureInfo.CurrentCulture, "{0}/{1}", source.GetType().ToString(), source.GetHashCode());
354 protected static void AddExceptionToTraceString(XmlWriter xml, Exception exception)
356 xml.WriteElementString(DiagnosticStrings.ExceptionTypeTag, XmlEncode(exception.GetType().AssemblyQualifiedName));
357 xml.WriteElementString(DiagnosticStrings.MessageTag, XmlEncode(exception.Message));
358 xml.WriteElementString(DiagnosticStrings.StackTraceTag, XmlEncode(StackTraceString(exception)));
359 xml.WriteElementString(DiagnosticStrings.ExceptionStringTag, XmlEncode(exception.ToString()));
360 Win32Exception win32Exception = exception as Win32Exception;
361 if (win32Exception != null)
363 xml.WriteElementString(DiagnosticStrings.NativeErrorCodeTag, win32Exception.NativeErrorCode.ToString("X", CultureInfo.InvariantCulture));
366 if (exception.Data != null && exception.Data.Count > 0)
368 xml.WriteStartElement(DiagnosticStrings.DataItemsTag);
369 foreach (object dataItem in exception.Data.Keys)
371 xml.WriteStartElement(DiagnosticStrings.DataTag);
372 xml.WriteElementString(DiagnosticStrings.KeyTag, XmlEncode(dataItem.ToString()));
373 xml.WriteElementString(DiagnosticStrings.ValueTag, XmlEncode(exception.Data[dataItem].ToString()));
374 xml.WriteEndElement();
376 xml.WriteEndElement();
378 if (exception.InnerException != null)
380 xml.WriteStartElement(DiagnosticStrings.InnerExceptionTag);
381 AddExceptionToTraceString(xml, exception.InnerException);
382 xml.WriteEndElement();
386 protected static string StackTraceString(Exception exception)
388 string retval = exception.StackTrace;
389 if (string.IsNullOrEmpty(retval))
391 // This means that the exception hasn't been thrown yet. We need to manufacture the stack then.
392 StackTrace stackTrace = new StackTrace(false);
393 // Figure out how many frames should be throw away
394 System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames();
397 bool breakLoop = false;
398 foreach (StackFrame frame in stackFrames)
400 string methodName = frame.GetMethod().Name;
403 case "StackTraceString":
404 case "AddExceptionToTraceString":
407 case "TraceException":
408 case "GetAdditionalPayload":
412 if (methodName.StartsWith("ThrowHelper", StringComparison.Ordinal))
428 stackTrace = new StackTrace(frameCount, false);
429 retval = stackTrace.ToString();
434 //CSDMain:109153, Duplicate code from System.ServiceModel.Diagnostics
435 [Fx.Tag.SecurityNote(Critical = "Calls unsafe methods, UnsafeCreateEventLogger and UnsafeLogEvent.",
436 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.")]
437 [SecuritySafeCritical]
438 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.SecureAsserts,
439 Justification = "Should not demand permission that is asserted by the EtwProvider ctor.")]
440 protected void LogTraceFailure(string traceString, Exception exception)
442 const int FailureBlackoutDuration = 10;
443 TimeSpan FailureBlackout = TimeSpan.FromMinutes(FailureBlackoutDuration);
448 if (DateTime.UtcNow.Subtract(this.LastFailure) >= FailureBlackout)
450 this.LastFailure = DateTime.UtcNow;
451 #pragma warning disable 618
452 EventLogger logger = EventLogger.UnsafeCreateEventLogger(this.eventSourceName, this);
453 #pragma warning restore 618
454 if (exception == null)
456 logger.UnsafeLogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToTraceEvent, false,
461 logger.UnsafeLogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)System.Runtime.Diagnostics.EventLogEventId.FailedToTraceEventWithException, false,
462 traceString, exception.ToString());
467 catch (Exception eventLoggerException)
469 if (Fx.IsFatal(eventLoggerException))
476 protected abstract void OnShutdownTracing();
478 void ShutdownTracing()
480 if (!this.calledShutdown)
482 this.calledShutdown = true;
487 #pragma warning suppress 56500 //[....]; Taken care of by FxCop
488 catch (Exception exception)
490 if (Fx.IsFatal(exception))
496 LogTraceFailure(null, exception);
501 protected bool CalledShutdown
505 return this.calledShutdown;
509 public static Guid ActivityId
511 [Fx.Tag.SecurityNote(Critical = "gets the CorrelationManager, which does a LinkDemand for UnmanagedCode",
512 Safe = "only uses the CM to get the ActivityId, which is not protected data, doesn't leak the CM")]
513 [SecuritySafeCritical]
514 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands,
515 Justification = "SecuritySafeCriticial method")]
518 object id = Trace.CorrelationManager.ActivityId;
519 return id == null ? Guid.Empty : (Guid)id;
522 [Fx.Tag.SecurityNote(Critical = "gets the CorrelationManager, which does a LinkDemand for UnmanagedCode",
523 Safe = "only uses the CM to get the ActivityId, which is not protected data, doesn't leak the CM")]
524 [SecuritySafeCritical]
527 Trace.CorrelationManager.ActivityId = value;
531 #pragma warning restore 56500
533 protected static string LookupSeverity(TraceEventType type)
538 case TraceEventType.Critical:
541 case TraceEventType.Error:
544 case TraceEventType.Warning:
547 case TraceEventType.Information:
550 case TraceEventType.Verbose:
553 case TraceEventType.Start:
556 case TraceEventType.Stop:
559 case TraceEventType.Suspend:
562 case TraceEventType.Transfer:
570 #pragma warning disable 618
571 Fx.Assert(s == type.ToString(), "Return value should equal the name of the enum");
572 #pragma warning restore 618
576 public abstract bool IsEnabled();
577 public abstract void TraceEventLogEvent(TraceEventType type, TraceRecord traceRecord);