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