/WCFWebApi/src/Microsoft.Server.Common/Microsoft/Server/Common/Diagnostics/DiagnosticTraceBase.cs
C# | 596 lines | 525 code | 57 blank | 14 comment | 60 complexity | 1d083e047ee853b82e3fea951bd87224 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, Apache-2.0
- //-----------------------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
-
- namespace Microsoft.Server.Common.Diagnostics
- {
- using System;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.IO;
- using System.Security;
- using System.Security.Permissions;
- using System.Text;
- using System.Xml;
-
- public abstract class DiagnosticTraceBase
- {
- //Diagnostics trace
- protected const string DefaultTraceListenerName = "Default";
- protected const string TraceRecordVersion = "http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord";
-
- internal static string AppDomainFriendlyName = AppDomain.CurrentDomain.FriendlyName;
- const ushort TracingEventLogCategory = 4;
-
- object thisLock;
- bool tracingEnabled = true;
- bool calledShutdown;
- bool haveListeners;
- SourceLevels sourceLevels;
- protected string TraceSourceName { get; set; }
- TraceSource traceSource;
- [Fx.Tag.SecurityNote(Critical = "This determines the event source name.")]
- [SecurityCritical]
- string eventSourceName;
-
- protected DiagnosticTraceBase(string traceSourceName)
- {
- this.thisLock = new object();
- this.TraceSourceName = traceSourceName;
- this.LastFailure = DateTime.MinValue;
- }
-
- protected DateTime LastFailure { get; set; }
-
- [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
- Justification = "SecurityCritical method. Does not expose critical resources returned by methods with Link Demands")]
- [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts",
- Justification = "Preserve compat with 4.0 and 4.5")]
- [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking TraceSource.Listeners which has a Link Demand for UnmanagedCode permission.",
- 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")]
- [SecurityCritical]
- [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
- static void UnsafeRemoveDefaultTraceListener(TraceSource traceSource)
- {
- traceSource.Listeners.Remove(DiagnosticTraceBase.DefaultTraceListenerName);
- }
-
- public TraceSource TraceSource
- {
- get
- {
- return this.traceSource;
- }
-
- set
- {
- SetTraceSource(value);
- }
- }
-
- [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
- Justification = "Does not expose critical resources returned by methods with Link Demands")]
- [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking TraceSource.Listeners which has a Link Demand for UnmanagedCode permission.",
- Safe = "Safe because are only retrieving the count of listeners and removing the default trace listener - we aren't leaking any critical resources.")]
- [SecuritySafeCritical]
- protected void SetTraceSource(TraceSource source)
- {
- if (source != null)
- {
- UnsafeRemoveDefaultTraceListener(source);
- this.traceSource = source;
- this.haveListeners = this.traceSource.Listeners.Count > 0;
- }
- }
-
- public bool HaveListeners
- {
- get
- {
- return this.haveListeners;
- }
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Existing API")]
- SourceLevels FixLevel(SourceLevels level)
- {
- //the bit fixing below is meant to keep the trace level legal even if somebody uses numbers in config
- if (((level & ~SourceLevels.Information) & SourceLevels.Verbose) != 0)
- {
- level |= SourceLevels.Verbose;
- }
- else if (((level & ~SourceLevels.Warning) & SourceLevels.Information) != 0)
- {
- level |= SourceLevels.Information;
- }
- else if (((level & ~SourceLevels.Error) & SourceLevels.Warning) != 0)
- {
- level |= SourceLevels.Warning;
- }
- if (((level & ~SourceLevels.Critical) & SourceLevels.Error) != 0)
- {
- level |= SourceLevels.Error;
- }
- if ((level & SourceLevels.Critical) != 0)
- {
- level |= SourceLevels.Critical;
- }
-
- // If only the ActivityTracing flag is set, then
- // we really have Off. Do not do ActivityTracing then.
- if (level == SourceLevels.ActivityTracing)
- {
- level = SourceLevels.Off;
- }
-
- return level;
- }
-
- protected virtual void OnSetLevel(SourceLevels level)
- {
- }
-
- [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
- Justification = "Does not expose critical resources returned by methods with Link Demands")]
- [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking TraceSource.Listeners and SourceSwitch.Level which have Link Demands for UnmanagedCode permission.")]
- [SecurityCritical]
- void SetLevel(SourceLevels level)
- {
- SourceLevels fixedLevel = FixLevel(level);
- this.sourceLevels = fixedLevel;
-
- if (this.TraceSource != null)
- {
- // Need this for setup from places like TransactionBridge.
- this.haveListeners = this.TraceSource.Listeners.Count > 0;
- OnSetLevel(level);
-
- #pragma warning disable 618
- this.tracingEnabled = this.HaveListeners && (level != SourceLevels.Off);
- #pragma warning restore 618
- this.TraceSource.Switch.Level = level;
- }
- }
-
- [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking SetLevel.")]
- [SecurityCritical]
- void SetLevelThreadSafe(SourceLevels level)
- {
- lock (this.thisLock)
- {
- SetLevel(level);
- }
- }
-
- public SourceLevels Level
- {
- get
- {
- if (this.TraceSource != null && (this.TraceSource.Switch.Level != this.sourceLevels))
- {
- this.sourceLevels = this.TraceSource.Switch.Level;
- }
-
- return this.sourceLevels;
- }
-
- [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking SetLevelTheadSafe.")]
- [SecurityCritical]
- set
- {
- SetLevelThreadSafe(value);
- }
- }
-
- protected string EventSourceName
- {
- [Fx.Tag.SecurityNote(Critical = "Access critical eventSourceName field",
- Safe = "Doesn't leak info\\resources")]
- [SecuritySafeCritical]
- get
- {
- return this.eventSourceName;
- }
-
- [Fx.Tag.SecurityNote(Critical = "This determines the event source name.")]
- [SecurityCritical]
- set
- {
- this.eventSourceName = value;
- }
- }
-
- public bool TracingEnabled
- {
- get
- {
- return this.tracingEnabled && this.traceSource != null;
- }
- }
-
- protected static string ProcessName
- {
- [Fx.Tag.SecurityNote(Critical = "Satisfies a LinkDemand for 'PermissionSetAttribute' on type 'Process' when calling method GetCurrentProcess",
- Safe = "Does not leak any resource and has been reviewed")]
- [SecuritySafeCritical]
- get
- {
- string retval = null;
- using (Process process = Process.GetCurrentProcess())
- {
- retval = process.ProcessName;
- }
- return retval;
- }
- }
-
- protected static int ProcessId
- {
- [Fx.Tag.SecurityNote(Critical = "Satisfies a LinkDemand for 'PermissionSetAttribute' on type 'Process' when calling method GetCurrentProcess",
- Safe = "Does not leak any resource and has been reviewed")]
- [SecuritySafeCritical]
- get
- {
- int retval = -1;
- using (Process process = Process.GetCurrentProcess())
- {
- retval = process.Id;
- }
- return retval;
- }
- }
-
- public virtual bool ShouldTrace(TraceEventLevel level)
- {
- return ShouldTraceToTraceSource(level);
- }
-
- public bool ShouldTrace(TraceEventType type)
- {
- return this.TracingEnabled && this.HaveListeners &&
- (this.TraceSource != null) &&
- 0 != ((int)type & (int)this.Level);
- }
-
- public bool ShouldTraceToTraceSource(TraceEventLevel level)
- {
- return ShouldTrace(TraceLevelHelper.GetTraceEventType(level));
- }
-
- //only used for exceptions, perf is not important
- public static string XmlEncode(string text)
- {
- if (string.IsNullOrEmpty(text))
- {
- return text;
- }
-
- int len = text.Length;
- StringBuilder encodedText = new StringBuilder(len + 8); //perf optimization, expecting no more than 2 > characters
-
- for (int i = 0; i < len; ++i)
- {
- char ch = text[i];
- switch (ch)
- {
- case '<':
- encodedText.Append("<");
- break;
- case '>':
- encodedText.Append(">");
- break;
- case '&':
- encodedText.Append("&");
- break;
- default:
- encodedText.Append(ch);
- break;
- }
- }
- return encodedText.ToString();
- }
-
- [Fx.Tag.SecurityNote(Critical = "Sets global event handlers for the AppDomain",
- Safe = "Doesn't leak resources\\Information")]
- [SecuritySafeCritical]
- [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
- Justification = "SecuritySafeCritical method, Does not expose critical resources returned by methods with Link Demands")]
- protected void AddDomainEventHandlersForCleanup()
- {
- AppDomain currentDomain = AppDomain.CurrentDomain;
- if (this.TraceSource != null)
- {
- this.haveListeners = this.TraceSource.Listeners.Count > 0;
- }
-
- this.tracingEnabled = this.haveListeners;
- if (this.TracingEnabled)
- {
- currentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
- this.SetLevel(this.TraceSource.Switch.Level);
- currentDomain.DomainUnload += new EventHandler(ExitOrUnloadEventHandler);
- currentDomain.ProcessExit += new EventHandler(ExitOrUnloadEventHandler);
- }
- }
-
- void ExitOrUnloadEventHandler(object sender, EventArgs e)
- {
- ShutdownTracing();
- }
-
- protected abstract void OnUnhandledException(Exception exception);
-
- private void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
- {
- Exception e = (Exception)args.ExceptionObject;
- OnUnhandledException(e);
- ShutdownTracing();
- }
-
- protected static string CreateSourceString(object source)
- {
- return source.GetType().ToString() + "/" + source.GetHashCode().ToString(CultureInfo.CurrentCulture);
- }
-
- [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "disposed by outer disposable")]
- protected static string ExceptionToTraceString(Exception exception)
- {
- StringBuilder sb = new StringBuilder();
- using (XmlTextWriter xml = new XmlTextWriter(new StringWriter(sb, CultureInfo.CurrentCulture)))
- {
-
- xml.WriteStartElement(DiagnosticStrings.ExceptionTag);
- xml.WriteElementString(DiagnosticStrings.ExceptionTypeTag, XmlEncode(exception.GetType().AssemblyQualifiedName));
- xml.WriteElementString(DiagnosticStrings.MessageTag, XmlEncode(exception.Message));
- xml.WriteElementString(DiagnosticStrings.StackTraceTag, XmlEncode(StackTraceString(exception)));
- xml.WriteElementString(DiagnosticStrings.ExceptionStringTag, XmlEncode(exception.ToString()));
- System.ComponentModel.Win32Exception win32Exception = exception as System.ComponentModel.Win32Exception;
- if (win32Exception != null)
- {
- xml.WriteElementString(DiagnosticStrings.NativeErrorCodeTag, win32Exception.NativeErrorCode.ToString("X", CultureInfo.InvariantCulture));
- }
-
- if (exception.Data != null && exception.Data.Count > 0)
- {
- xml.WriteStartElement(DiagnosticStrings.DataItemsTag);
- foreach (object dataItem in exception.Data.Keys)
- {
- xml.WriteStartElement(DiagnosticStrings.DataTag);
- xml.WriteElementString(DiagnosticStrings.KeyTag, XmlEncode(dataItem.ToString()));
- xml.WriteElementString(DiagnosticStrings.ValueTag, XmlEncode(exception.Data[dataItem].ToString()));
- xml.WriteEndElement();
- }
- xml.WriteEndElement();
- }
- if (exception.InnerException != null)
- {
- xml.WriteStartElement(DiagnosticStrings.InnerExceptionTag);
- xml.WriteRaw(ExceptionToTraceString(exception.InnerException));
- xml.WriteEndElement();
- }
- xml.WriteEndElement();
-
- return sb.ToString();
- }
- }
-
- protected static void AddExceptionToTraceString(XmlWriter xml, Exception exception)
- {
- xml.WriteElementString(DiagnosticStrings.ExceptionTypeTag, XmlEncode(exception.GetType().AssemblyQualifiedName));
- xml.WriteElementString(DiagnosticStrings.MessageTag, XmlEncode(exception.Message));
- xml.WriteElementString(DiagnosticStrings.StackTraceTag, XmlEncode(StackTraceString(exception)));
- xml.WriteElementString(DiagnosticStrings.ExceptionStringTag, XmlEncode(exception.ToString()));
- Win32Exception win32Exception = exception as Win32Exception;
- if (win32Exception != null)
- {
- xml.WriteElementString(DiagnosticStrings.NativeErrorCodeTag, win32Exception.NativeErrorCode.ToString("X", CultureInfo.InvariantCulture));
- }
-
- if (exception.Data != null && exception.Data.Count > 0)
- {
- xml.WriteStartElement(DiagnosticStrings.DataItemsTag);
- foreach (object dataItem in exception.Data.Keys)
- {
- xml.WriteStartElement(DiagnosticStrings.DataTag);
- xml.WriteElementString(DiagnosticStrings.KeyTag, XmlEncode(dataItem.ToString()));
- xml.WriteElementString(DiagnosticStrings.ValueTag, XmlEncode(exception.Data[dataItem].ToString()));
- xml.WriteEndElement();
- }
- xml.WriteEndElement();
- }
- if (exception.InnerException != null)
- {
- xml.WriteStartElement(DiagnosticStrings.InnerExceptionTag);
- AddExceptionToTraceString(xml, exception.InnerException);
- xml.WriteEndElement();
- }
- }
-
- protected static string StackTraceString(Exception exception)
- {
- string retval = exception.StackTrace;
- if (string.IsNullOrEmpty(retval))
- {
- // This means that the exception hasn't been thrown yet. We need to manufacture the stack then.
- StackTrace stackTrace = new StackTrace(false);
- // Figure out how many frames should be throw away
- System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames();
-
- int frameCount = 0;
- bool breakLoop = false;
- foreach (StackFrame frame in stackFrames)
- {
- string methodName = frame.GetMethod().Name;
- switch (methodName)
- {
- case "StackTraceString":
- case "AddExceptionToTraceString":
- case "BuildTrace":
- case "TraceEvent":
- case "TraceException":
- case "GetAdditionalPayload":
- ++frameCount;
- break;
- default:
- if (methodName.StartsWith("ThrowHelper", StringComparison.Ordinal))
- {
- ++frameCount;
- }
- else
- {
- breakLoop = true;
- }
- break;
- }
- if (breakLoop)
- {
- break;
- }
- }
-
- stackTrace = new StackTrace(frameCount, false);
- retval = stackTrace.ToString();
- }
- return retval;
- }
-
- //CSDMain:109153, Duplicate code from System.ServiceModel.Diagnostics
- [Fx.Tag.SecurityNote(Critical = "Calls unsafe methods, UnsafeCreateEventLogger and UnsafeLogEvent.",
- 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.")]
- [SecuritySafeCritical]
- [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts",
- Justification = "Should not demand permission that is asserted by the EtwProvider ctor.")]
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Ported from WCF")]
- protected void LogTraceFailure(string traceMessage, Exception exception)
- {
- const int FailureBlackoutDuration = 10;
- TimeSpan FailureBlackout = TimeSpan.FromMinutes(FailureBlackoutDuration);
- try
- {
- lock (this.thisLock)
- {
- if (DateTime.UtcNow.Subtract(this.LastFailure) >= FailureBlackout)
- {
- this.LastFailure = DateTime.UtcNow;
- #pragma warning disable 618
- EventLogger logger = EventLogger.UnsafeCreateEventLogger(this.eventSourceName, this);
- #pragma warning restore 618
- if (exception == null)
- {
- logger.UnsafeLogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)Microsoft.Server.Common.Diagnostics.EventLogEventId.FailedToTraceEvent, false,
- traceMessage);
- }
- else
- {
- logger.UnsafeLogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)Microsoft.Server.Common.Diagnostics.EventLogEventId.FailedToTraceEventWithException, false,
- traceMessage, exception.ToString());
- }
- }
- }
- }
- catch (Exception)
- {
- // Cannot do anything with exception but prevent aborting the thread
- }
- }
-
- protected abstract void OnShutdownTracing();
-
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is traced")]
- void ShutdownTracing()
- {
- if (!this.calledShutdown)
- {
- this.calledShutdown = true;
- try
- {
- OnShutdownTracing();
- }
- catch (Exception exception)
- {
- //log failure
- LogTraceFailure(null, exception);
- }
- }
- }
-
- protected bool CalledShutdown
- {
- get
- {
- return this.calledShutdown;
- }
- }
-
- public static Guid ActivityId
- {
- [Fx.Tag.SecurityNote(Critical = "gets the CorrelationManager, which does a LinkDemand for UnmanagedCode",
- Safe = "only uses the CM to get the ActivityId, which is not protected data, doesn't leak the CM")]
- [SecuritySafeCritical]
- [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
- Justification = "SecuritySafeCriticial method")]
- get
- {
- object id = Trace.CorrelationManager.ActivityId;
- return id == null ? Guid.Empty : (Guid)id;
- }
-
- [Fx.Tag.SecurityNote(Critical = "gets the CorrelationManager, which does a LinkDemand for UnmanagedCode",
- Safe = "only uses the CM to get the ActivityId, which is not protected data, doesn't leak the CM")]
- [SecuritySafeCritical]
- set
- {
- Trace.CorrelationManager.ActivityId = value;
- }
- }
-
- protected static string LookupSeverity(TraceEventType type)
- {
- string s;
- switch (type)
- {
- case TraceEventType.Critical:
- s = "Critical";
- break;
- case TraceEventType.Error:
- s = "Error";
- break;
- case TraceEventType.Warning:
- s = "Warning";
- break;
- case TraceEventType.Information:
- s = "Information";
- break;
- case TraceEventType.Verbose:
- s = "Verbose";
- break;
- case TraceEventType.Start:
- s = "Start";
- break;
- case TraceEventType.Stop:
- s = "Stop";
- break;
- case TraceEventType.Suspend:
- s = "Suspend";
- break;
- case TraceEventType.Transfer:
- s = "Transfer";
- break;
- default:
- s = type.ToString();
- break;
- }
-
- #pragma warning disable 618
- Fx.Assert(s == type.ToString(), "Return value should equal the name of the enum");
- #pragma warning restore 618
- return s;
- }
-
- public abstract void TraceHandledException(Exception exception);
- public abstract bool IsEnabled();
- public abstract void TraceEventLogEvent(TraceEventType type, TraceRecord traceRecord);
- }
- }