PageRenderTime 35ms CodeModel.GetById 10ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 1ms

/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
  1//-----------------------------------------------------------------------------
  2// Copyright (c) Microsoft Corporation.  All rights reserved.
  3//-----------------------------------------------------------------------------
  4
  5namespace Microsoft.Server.Common.Diagnostics
  6{
  7    using System;
  8    using System.ComponentModel;
  9    using System.Diagnostics;
 10    using System.Diagnostics.CodeAnalysis;
 11    using System.Globalization;
 12    using System.IO;
 13    using System.Security;
 14    using System.Security.Permissions;
 15    using System.Text;
 16    using System.Xml;
 17
 18    public abstract class DiagnosticTraceBase
 19    {
 20        //Diagnostics trace
 21        protected const string DefaultTraceListenerName = "Default";
 22        protected const string TraceRecordVersion = "http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord";
 23
 24        internal static string AppDomainFriendlyName = AppDomain.CurrentDomain.FriendlyName;
 25        const ushort TracingEventLogCategory = 4;
 26
 27        object thisLock;
 28        bool tracingEnabled = true;
 29        bool calledShutdown;
 30        bool haveListeners;
 31        SourceLevels sourceLevels;
 32        protected string TraceSourceName { get; set; }
 33        TraceSource traceSource;
 34        [Fx.Tag.SecurityNote(Critical = "This determines the event source name.")]
 35        [SecurityCritical]
 36        string eventSourceName;
 37
 38        protected DiagnosticTraceBase(string traceSourceName)
 39        {
 40            this.thisLock = new object();
 41            this.TraceSourceName = traceSourceName;
 42            this.LastFailure = DateTime.MinValue;
 43        }
 44
 45        protected DateTime LastFailure { get; set; }
 46
 47        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
 48                Justification = "SecurityCritical method. Does not expose critical resources returned by methods with Link Demands")]
 49        [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts",
 50                Justification = "Preserve compat with 4.0 and 4.5")]
 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")]
 53        [SecurityCritical]
 54        [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
 55        static void UnsafeRemoveDefaultTraceListener(TraceSource traceSource)
 56        {
 57            traceSource.Listeners.Remove(DiagnosticTraceBase.DefaultTraceListenerName);
 58        }
 59
 60        public TraceSource TraceSource
 61        {
 62            get
 63            {
 64                return this.traceSource;
 65            }
 66
 67            set
 68            {
 69                SetTraceSource(value);
 70            }
 71        }
 72
 73        [SuppressMessage("Microsoft.Security", "CA2122: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 source)
 79        {
 80            if (source != null)
 81            {
 82                UnsafeRemoveDefaultTraceListener(source);
 83                this.traceSource = source;
 84                this.haveListeners = this.traceSource.Listeners.Count > 0;
 85            }
 86        }
 87
 88        public bool HaveListeners
 89        {
 90            get
 91            {
 92                return this.haveListeners;
 93            }
 94        }
 95
 96        [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Existing API")]
 97        SourceLevels FixLevel(SourceLevels level)
 98        {
 99            //the bit fixing below is meant to keep the trace level legal even if somebody uses numbers in config
100            if (((level & ~SourceLevels.Information) & SourceLevels.Verbose) != 0)
101            {
102                level |= SourceLevels.Verbose;
103            }
104            else if (((level & ~SourceLevels.Warning) & SourceLevels.Information) != 0)
105            {
106                level |= SourceLevels.Information;
107            }
108            else if (((level & ~SourceLevels.Error) & SourceLevels.Warning) != 0)
109            {
110                level |= SourceLevels.Warning;
111            }
112            if (((level & ~SourceLevels.Critical) & SourceLevels.Error) != 0)
113            {
114                level |= SourceLevels.Error;
115            }
116            if ((level & SourceLevels.Critical) != 0)
117            {
118                level |= SourceLevels.Critical;
119            }
120
121            // If only the ActivityTracing flag is set, then
122            // we really have Off. Do not do ActivityTracing then.
123            if (level == SourceLevels.ActivityTracing)
124            {
125                level = SourceLevels.Off;
126            }
127
128            return level;
129        }
130
131        protected virtual void OnSetLevel(SourceLevels level)
132        {
133        }
134
135        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
136                Justification = "Does not expose critical resources returned by methods with Link Demands")]
137        [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking TraceSource.Listeners and SourceSwitch.Level which have Link Demands for UnmanagedCode permission.")]
138        [SecurityCritical]
139        void SetLevel(SourceLevels level)
140        {
141            SourceLevels fixedLevel = FixLevel(level);
142            this.sourceLevels = fixedLevel;
143
144            if (this.TraceSource != null)
145            {
146                // Need this for setup from places like TransactionBridge.
147                this.haveListeners = this.TraceSource.Listeners.Count > 0;
148                OnSetLevel(level);
149
150#pragma warning disable 618
151                this.tracingEnabled = this.HaveListeners && (level != SourceLevels.Off);
152#pragma warning restore 618
153                this.TraceSource.Switch.Level = level;
154            }
155        }
156
157        [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking SetLevel.")]
158        [SecurityCritical]
159        void SetLevelThreadSafe(SourceLevels level)
160        {
161            lock (this.thisLock)
162            {
163                SetLevel(level);
164            }
165        }
166
167        public SourceLevels Level
168        {
169            get
170            {
171                if (this.TraceSource != null && (this.TraceSource.Switch.Level != this.sourceLevels))
172                {
173                    this.sourceLevels = this.TraceSource.Switch.Level;
174                }
175
176                return this.sourceLevels;
177            }
178
179            [Fx.Tag.SecurityNote(Critical = "Critical because we are invoking SetLevelTheadSafe.")]
180            [SecurityCritical]
181            set
182            {
183                SetLevelThreadSafe(value);
184            }
185        }
186
187        protected string EventSourceName
188        {
189            [Fx.Tag.SecurityNote(Critical = "Access critical eventSourceName field",
190                Safe = "Doesn't leak info\\resources")]
191            [SecuritySafeCritical]
192            get
193            {
194                return this.eventSourceName;
195            }
196
197            [Fx.Tag.SecurityNote(Critical = "This determines the event source name.")]
198            [SecurityCritical]
199            set
200            {
201                this.eventSourceName = value;
202            }
203        }
204
205        public bool TracingEnabled
206        {
207            get
208            {
209                return this.tracingEnabled && this.traceSource != null;
210            }
211        }
212
213        protected static string ProcessName
214        {
215            [Fx.Tag.SecurityNote(Critical = "Satisfies a LinkDemand for 'PermissionSetAttribute' on type 'Process' when calling method GetCurrentProcess",
216            Safe = "Does not leak any resource and has been reviewed")]
217            [SecuritySafeCritical]
218            get
219            {
220                string retval = null;
221                using (Process process = Process.GetCurrentProcess())
222                {
223                    retval = process.ProcessName;
224                }
225                return retval;
226            }
227        }
228
229        protected static int ProcessId
230        {
231            [Fx.Tag.SecurityNote(Critical = "Satisfies a LinkDemand for 'PermissionSetAttribute' on type 'Process' when calling method GetCurrentProcess",
232            Safe = "Does not leak any resource and has been reviewed")]
233            [SecuritySafeCritical]
234            get
235            {
236                int retval = -1;
237                using (Process process = Process.GetCurrentProcess())
238                {
239                    retval = process.Id;
240                }
241                return retval;
242            }
243        }
244
245        public virtual bool ShouldTrace(TraceEventLevel level)
246        {
247            return ShouldTraceToTraceSource(level);
248        }
249
250        public bool ShouldTrace(TraceEventType type)
251        {
252            return this.TracingEnabled && this.HaveListeners &&
253                (this.TraceSource != null) &&
254                0 != ((int)type & (int)this.Level);
255        }
256
257        public bool ShouldTraceToTraceSource(TraceEventLevel level)
258        {
259            return ShouldTrace(TraceLevelHelper.GetTraceEventType(level));
260        }
261
262        //only used for exceptions, perf is not important
263        public static string XmlEncode(string text)
264        {
265            if (string.IsNullOrEmpty(text))
266            {
267                return text;
268            }
269
270            int len = text.Length;
271            StringBuilder encodedText = new StringBuilder(len + 8); //perf optimization, expecting no more than 2 > characters
272
273            for (int i = 0; i < len; ++i)
274            {
275                char ch = text[i];
276                switch (ch)
277                {
278                    case '<':
279                        encodedText.Append("&lt;");
280                        break;
281                    case '>':
282                        encodedText.Append("&gt;");
283                        break;
284                    case '&':
285                        encodedText.Append("&amp;");
286                        break;
287                    default:
288                        encodedText.Append(ch);
289                        break;
290                }
291            }
292            return encodedText.ToString();
293        }
294
295        [Fx.Tag.SecurityNote(Critical = "Sets global event handlers for the AppDomain",
296            Safe = "Doesn't leak resources\\Information")]
297        [SecuritySafeCritical]
298        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
299                Justification = "SecuritySafeCritical method, Does not expose critical resources returned by methods with Link Demands")]
300        protected void AddDomainEventHandlersForCleanup()
301        {
302            AppDomain currentDomain = AppDomain.CurrentDomain;
303            if (this.TraceSource != null)
304            {
305                this.haveListeners = this.TraceSource.Listeners.Count > 0;
306            }
307
308            this.tracingEnabled = this.haveListeners;
309            if (this.TracingEnabled)
310            {
311                currentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
312                this.SetLevel(this.TraceSource.Switch.Level);
313                currentDomain.DomainUnload += new EventHandler(ExitOrUnloadEventHandler);
314                currentDomain.ProcessExit += new EventHandler(ExitOrUnloadEventHandler);
315            }
316        }
317
318        void ExitOrUnloadEventHandler(object sender, EventArgs e)
319        {
320            ShutdownTracing();
321        }
322
323        protected abstract void OnUnhandledException(Exception exception);
324
325        private void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
326        {
327            Exception e = (Exception)args.ExceptionObject;
328            OnUnhandledException(e);
329            ShutdownTracing();
330        }
331
332        protected static string CreateSourceString(object source)
333        {
334            return source.GetType().ToString() + "/" + source.GetHashCode().ToString(CultureInfo.CurrentCulture);
335        }
336
337        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "disposed by outer disposable")]
338        protected static string ExceptionToTraceString(Exception exception)
339        {
340            StringBuilder sb = new StringBuilder();
341            using (XmlTextWriter xml = new XmlTextWriter(new StringWriter(sb, CultureInfo.CurrentCulture)))
342            {
343
344                xml.WriteStartElement(DiagnosticStrings.ExceptionTag);
345                xml.WriteElementString(DiagnosticStrings.ExceptionTypeTag, XmlEncode(exception.GetType().AssemblyQualifiedName));
346                xml.WriteElementString(DiagnosticStrings.MessageTag, XmlEncode(exception.Message));
347                xml.WriteElementString(DiagnosticStrings.StackTraceTag, XmlEncode(StackTraceString(exception)));
348                xml.WriteElementString(DiagnosticStrings.ExceptionStringTag, XmlEncode(exception.ToString()));
349                System.ComponentModel.Win32Exception win32Exception = exception as System.ComponentModel.Win32Exception;
350                if (win32Exception != null)
351                {
352                    xml.WriteElementString(DiagnosticStrings.NativeErrorCodeTag, win32Exception.NativeErrorCode.ToString("X", CultureInfo.InvariantCulture));
353                }
354
355                if (exception.Data != null && exception.Data.Count > 0)
356                {
357                    xml.WriteStartElement(DiagnosticStrings.DataItemsTag);
358                    foreach (object dataItem in exception.Data.Keys)
359                    {
360                        xml.WriteStartElement(DiagnosticStrings.DataTag);
361                        xml.WriteElementString(DiagnosticStrings.KeyTag, XmlEncode(dataItem.ToString()));
362                        xml.WriteElementString(DiagnosticStrings.ValueTag, XmlEncode(exception.Data[dataItem].ToString()));
363                        xml.WriteEndElement();
364                    }
365                    xml.WriteEndElement();
366                }
367                if (exception.InnerException != null)
368                {
369                    xml.WriteStartElement(DiagnosticStrings.InnerExceptionTag);
370                    xml.WriteRaw(ExceptionToTraceString(exception.InnerException));
371                    xml.WriteEndElement();
372                }
373                xml.WriteEndElement();
374
375                return sb.ToString();
376            }
377        }
378
379        protected static void AddExceptionToTraceString(XmlWriter xml, Exception exception)
380        {
381            xml.WriteElementString(DiagnosticStrings.ExceptionTypeTag, XmlEncode(exception.GetType().AssemblyQualifiedName));
382            xml.WriteElementString(DiagnosticStrings.MessageTag, XmlEncode(exception.Message));
383            xml.WriteElementString(DiagnosticStrings.StackTraceTag, XmlEncode(StackTraceString(exception)));
384            xml.WriteElementString(DiagnosticStrings.ExceptionStringTag, XmlEncode(exception.ToString()));
385            Win32Exception win32Exception = exception as Win32Exception;
386            if (win32Exception != null)
387            {
388                xml.WriteElementString(DiagnosticStrings.NativeErrorCodeTag, win32Exception.NativeErrorCode.ToString("X", CultureInfo.InvariantCulture));
389            }
390
391            if (exception.Data != null && exception.Data.Count > 0)
392            {
393                xml.WriteStartElement(DiagnosticStrings.DataItemsTag);
394                foreach (object dataItem in exception.Data.Keys)
395                {
396                    xml.WriteStartElement(DiagnosticStrings.DataTag);
397                    xml.WriteElementString(DiagnosticStrings.KeyTag, XmlEncode(dataItem.ToString()));
398                    xml.WriteElementString(DiagnosticStrings.ValueTag, XmlEncode(exception.Data[dataItem].ToString()));
399                    xml.WriteEndElement();
400                }
401                xml.WriteEndElement();
402            }
403            if (exception.InnerException != null)
404            {
405                xml.WriteStartElement(DiagnosticStrings.InnerExceptionTag);
406                AddExceptionToTraceString(xml, exception.InnerException);
407                xml.WriteEndElement();
408            }
409        }
410
411        protected static string StackTraceString(Exception exception)
412        {
413            string retval = exception.StackTrace;
414            if (string.IsNullOrEmpty(retval))
415            {
416                // This means that the exception hasn't been thrown yet. We need to manufacture the stack then.
417                StackTrace stackTrace = new StackTrace(false);
418                // Figure out how many frames should be throw away
419                System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames();
420
421                int frameCount = 0;
422                bool breakLoop = false;
423                foreach (StackFrame frame in stackFrames)
424                {
425                    string methodName = frame.GetMethod().Name;
426                    switch (methodName)
427                    {
428                        case "StackTraceString":
429                        case "AddExceptionToTraceString":
430                        case "BuildTrace":
431                        case "TraceEvent":
432                        case "TraceException":
433                        case "GetAdditionalPayload":
434                            ++frameCount;
435                            break;
436                        default:
437                            if (methodName.StartsWith("ThrowHelper", StringComparison.Ordinal))
438                            {
439                                ++frameCount;
440                            }
441                            else
442                            {
443                                breakLoop = true;
444                            }
445                            break;
446                    }
447                    if (breakLoop)
448                    {
449                        break;
450                    }
451                }
452
453                stackTrace = new StackTrace(frameCount, false);
454                retval = stackTrace.ToString();
455            }
456            return retval;
457        }
458
459        //CSDMain:109153, Duplicate code from System.ServiceModel.Diagnostics
460        [Fx.Tag.SecurityNote(Critical = "Calls unsafe methods, UnsafeCreateEventLogger and UnsafeLogEvent.",
461            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.")]
462        [SecuritySafeCritical]
463        [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts",
464            Justification = "Should not demand permission that is asserted by the EtwProvider ctor.")]
465        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Ported from WCF")]
466        protected void LogTraceFailure(string traceMessage, Exception exception)
467        {
468            const int FailureBlackoutDuration = 10;
469            TimeSpan FailureBlackout = TimeSpan.FromMinutes(FailureBlackoutDuration);
470            try
471            {
472                lock (this.thisLock)
473                {
474                    if (DateTime.UtcNow.Subtract(this.LastFailure) >= FailureBlackout)
475                    {
476                        this.LastFailure = DateTime.UtcNow;
477#pragma warning disable 618
478                        EventLogger logger = EventLogger.UnsafeCreateEventLogger(this.eventSourceName, this);
479#pragma warning restore 618
480                        if (exception == null)
481                        {
482                            logger.UnsafeLogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)Microsoft.Server.Common.Diagnostics.EventLogEventId.FailedToTraceEvent, false,
483                                traceMessage);
484                        }
485                        else
486                        {
487                            logger.UnsafeLogEvent(TraceEventType.Error, TracingEventLogCategory, (uint)Microsoft.Server.Common.Diagnostics.EventLogEventId.FailedToTraceEventWithException, false,
488                                traceMessage, exception.ToString());
489                        }
490                    }
491                }
492            }
493            catch (Exception)
494            {
495                // Cannot do anything with exception but prevent aborting the thread
496            }
497        }
498
499        protected abstract void OnShutdownTracing();
500
501        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is traced")]
502        void ShutdownTracing()
503        {
504            if (!this.calledShutdown)
505            {
506                this.calledShutdown = true;
507                try
508                {
509                    OnShutdownTracing();
510                }
511                catch (Exception exception)
512                {
513                    //log failure
514                    LogTraceFailure(null, exception);
515                }
516            }
517        }
518
519        protected bool CalledShutdown
520        {
521            get
522            {
523                return this.calledShutdown;
524            }
525        }
526
527        public static Guid ActivityId
528        {
529            [Fx.Tag.SecurityNote(Critical = "gets the CorrelationManager, which does a LinkDemand for UnmanagedCode",
530                Safe = "only uses the CM to get the ActivityId, which is not protected data, doesn't leak the CM")]
531            [SecuritySafeCritical]
532            [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
533                Justification = "SecuritySafeCriticial method")]
534            get
535            {
536                object id = Trace.CorrelationManager.ActivityId;
537                return id == null ? Guid.Empty : (Guid)id;
538            }
539
540            [Fx.Tag.SecurityNote(Critical = "gets the CorrelationManager, which does a LinkDemand for UnmanagedCode",
541                Safe = "only uses the CM to get the ActivityId, which is not protected data, doesn't leak the CM")]
542            [SecuritySafeCritical]
543            set
544            {
545                Trace.CorrelationManager.ActivityId = value;
546            }
547        }
548
549        protected static string LookupSeverity(TraceEventType type)
550        {
551            string s;
552            switch (type)
553            {
554                case TraceEventType.Critical:
555                    s = "Critical";
556                    break;
557                case TraceEventType.Error:
558                    s = "Error";
559                    break;
560                case TraceEventType.Warning:
561                    s = "Warning";
562                    break;
563                case TraceEventType.Information:
564                    s = "Information";
565                    break;
566                case TraceEventType.Verbose:
567                    s = "Verbose";
568                    break;
569                case TraceEventType.Start:
570                    s = "Start";
571                    break;
572                case TraceEventType.Stop:
573                    s = "Stop";
574                    break;
575                case TraceEventType.Suspend:
576                    s = "Suspend";
577                    break;
578                case TraceEventType.Transfer:
579                    s = "Transfer";
580                    break;
581                default:
582                    s = type.ToString();
583                    break;
584            }
585
586#pragma warning disable 618
587            Fx.Assert(s == type.ToString(), "Return value should equal the name of the enum");
588#pragma warning restore 618
589            return s;
590        }
591
592        public abstract void TraceHandledException(Exception exception);
593        public abstract bool IsEnabled();
594        public abstract void TraceEventLogEvent(TraceEventType type, TraceRecord traceRecord);
595    }
596}