e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
526 lines
22 KiB
C#
526 lines
22 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="TraceSource.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Threading;
|
|
using System.Configuration;
|
|
using System.Security;
|
|
using System.Security.Permissions;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
namespace System.Diagnostics {
|
|
public class TraceSource {
|
|
private static List<WeakReference> tracesources = new List<WeakReference>();
|
|
private static int s_LastCollectionCount;
|
|
|
|
private volatile SourceSwitch internalSwitch;
|
|
private volatile TraceListenerCollection listeners;
|
|
private StringDictionary attributes;
|
|
private SourceLevels switchLevel;
|
|
private volatile string sourceName;
|
|
internal volatile bool _initCalled = false; // Whether we've called Initialize already.
|
|
|
|
public TraceSource(string name)
|
|
: this(name, SourceLevels.Off) {
|
|
}
|
|
|
|
public TraceSource(string name, SourceLevels defaultLevel) {
|
|
if (name == null)
|
|
throw new ArgumentNullException("name");
|
|
if (name.Length == 0)
|
|
throw new ArgumentException("name");
|
|
|
|
sourceName = name;
|
|
switchLevel = defaultLevel;
|
|
|
|
// Delay load config to avoid perf (and working set) issues in retail
|
|
|
|
// Add a weakreference to this source and cleanup invalid references
|
|
lock(tracesources) {
|
|
_pruneCachedTraceSources();
|
|
tracesources.Add(new WeakReference(this));
|
|
}
|
|
}
|
|
|
|
private static void _pruneCachedTraceSources() {
|
|
lock (tracesources) {
|
|
if (s_LastCollectionCount != GC.CollectionCount(2)) {
|
|
List<WeakReference> buffer = new List<WeakReference>(tracesources.Count);
|
|
for (int i = 0; i < tracesources.Count; i++) {
|
|
TraceSource tracesource = ((TraceSource)tracesources[i].Target);
|
|
if (tracesource != null) {
|
|
buffer.Add(tracesources[i]);
|
|
}
|
|
}
|
|
if (buffer.Count < tracesources.Count) {
|
|
tracesources.Clear();
|
|
tracesources.AddRange(buffer);
|
|
tracesources.TrimExcess();
|
|
}
|
|
s_LastCollectionCount = GC.CollectionCount(2);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Initialize() {
|
|
if (!_initCalled) {
|
|
lock(this) {
|
|
if (_initCalled)
|
|
return;
|
|
|
|
#if CONFIGURATION_DEP
|
|
SourceElementsCollection sourceElements = DiagnosticsConfiguration.Sources;
|
|
|
|
if (sourceElements != null) {
|
|
SourceElement sourceElement = sourceElements[sourceName];
|
|
if (sourceElement != null) {
|
|
if (!String.IsNullOrEmpty(sourceElement.SwitchName)) {
|
|
CreateSwitch(sourceElement.SwitchType, sourceElement.SwitchName);
|
|
}
|
|
else {
|
|
CreateSwitch(sourceElement.SwitchType, sourceName);
|
|
|
|
if (!String.IsNullOrEmpty(sourceElement.SwitchValue))
|
|
internalSwitch.Level = (SourceLevels) Enum.Parse(typeof(SourceLevels), sourceElement.SwitchValue);
|
|
}
|
|
|
|
listeners = sourceElement.Listeners.GetRuntimeObject();
|
|
|
|
attributes = new StringDictionary();
|
|
TraceUtils.VerifyAttributes(sourceElement.Attributes, GetSupportedAttributes(), this);
|
|
attributes.ReplaceHashtable(sourceElement.Attributes);
|
|
}
|
|
else
|
|
NoConfigInit();
|
|
}
|
|
else
|
|
#endif
|
|
NoConfigInit();
|
|
|
|
_initCalled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void NoConfigInit() {
|
|
internalSwitch = new SourceSwitch(sourceName, switchLevel.ToString());
|
|
listeners = new TraceListenerCollection();
|
|
listeners.Add(new DefaultTraceListener());
|
|
attributes = null;
|
|
}
|
|
|
|
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
|
|
public void Close() {
|
|
// No need to call Initialize()
|
|
if (listeners != null) {
|
|
// Use global lock
|
|
lock (TraceInternal.critSec) {
|
|
foreach (TraceListener listener in listeners) {
|
|
listener.Close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Flush() {
|
|
// No need to call Initialize()
|
|
if (listeners != null) {
|
|
if (TraceInternal.UseGlobalLock) {
|
|
lock (TraceInternal.critSec) {
|
|
foreach (TraceListener listener in listeners) {
|
|
listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
foreach (TraceListener listener in listeners) {
|
|
if (!listener.IsThreadSafe) {
|
|
lock (listener) {
|
|
listener.Flush();
|
|
}
|
|
}
|
|
else {
|
|
listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual protected internal string[] GetSupportedAttributes() {
|
|
return null;
|
|
}
|
|
|
|
internal static void RefreshAll() {
|
|
lock (tracesources) {
|
|
_pruneCachedTraceSources();
|
|
for (int i=0; i<tracesources.Count; i++) {
|
|
TraceSource tracesource = ((TraceSource) tracesources[i].Target);
|
|
if (tracesource != null) {
|
|
tracesource.Refresh();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void Refresh() {
|
|
if (!_initCalled) {
|
|
Initialize();
|
|
return;
|
|
}
|
|
|
|
#if CONFIGURATION_DEP
|
|
SourceElementsCollection sources = DiagnosticsConfiguration.Sources;
|
|
|
|
if (sources != null) {
|
|
SourceElement sourceElement = sources[Name];
|
|
if (sourceElement != null) {
|
|
|
|
// first check if the type changed
|
|
if ((String.IsNullOrEmpty(sourceElement.SwitchType) && internalSwitch.GetType() != typeof(SourceSwitch)) ||
|
|
(sourceElement.SwitchType != internalSwitch.GetType().AssemblyQualifiedName)) {
|
|
|
|
if (!String.IsNullOrEmpty(sourceElement.SwitchName)) {
|
|
CreateSwitch(sourceElement.SwitchType, sourceElement.SwitchName);
|
|
}
|
|
else {
|
|
CreateSwitch(sourceElement.SwitchType, Name);
|
|
|
|
if (!String.IsNullOrEmpty(sourceElement.SwitchValue))
|
|
internalSwitch.Level = (SourceLevels) Enum.Parse(typeof(SourceLevels), sourceElement.SwitchValue);
|
|
}
|
|
}
|
|
else if (!String.IsNullOrEmpty(sourceElement.SwitchName)) {
|
|
// create a new switch if the name changed, otherwise just refresh.
|
|
if (sourceElement.SwitchName != internalSwitch.DisplayName)
|
|
CreateSwitch(sourceElement.SwitchType, sourceElement.SwitchName);
|
|
else
|
|
internalSwitch.Refresh();
|
|
}
|
|
else {
|
|
// the switchValue changed. Just update our internalSwitch.
|
|
if (!String.IsNullOrEmpty(sourceElement.SwitchValue))
|
|
internalSwitch.Level = (SourceLevels) Enum.Parse(typeof(SourceLevels), sourceElement.SwitchValue);
|
|
else
|
|
internalSwitch.Level = SourceLevels.Off;
|
|
}
|
|
|
|
TraceListenerCollection newListenerCollection = new TraceListenerCollection();
|
|
foreach (ListenerElement listenerElement in sourceElement.Listeners) {
|
|
TraceListener listener = listeners[listenerElement.Name];
|
|
if (listener != null) {
|
|
newListenerCollection.Add(listenerElement.RefreshRuntimeObject(listener));
|
|
}
|
|
else {
|
|
newListenerCollection.Add(listenerElement.GetRuntimeObject());
|
|
}
|
|
}
|
|
|
|
TraceUtils.VerifyAttributes(sourceElement.Attributes, GetSupportedAttributes(), this);
|
|
|
|
attributes = new StringDictionary();
|
|
attributes.ReplaceHashtable(sourceElement.Attributes);
|
|
|
|
listeners = newListenerCollection;
|
|
}
|
|
else {
|
|
// there was no config, so clear whatever we have.
|
|
internalSwitch.Level = switchLevel;
|
|
listeners.Clear();
|
|
attributes = null;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
[Conditional("TRACE")]
|
|
public void TraceEvent(TraceEventType eventType, int id) {
|
|
// Ensure that config is loaded
|
|
Initialize();
|
|
|
|
TraceEventCache manager = new TraceEventCache();
|
|
|
|
if (internalSwitch.ShouldTrace(eventType) && listeners != null) {
|
|
if (TraceInternal.UseGlobalLock) {
|
|
// we lock on the same object that Trace does because we're writing to the same Listeners.
|
|
lock (TraceInternal.critSec) {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
listener.TraceEvent(manager, Name, eventType, id);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
if (!listener.IsThreadSafe) {
|
|
lock (listener) {
|
|
listener.TraceEvent(manager, Name, eventType, id);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
else {
|
|
listener.TraceEvent(manager, Name, eventType, id);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Conditional("TRACE")]
|
|
public void TraceEvent(TraceEventType eventType, int id, string message) {
|
|
// Ensure that config is loaded
|
|
Initialize();
|
|
|
|
TraceEventCache manager = new TraceEventCache();
|
|
|
|
if (internalSwitch.ShouldTrace(eventType) && listeners != null) {
|
|
if (TraceInternal.UseGlobalLock) {
|
|
// we lock on the same object that Trace does because we're writing to the same Listeners.
|
|
lock (TraceInternal.critSec) {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
listener.TraceEvent(manager, Name, eventType, id, message);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
if (!listener.IsThreadSafe) {
|
|
lock (listener) {
|
|
listener.TraceEvent(manager, Name, eventType, id, message);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
else {
|
|
listener.TraceEvent(manager, Name, eventType, id, message);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Conditional("TRACE")]
|
|
public void TraceEvent(TraceEventType eventType, int id, string format, params object[] args) {
|
|
// Ensure that config is loaded
|
|
Initialize();
|
|
|
|
TraceEventCache manager = new TraceEventCache();
|
|
|
|
if (internalSwitch.ShouldTrace(eventType) && listeners != null) {
|
|
if (TraceInternal.UseGlobalLock) {
|
|
// we lock on the same object that Trace does because we're writing to the same Listeners.
|
|
lock (TraceInternal.critSec) {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
listener.TraceEvent(manager, Name, eventType, id, format, args);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
if (!listener.IsThreadSafe) {
|
|
lock (listener) {
|
|
listener.TraceEvent(manager, Name, eventType, id, format, args);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
else {
|
|
listener.TraceEvent(manager, Name, eventType, id, format, args);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Conditional("TRACE")]
|
|
public void TraceData(TraceEventType eventType, int id, object data) {
|
|
// Ensure that config is loaded
|
|
Initialize();
|
|
|
|
TraceEventCache manager = new TraceEventCache();
|
|
|
|
if (internalSwitch.ShouldTrace(eventType) && listeners != null) {
|
|
if (TraceInternal.UseGlobalLock) {
|
|
// we lock on the same object that Trace does because we're writing to the same Listeners.
|
|
lock (TraceInternal.critSec) {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
listener.TraceData(manager, Name, eventType, id, data);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
if (!listener.IsThreadSafe) {
|
|
lock (listener) {
|
|
listener.TraceData(manager, Name, eventType, id, data);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
else {
|
|
listener.TraceData(manager, Name, eventType, id, data);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Conditional("TRACE")]
|
|
public void TraceData(TraceEventType eventType, int id, params object[] data) {
|
|
// Ensure that config is loaded
|
|
Initialize();
|
|
|
|
TraceEventCache manager = new TraceEventCache();
|
|
|
|
if (internalSwitch.ShouldTrace(eventType) && listeners != null) {
|
|
if (TraceInternal.UseGlobalLock) {
|
|
// we lock on the same object that Trace does because we're writing to the same Listeners.
|
|
lock (TraceInternal.critSec) {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
listener.TraceData(manager, Name, eventType, id, data);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
if (!listener.IsThreadSafe) {
|
|
lock (listener) {
|
|
listener.TraceData(manager, Name, eventType, id, data);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
else {
|
|
listener.TraceData(manager, Name, eventType, id, data);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Conditional("TRACE")]
|
|
public void TraceInformation(string message) { // eventType= TraceEventType.Info, id=0
|
|
// No need to call Initialize()
|
|
TraceEvent(TraceEventType.Information, 0, message, null);
|
|
}
|
|
|
|
[Conditional("TRACE")]
|
|
public void TraceInformation(string format, params object[] args) {
|
|
// No need to call Initialize()
|
|
TraceEvent(TraceEventType.Information, 0, format, args);
|
|
}
|
|
|
|
[Conditional("TRACE")]
|
|
public void TraceTransfer(int id, string message, Guid relatedActivityId) {
|
|
// Ensure that config is loaded
|
|
Initialize();
|
|
|
|
TraceEventCache manager = new TraceEventCache();
|
|
|
|
if (internalSwitch.ShouldTrace(TraceEventType.Transfer) && listeners != null) {
|
|
if (TraceInternal.UseGlobalLock) {
|
|
// we lock on the same object that Trace does because we're writing to the same Listeners.
|
|
lock (TraceInternal.critSec) {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
listener.TraceTransfer(manager, Name, id, message, relatedActivityId);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (int i=0; i<listeners.Count; i++) {
|
|
TraceListener listener = listeners[i];
|
|
if (!listener.IsThreadSafe) {
|
|
lock (listener) {
|
|
listener.TraceTransfer(manager, Name, id, message, relatedActivityId);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
else {
|
|
listener.TraceTransfer(manager, Name, id, message, relatedActivityId);
|
|
if (Trace.AutoFlush) listener.Flush();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CreateSwitch(string typename, string name) {
|
|
if (!String.IsNullOrEmpty(typename))
|
|
internalSwitch = (SourceSwitch) TraceUtils.GetRuntimeObject(typename, typeof(SourceSwitch), name);
|
|
else
|
|
internalSwitch = new SourceSwitch(name, switchLevel.ToString());
|
|
}
|
|
|
|
public StringDictionary Attributes {
|
|
get {
|
|
// Ensure that config is loaded
|
|
Initialize();
|
|
|
|
if (attributes == null)
|
|
attributes = new StringDictionary();
|
|
|
|
return attributes;
|
|
}
|
|
}
|
|
|
|
public string Name {
|
|
get {
|
|
return sourceName;
|
|
}
|
|
}
|
|
|
|
public TraceListenerCollection Listeners {
|
|
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
|
|
get {
|
|
// Ensure that config is loaded
|
|
Initialize();
|
|
|
|
return listeners;
|
|
}
|
|
}
|
|
|
|
public SourceSwitch Switch {
|
|
// No need for security demand here. SourceSwitch.set_Level is protected already.
|
|
get {
|
|
// Ensure that config is loaded
|
|
Initialize();
|
|
|
|
return internalSwitch;
|
|
}
|
|
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
|
|
set {
|
|
if (value == null)
|
|
throw new ArgumentNullException("Switch");
|
|
|
|
// Ensure that config is loaded
|
|
Initialize();
|
|
|
|
internalSwitch = value;
|
|
}
|
|
}
|
|
}
|
|
}
|