diff --git a/src/libraries/System.Configuration.ConfigurationManager/System.Configuration.ConfigurationManager.sln b/src/libraries/System.Configuration.ConfigurationManager/System.Configuration.ConfigurationManager.sln
index 88ac5e88510295..1e57b6f6cba176 100644
--- a/src/libraries/System.Configuration.ConfigurationManager/System.Configuration.ConfigurationManager.sln
+++ b/src/libraries/System.Configuration.ConfigurationManager/System.Configuration.ConfigurationManager.sln
@@ -1,4 +1,8 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32317.152
+MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{15012FB4-9C7C-4DE0-AB44-83A64654D738}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Win32.SystemEvents", "..\Microsoft.Win32.SystemEvents\ref\Microsoft.Win32.SystemEvents.csproj", "{C7D1410B-8CF0-48DB-A0DF-C8E3A341FD12}"
@@ -39,6 +43,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A3B7282E-7D6
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{BFBE3E0E-4E75-4665-A373-132D283D366D}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Diagnostics.EventLog", "..\System.Diagnostics.EventLog\ref\System.Diagnostics.EventLog.csproj", "{C70C30E4-56DC-44FA-B621-9BB4C4E365D0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Diagnostics.EventLog", "..\System.Diagnostics.EventLog\src\System.Diagnostics.EventLog.csproj", "{808D1F6A-2605-4BEB-A64B-0D09CDE558C8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -109,27 +117,37 @@ Global
{E34EBFC8-D1BC-4ED7-8335-C183A5994EE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E34EBFC8-D1BC-4ED7-8335-C183A5994EE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E34EBFC8-D1BC-4ED7-8335-C183A5994EE1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C70C30E4-56DC-44FA-B621-9BB4C4E365D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C70C30E4-56DC-44FA-B621-9BB4C4E365D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C70C30E4-56DC-44FA-B621-9BB4C4E365D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C70C30E4-56DC-44FA-B621-9BB4C4E365D0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {808D1F6A-2605-4BEB-A64B-0D09CDE558C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {808D1F6A-2605-4BEB-A64B-0D09CDE558C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {808D1F6A-2605-4BEB-A64B-0D09CDE558C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {808D1F6A-2605-4BEB-A64B-0D09CDE558C8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{15012FB4-9C7C-4DE0-AB44-83A64654D738} = {3393D976-CAF0-47D0-850E-7A44DF892122}
- {9476F50F-E44C-4420-96AB-448259A47C4F} = {3393D976-CAF0-47D0-850E-7A44DF892122}
{C7D1410B-8CF0-48DB-A0DF-C8E3A341FD12} = {751DA007-D916-4F22-AF16-85F343C2992C}
- {6F662E39-BB56-4BCF-B053-B4A1782A33E1} = {751DA007-D916-4F22-AF16-85F343C2992C}
- {6B586A50-5DFE-4FBE-A65B-9152B992C2E1} = {751DA007-D916-4F22-AF16-85F343C2992C}
- {1C48B652-BA62-4D46-9CDD-24A1670EE3E3} = {751DA007-D916-4F22-AF16-85F343C2992C}
- {CFF7519C-4D41-4713-991D-27777DA2DB21} = {751DA007-D916-4F22-AF16-85F343C2992C}
- {3278A0F9-8E0A-42E1-8FE3-1A01851A6248} = {751DA007-D916-4F22-AF16-85F343C2992C}
{D54AFFF7-9BEE-42C8-89C3-96E7228A23FA} = {A3B7282E-7D62-48ED-B5FC-E6AB32DA6F0A}
+ {6F662E39-BB56-4BCF-B053-B4A1782A33E1} = {751DA007-D916-4F22-AF16-85F343C2992C}
{7C9D7BE4-BF9C-486C-8ADF-53DE282E018A} = {A3B7282E-7D62-48ED-B5FC-E6AB32DA6F0A}
+ {9476F50F-E44C-4420-96AB-448259A47C4F} = {3393D976-CAF0-47D0-850E-7A44DF892122}
+ {6B586A50-5DFE-4FBE-A65B-9152B992C2E1} = {751DA007-D916-4F22-AF16-85F343C2992C}
{E49D2841-A288-4B5F-89FC-857CCE0401F0} = {A3B7282E-7D62-48ED-B5FC-E6AB32DA6F0A}
+ {355B775A-BFE1-4384-BC83-C6B5D1FB77BB} = {BFBE3E0E-4E75-4665-A373-132D283D366D}
+ {66EC63BC-99DC-40CA-B53B-B4D8BF6D1630} = {BFBE3E0E-4E75-4665-A373-132D283D366D}
+ {1C48B652-BA62-4D46-9CDD-24A1670EE3E3} = {751DA007-D916-4F22-AF16-85F343C2992C}
{7B2CA04A-9DFB-471E-B4CB-1937D0049B96} = {A3B7282E-7D62-48ED-B5FC-E6AB32DA6F0A}
+ {CFF7519C-4D41-4713-991D-27777DA2DB21} = {751DA007-D916-4F22-AF16-85F343C2992C}
{2BE20C4A-C20B-4A0C-8E4A-31B681B6967D} = {A3B7282E-7D62-48ED-B5FC-E6AB32DA6F0A}
+ {3278A0F9-8E0A-42E1-8FE3-1A01851A6248} = {751DA007-D916-4F22-AF16-85F343C2992C}
{E34EBFC8-D1BC-4ED7-8335-C183A5994EE1} = {A3B7282E-7D62-48ED-B5FC-E6AB32DA6F0A}
- {355B775A-BFE1-4384-BC83-C6B5D1FB77BB} = {BFBE3E0E-4E75-4665-A373-132D283D366D}
- {66EC63BC-99DC-40CA-B53B-B4D8BF6D1630} = {BFBE3E0E-4E75-4665-A373-132D283D366D}
+ {C70C30E4-56DC-44FA-B621-9BB4C4E365D0} = {751DA007-D916-4F22-AF16-85F343C2992C}
+ {808D1F6A-2605-4BEB-A64B-0D09CDE558C8} = {A3B7282E-7D62-48ED-B5FC-E6AB32DA6F0A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {942CAD8E-C19B-4B9A-BEDE-09B43F45F535}
diff --git a/src/libraries/System.Configuration.ConfigurationManager/ref/System.Configuration.ConfigurationManager.csproj b/src/libraries/System.Configuration.ConfigurationManager/ref/System.Configuration.ConfigurationManager.csproj
index 58e634cc9634ee..82439a81b78185 100644
--- a/src/libraries/System.Configuration.ConfigurationManager/ref/System.Configuration.ConfigurationManager.csproj
+++ b/src/libraries/System.Configuration.ConfigurationManager/ref/System.Configuration.ConfigurationManager.csproj
@@ -8,6 +8,7 @@
+
diff --git a/src/libraries/System.Configuration.ConfigurationManager/ref/System.Configuration.ConfigurationManager.netcoreapp.cs b/src/libraries/System.Configuration.ConfigurationManager/ref/System.Configuration.ConfigurationManager.netcoreapp.cs
new file mode 100644
index 00000000000000..3fffb8cd3951d8
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/ref/System.Configuration.ConfigurationManager.netcoreapp.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ------------------------------------------------------------------------------
+// Changes to this file must follow the https://aka.ms/api-review process.
+// ------------------------------------------------------------------------------
+
+namespace System.Diagnostics
+{
+ public static partial class TraceConfiguration
+ {
+ public static void Register() { }
+ }
+}
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/Resources/Strings.resx b/src/libraries/System.Configuration.ConfigurationManager/src/Resources/Strings.resx
index 29f880277d1335..de9cd14cb77b88 100644
--- a/src/libraries/System.Configuration.ConfigurationManager/src/Resources/Strings.resx
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/Resources/Strings.resx
@@ -664,4 +664,34 @@
The file name '{0}' was already in the collection.
+
+ Could not create listener '{0}'.
+
+
+ Could not create {0}.
+
+
+ Could not find type for class {0}.
+
+
+ Could not find constructor for class {0}.
+
+
+ switchType needs to be a valid class name. It can't be empty.
+
+
+ The specified type, '{0}' is not derived from the appropriate base type, '{1}'.
+
+
+ 'switchValue' and 'switchName' cannot both be specified on source '{0}'.
+
+
+ A listener with no type name specified references the sharedListeners section and cannot have any attributes other than 'Name'. Listener: '{0}'.
+
+
+ Listener '{0}' does not exist in the sharedListeners section.
+
+
+ initializeData needs to be valid for this TraceListener.
+
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj
index 74dcf5f95c4532..2badd2c37adec6 100644
--- a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj
@@ -252,18 +252,28 @@ System.Configuration.ConfigurationManager
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -271,6 +281,10 @@ System.Configuration.ConfigurationManager
+
+
+
+
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ImplicitMachineConfigHost.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ImplicitMachineConfigHost.cs
index 36e4cd1786f3b3..665ef24e47c262 100644
--- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ImplicitMachineConfigHost.cs
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ImplicitMachineConfigHost.cs
@@ -81,6 +81,7 @@ public override Stream OpenStreamForRead(string streamName)
+
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/DiagnosticsConfiguration.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/DiagnosticsConfiguration.cs
new file mode 100644
index 00000000000000..868298fa7f7476
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/DiagnosticsConfiguration.cs
@@ -0,0 +1,213 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Configuration;
+using System.Runtime.Versioning;
+
+namespace System.Diagnostics
+{
+ internal static class DiagnosticsConfiguration
+ {
+ private static volatile SystemDiagnosticsSection s_configSection;
+ private static volatile InitState s_initState = InitState.NotInitialized;
+
+ // Setting for Switch.switchSetting
+ internal static SwitchElementsCollection SwitchSettings
+ {
+ get
+ {
+ Initialize();
+ SystemDiagnosticsSection configSectionSav = s_configSection;
+ return configSectionSav?.Switches;
+ }
+ }
+
+ internal static string ConfigFilePath
+ {
+ [ResourceExposure(ResourceScope.Machine)]
+ [ResourceConsumption(ResourceScope.Machine)]
+ get
+ {
+ Initialize();
+ SystemDiagnosticsSection configSectionSav = s_configSection;
+ if (configSectionSav != null)
+ {
+ return configSectionSav.ElementInformation.Source;
+ }
+
+ return string.Empty; // the default
+ }
+ }
+
+ // Setting for TraceInternal.AutoFlush
+ internal static bool AutoFlush
+ {
+ get
+ {
+ Initialize();
+ SystemDiagnosticsSection configSectionSav = s_configSection;
+ if (configSectionSav != null && configSectionSav.Trace != null)
+ {
+ return configSectionSav.Trace.AutoFlush;
+ }
+
+ return false; // the default
+ }
+ }
+
+ // Setting for TraceInternal.UseGlobalLock
+ internal static bool UseGlobalLock
+ {
+ get
+ {
+ Initialize();
+ SystemDiagnosticsSection configSectionSav = s_configSection;
+ if (configSectionSav != null && configSectionSav.Trace != null)
+ {
+ return configSectionSav.Trace.UseGlobalLock;
+ }
+
+ return true; // the default
+ }
+ }
+
+ // Setting for TraceInternal.IndentSize
+ internal static int IndentSize
+ {
+ get
+ {
+ Initialize();
+ SystemDiagnosticsSection configSectionSav = s_configSection;
+ if (configSectionSav != null && configSectionSav.Trace != null)
+ {
+ return configSectionSav.Trace.IndentSize;
+ }
+
+ return 4; // the default
+ }
+ }
+
+ internal static ListenerElementsCollection SharedListeners
+ {
+ get
+ {
+ Initialize();
+ SystemDiagnosticsSection configSectionSav = s_configSection;
+ return configSectionSav?.SharedListeners;
+ }
+ }
+
+ internal static SourceElementsCollection Sources
+ {
+ get
+ {
+ Initialize();
+ SystemDiagnosticsSection configSectionSav = s_configSection;
+ return configSectionSav?.Sources;
+ }
+ }
+
+ internal static SystemDiagnosticsSection SystemDiagnosticsSection
+ {
+ get
+ {
+ Initialize();
+ return s_configSection;
+ }
+ }
+
+ private static SystemDiagnosticsSection GetConfigSection()
+ {
+ return s_configSection ??= (SystemDiagnosticsSection)PrivilegedConfigurationManager.GetSection("system.diagnostics");
+ }
+
+ internal static bool IsInitializing() => s_initState == InitState.Initializing;
+ internal static bool IsInitialized() => s_initState == InitState.Initialized;
+
+ internal static bool CanInitialize() => (s_initState != InitState.Initializing) &&
+ !ConfigurationManagerInternalFactory.Instance.SetConfigurationSystemInProgress;
+
+ internal static void Initialize()
+ {
+ // Ported from https://referencesource.microsoft.com/#System/compmod/system/diagnostics/DiagnosticsConfiguration.cs,188
+ // This port removed the lock on TraceInternal.critSec since that is now in a separate assembly and TraceInternal
+ // is internal and because GetConfigSection() is not locked elsewhere such as for connection strings.
+
+ // Because some of the code used to load config also uses diagnostics
+ // we can't block them while we initialize from config. Therefore we just
+ // return immediately and they just use the default values.
+ if (s_initState != InitState.NotInitialized ||
+ ConfigurationManagerInternalFactory.Instance.SetConfigurationSystemInProgress)
+ {
+ return;
+ }
+
+ s_initState = InitState.Initializing; // used for preventing recursion
+ try
+ {
+ s_configSection = GetConfigSection();
+ }
+ finally
+ {
+ s_initState = InitState.Initialized;
+ }
+ }
+
+ internal static void Refresh()
+ {
+ ConfigurationManager.RefreshSection("system.diagnostics");
+
+ // There might still be some persistant state left behind for
+ // ConfigPropertyCollection (for ex, swtichelements), probably for perf.
+ // We need to explicitly cleanup any unrecognized attributes that we
+ // have added during last deserialization, so that they are re-added
+ // during the next Config.GetSection properly and we get a chance to
+ // populate the Attributes collection for re-deserialization.
+ // Another alternative could be to expose the properties collection
+ // directly as Attributes collection (currently we keep a local
+ // hashtable which we explicitly need to keep in sycn and hence the
+ // cleanup logic below) but the down side of that would be we need to
+ // explicitly compute what is recognized Vs unrecognized from that
+ // collection when we expose the unrecognized Attributes publically
+ SystemDiagnosticsSection configSectionSav = s_configSection;
+ if (configSectionSav != null)
+ {
+ if (configSectionSav.Switches != null)
+ {
+ foreach (SwitchElement swelem in configSectionSav.Switches)
+ {
+ swelem.ResetProperties();
+ }
+ }
+
+ if (configSectionSav.SharedListeners != null)
+ {
+ foreach (ListenerElement lnelem in configSectionSav.SharedListeners)
+ {
+ lnelem.ResetProperties();
+ }
+ }
+
+ if (configSectionSav.Sources != null)
+ {
+ foreach (SourceElement srelem in configSectionSav.Sources)
+ {
+ srelem.ResetProperties();
+ }
+ }
+ }
+
+ s_configSection = null;
+
+ s_initState = InitState.NotInitialized;
+ Initialize();
+ }
+
+ private enum InitState
+ {
+ NotInitialized,
+ Initializing,
+ Initialized
+ }
+ }
+}
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/FilterElement.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/FilterElement.cs
new file mode 100644
index 00000000000000..6067c06dc5a6e2
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/FilterElement.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.CompilerServices;
+
+namespace System.Diagnostics
+{
+ internal sealed class FilterElement : TypedElement
+ {
+ private static ConditionalWeakTable s_initData = new();
+
+ public FilterElement() : base(typeof(TraceFilter)) { }
+
+ public TraceFilter GetRuntimeObject()
+ {
+ TraceFilter newFilter = (TraceFilter)BaseGetRuntimeObject();
+ s_initData.AddOrUpdate(newFilter, InitData);
+ return newFilter;
+ }
+
+ internal TraceFilter RefreshRuntimeObject(TraceFilter filter)
+ {
+ if (Type.GetType(TypeName) != filter.GetType() || InitDataChanged(filter))
+ {
+ // Type or initdata changed.
+ _runtimeObject = null;
+ return GetRuntimeObject();
+ }
+ else
+ {
+ return filter;
+ }
+ }
+
+ private bool InitDataChanged(TraceFilter filter) => !s_initData.TryGetValue(filter, out string previousInitData) || InitData != previousInitData;
+ }
+}
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/ListenerElementsCollection.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/ListenerElementsCollection.cs
new file mode 100644
index 00000000000000..04a54960bcc91d
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/ListenerElementsCollection.cs
@@ -0,0 +1,372 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Configuration;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Xml;
+
+namespace System.Diagnostics
+{
+ [ConfigurationCollection(typeof(ListenerElement))]
+ internal class ListenerElementsCollection : ConfigurationElementCollection
+ {
+ public new ListenerElement this[string name] => (ListenerElement)BaseGet(name);
+
+ public override ConfigurationElementCollectionType CollectionType => ConfigurationElementCollectionType.AddRemoveClearMap;
+
+ protected override ConfigurationElement CreateNewElement() => new ListenerElement(true);
+
+ protected override object GetElementKey(ConfigurationElement element) => ((ListenerElement)element).Name;
+
+ public IEnumerable GetRuntimeObject()
+ {
+ List listeners = new();
+
+ foreach (ListenerElement element in this)
+ {
+ // At some point, we need to pull out adding/removing the 'default' DefaultTraceListener
+ // code from here in favor of adding/not-adding after we load the config (in TraceSource
+ // and in static Trace).
+
+ listeners.Add(element.GetRuntimeObject());
+ }
+
+ return listeners;
+ }
+
+ protected internal override void InitializeDefault() => InitializeDefaultInternal();
+
+ internal void InitializeDefaultInternal()
+ {
+ ListenerElement defaultListener = new ListenerElement(false);
+ defaultListener.Name = "Default";
+ defaultListener.TypeName = typeof(DefaultTraceListener).FullName;
+ defaultListener._isAddedByDefault = true;
+
+ BaseAdd(defaultListener);
+ }
+
+ protected override void BaseAdd(ConfigurationElement element)
+ {
+ ListenerElement listenerElement = element as ListenerElement;
+
+ Debug.Assert((listenerElement != null), "adding elements other than ListenerElement to ListenerElementsCollection?");
+
+ if (listenerElement.Name.Equals("Default") && listenerElement.TypeName.Equals(typeof(DefaultTraceListener).FullName))
+ BaseAdd(listenerElement, false);
+ else
+ BaseAdd(listenerElement, ThrowOnDuplicate);
+ }
+ }
+
+ // This is the collection used by the sharedListener section. It is only slightly different from ListenerElementsCollection.
+ // The differences are that it does not allow remove and clear, and that the ListenerElements it creates do not allow
+ // references.
+ [ConfigurationCollection(typeof(ListenerElement),
+ AddItemName = "add",
+ CollectionType = ConfigurationElementCollectionType.BasicMap)]
+ internal sealed class SharedListenerElementsCollection : ListenerElementsCollection
+ {
+ public override ConfigurationElementCollectionType CollectionType => ConfigurationElementCollectionType.BasicMap;
+ protected override ConfigurationElement CreateNewElement() => new ListenerElement(false);
+ protected override string ElementName => "add";
+ }
+
+ internal sealed class ListenerElement : TypedElement
+ {
+ private static readonly ConfigurationProperty s_propFilter = new("filter", typeof(FilterElement), null, ConfigurationPropertyOptions.None);
+ private static readonly ConfigurationProperty s_propName = new("name", typeof(string), null, ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsKey);
+ private static readonly ConfigurationProperty s_propOutputOpts = new("traceOutputOptions", typeof(TraceOptions), TraceOptions.None, ConfigurationPropertyOptions.None);
+
+ private ConfigurationProperty _propListenerTypeName;
+ private bool _allowReferences;
+ private StringDictionary _attributes;
+ internal bool _isAddedByDefault;
+
+ private static ConditionalWeakTable s_initData = new();
+
+ public ListenerElement(bool allowReferences) : base(typeof(TraceListener))
+ {
+ _allowReferences = allowReferences;
+
+ ConfigurationPropertyOptions flags = ConfigurationPropertyOptions.None;
+ if (!_allowReferences)
+ flags |= ConfigurationPropertyOptions.IsRequired;
+
+ _propListenerTypeName = new ConfigurationProperty("type", typeof(string), null, flags);
+
+ _properties.Remove("type");
+ _properties.Add(_propListenerTypeName);
+ _properties.Add(s_propFilter);
+ _properties.Add(s_propName);
+ _properties.Add(s_propOutputOpts);
+ }
+
+ public StringDictionary Attributes => _attributes ?? new StringDictionary();
+
+ [ConfigurationProperty("filter")]
+ public FilterElement Filter => (FilterElement)this[s_propFilter];
+
+ [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
+ public string Name
+ {
+ get
+ {
+ return (string)this[s_propName];
+ }
+ set
+ {
+ this[s_propName] = value;
+ }
+ }
+
+ [ConfigurationProperty("traceOutputOptions", DefaultValue = (TraceOptions)TraceOptions.None)]
+ public TraceOptions TraceOutputOptions
+ {
+ get
+ {
+ return (TraceOptions)this[s_propOutputOpts];
+ }
+ // This is useful when the OM becomes public. In the meantime, this can be utilized via reflection.
+ set
+ {
+ this[s_propOutputOpts] = value;
+ }
+
+ }
+
+ [ConfigurationProperty("type")]
+ public override string TypeName
+ {
+ get
+ {
+ return (string)this[_propListenerTypeName];
+ }
+ set
+ {
+ this[_propListenerTypeName] = value;
+ }
+ }
+
+ public override bool Equals(object compareTo)
+ {
+ if (Name.Equals("Default") && TypeName.Equals(typeof(DefaultTraceListener).FullName))
+ {
+ // This is a workaround to treat all DefaultTraceListener named 'Default' the same.
+ // This is needed for the Config.Save to work properly as otherwise config base layers
+ // above us would run into duplicate 'Default' listener element and perceive it as
+ // error.
+ ListenerElement compareToElem = compareTo as ListenerElement;
+ return (compareToElem != null) && compareToElem.Name.Equals("Default")
+ && compareToElem.TypeName.Equals(typeof(DefaultTraceListener).FullName);
+ }
+
+ return base.Equals(compareTo);
+ }
+
+ public override int GetHashCode() => base.GetHashCode();
+
+ public TraceListener GetRuntimeObject()
+ {
+ if (_runtimeObject != null)
+ return (TraceListener)_runtimeObject;
+
+ try
+ {
+ string className = TypeName;
+ if (string.IsNullOrEmpty(className))
+ {
+ // Look it up in SharedListeners.
+ Debug.Assert(_allowReferences, "_allowReferences must be true if type name is null");
+
+ if (_attributes != null || ElementInformation.Properties[s_propFilter.Name].ValueOrigin == PropertyValueOrigin.SetHere || TraceOutputOptions != TraceOptions.None || !string.IsNullOrEmpty(InitData))
+ {
+ throw new ConfigurationErrorsException(SR.Format(SR.Reference_listener_cant_have_properties, Name));
+ }
+
+ if (DiagnosticsConfiguration.SharedListeners == null)
+ {
+ throw new ConfigurationErrorsException(SR.Format(SR.Reference_to_nonexistent_listener, Name));
+ }
+
+ ListenerElement sharedListener = DiagnosticsConfiguration.SharedListeners[Name];
+ if (sharedListener == null)
+ {
+ throw new ConfigurationErrorsException(SR.Format(SR.Reference_to_nonexistent_listener, Name));
+ }
+
+ _runtimeObject = sharedListener.GetRuntimeObject();
+ return (TraceListener)_runtimeObject;
+ }
+ else
+ {
+ // Create a new one.
+ TraceListener newListener = (TraceListener)BaseGetRuntimeObject();
+ s_initData.AddOrUpdate(newListener, InitData);
+ newListener.Name = Name;
+ TraceUtils.CopyStringDictionary(Attributes, newListener.Attributes);
+ newListener.TraceOutputOptions = TraceOutputOptions;
+
+ if (Filter != null && !string.IsNullOrEmpty(Filter.TypeName))
+ {
+ newListener.Filter = Filter.GetRuntimeObject();
+ }
+
+ _runtimeObject = newListener;
+ return newListener;
+ }
+ }
+ catch (ArgumentException e)
+ {
+ throw new ConfigurationErrorsException(SR.Format(SR.Could_not_create_listener, Name), e);
+ }
+ }
+
+ // Our optional attributes implementation is little convoluted as there is
+ // no such first class mechanism from the config system. We basically cache
+ // any "unrecognized" attribute here and serialize it out later.
+ protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
+ {
+ Attributes.Add(name, value);
+ return true;
+ }
+
+ // We need to serialize optional attributes here, a better place would have
+ // been inside SerializeElement but the base class implementation from
+ // ConfigurationElement doesn't take into account for derived class doing
+ // extended serialization, it basically writes out child element that
+ // forces the element closing syntax, so any attribute serialization needs
+ // to happen before normal element serialization from ConfigurationElement.
+ // This means we would write out custom attributes ahead of normal ones.
+ // The other alternative would be to re-implement the entire routine here
+ // which is an overkill and a maintenance issue.
+ protected override void PreSerialize(XmlWriter writer)
+ {
+ if (_attributes != null)
+ {
+ IDictionaryEnumerator e = (IDictionaryEnumerator)_attributes.GetEnumerator();
+ while (e.MoveNext())
+ {
+ string xmlValue = (string)e.Value;
+ string xmlName = (string)e.Key;
+
+ if ((xmlValue != null) && (writer != null))
+ {
+ writer.WriteAttributeString(xmlName, xmlValue);
+ }
+ }
+ }
+ }
+
+ // Account for optional attributes from custom listeners.
+ protected internal override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
+ {
+ bool DataToWrite = base.SerializeElement(writer, serializeCollectionKey);
+ DataToWrite = DataToWrite || ((_attributes != null) && (_attributes.Count > 0));
+ return DataToWrite;
+ }
+
+ protected internal override void Unmerge(ConfigurationElement sourceElement,
+ ConfigurationElement parentElement,
+ ConfigurationSaveMode saveMode)
+ {
+ base.Unmerge(sourceElement, parentElement, saveMode);
+
+ // Unmerge the optional attributes cache as well
+ ListenerElement le = sourceElement as ListenerElement;
+ if ((le != null) && (le._attributes != null))
+ {
+ _attributes = le._attributes;
+ }
+ }
+
+ internal void ResetProperties()
+ {
+ // Blow away any UnrecognizedAttributes that we have deserialized earlier.
+ if (_attributes != null)
+ {
+ _attributes.Clear();
+ _properties.Clear();
+ _properties.Add(_propListenerTypeName);
+ _properties.Add(s_propFilter);
+ _properties.Add(s_propName);
+ _properties.Add(s_propOutputOpts);
+ }
+ }
+
+ internal TraceListener RefreshRuntimeObject(TraceListener listener)
+ {
+ _runtimeObject = null;
+ try
+ {
+ string className = TypeName;
+ if (string.IsNullOrEmpty(className))
+ {
+ // Look it up in SharedListeners and ask the sharedListener to refresh.
+ Debug.Assert(_allowReferences, "_allowReferences must be true if type name is null");
+
+ if (_attributes != null || ElementInformation.Properties[s_propFilter.Name].ValueOrigin == PropertyValueOrigin.SetHere || TraceOutputOptions != TraceOptions.None || !string.IsNullOrEmpty(InitData))
+ {
+ throw new ConfigurationErrorsException(SR.Format(SR.Reference_listener_cant_have_properties, Name));
+ }
+
+ if (DiagnosticsConfiguration.SharedListeners == null)
+ {
+ throw new ConfigurationErrorsException(SR.Format(SR.Reference_to_nonexistent_listener, Name));
+ }
+
+ ListenerElement sharedListener = DiagnosticsConfiguration.SharedListeners[Name];
+ if (sharedListener == null)
+ {
+ throw new ConfigurationErrorsException(SR.Format(SR.Reference_to_nonexistent_listener, Name));
+ }
+
+ _runtimeObject = sharedListener.RefreshRuntimeObject(listener);
+ return (TraceListener)_runtimeObject;
+ }
+ else
+ {
+ // We're the element with the type and initializeData info. First see if those two are the same as they were.
+ // If not, create a whole new object, otherwise, just update the other properties.
+ if (Type.GetType(className) != listener.GetType() || InitDataChanged(listener))
+ {
+ // Type or initdata changed.
+ return GetRuntimeObject();
+ }
+ else
+ {
+ TraceUtils.CopyStringDictionary(Attributes, listener.Attributes);
+ listener.TraceOutputOptions = TraceOutputOptions;
+
+ if (listener.Filter != null)
+ {
+ if (ElementInformation.Properties[s_propFilter.Name].ValueOrigin == PropertyValueOrigin.SetHere ||
+ ElementInformation.Properties[s_propFilter.Name].ValueOrigin == PropertyValueOrigin.Inherited)
+ {
+ listener.Filter = Filter.RefreshRuntimeObject(listener.Filter);
+ }
+ else
+ {
+ listener.Filter = null;
+ }
+ }
+
+ _runtimeObject = listener;
+ return listener;
+ }
+ }
+ }
+ catch (ArgumentException e)
+ {
+ throw new ConfigurationErrorsException(SR.Format(SR.Could_not_create_listener, Name), e);
+ }
+ }
+
+ private bool InitDataChanged(TraceListener listener) => !s_initData.TryGetValue(listener, out string previousInitData)
+ || InitData != previousInitData;
+ }
+}
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/SourceElementsCollection.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/SourceElementsCollection.cs
new file mode 100644
index 00000000000000..b4d0434445f9ab
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/SourceElementsCollection.cs
@@ -0,0 +1,151 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Configuration;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Xml;
+
+namespace System.Diagnostics
+{
+ [ConfigurationCollection(typeof(SourceElement),
+ AddItemName = "source",
+ CollectionType = ConfigurationElementCollectionType.BasicMap)]
+ internal sealed class SourceElementsCollection : ConfigurationElementCollection
+ {
+ public new SourceElement this[string name] => (SourceElement)BaseGet(name);
+
+ protected override string ElementName => "source";
+
+ public override ConfigurationElementCollectionType CollectionType => ConfigurationElementCollectionType.BasicMap;
+
+ protected override ConfigurationElement CreateNewElement()
+ {
+ SourceElement se = new SourceElement();
+ se.Listeners.InitializeDefaultInternal();
+ return se;
+ }
+
+ protected override object GetElementKey(ConfigurationElement element) => ((SourceElement)element).Name;
+ }
+
+
+ internal sealed class SourceElement : ConfigurationElement
+ {
+ private static readonly ConfigurationPropertyCollection _properties = new();
+ private static readonly ConfigurationProperty _propName = new("name", typeof(string), "", ConfigurationPropertyOptions.IsRequired);
+ private static readonly ConfigurationProperty _propSwitchName = new("switchName", typeof(string), null, ConfigurationPropertyOptions.None);
+ private static readonly ConfigurationProperty _propSwitchValue = new("switchValue", typeof(string), null, ConfigurationPropertyOptions.None);
+ private static readonly ConfigurationProperty _propSwitchType = new("switchType", typeof(string), null, ConfigurationPropertyOptions.None);
+ private static readonly ConfigurationProperty _propListeners = new("listeners", typeof(ListenerElementsCollection), new ListenerElementsCollection(), ConfigurationPropertyOptions.None);
+
+ private StringDictionary _attributes;
+
+ static SourceElement()
+ {
+ _properties.Add(_propName);
+ _properties.Add(_propSwitchName);
+ _properties.Add(_propSwitchValue);
+ _properties.Add(_propSwitchType);
+ _properties.Add(_propListeners);
+ }
+
+ public StringDictionary Attributes => _attributes ??= new StringDictionary();
+
+ [ConfigurationProperty("listeners")]
+ public ListenerElementsCollection Listeners => (ListenerElementsCollection)this[_propListeners];
+
+ [ConfigurationProperty("name", IsRequired = true, DefaultValue = "")]
+ public string Name => (string)this[_propName];
+
+ protected internal override ConfigurationPropertyCollection Properties => _properties;
+
+ [ConfigurationProperty("switchName")]
+ public string SwitchName => (string)this[_propSwitchName];
+
+ [ConfigurationProperty("switchValue")]
+ public string SwitchValue => (string)this[_propSwitchValue];
+
+ [ConfigurationProperty("switchType")]
+ public string SwitchType => (string)this[_propSwitchType];
+
+ protected internal override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
+ {
+ base.DeserializeElement(reader, serializeCollectionKey);
+
+ if (!string.IsNullOrEmpty(SwitchName) && !string.IsNullOrEmpty(SwitchValue))
+ throw new ConfigurationErrorsException(SR.Format(SR.Only_specify_one, Name));
+ }
+
+ // Our optional attributes implementation is little convoluted as there is
+ // no such first class mechanism from the config system. We basically cache
+ // any "unrecognized" attribute here and serialize it out later.
+ protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
+ {
+ Attributes.Add(name, value);
+ return true;
+ }
+
+ // We need to serialize optional attributes here, a better place would have
+ // been inside SerializeElement but the base class implementation from
+ // ConfigurationElement doesn't take into account for derived class doing
+ // extended serialization, it basically writes out child element that
+ // forces the element closing syntax, so any attribute serialization needs
+ // to happen before normal element serialization from ConfigurationElement.
+ // This means we would write out custom attributes ahead of normal ones.
+ // The other alternative would be to re-implement the entire routine here
+ // which is an overkill and a maintenance issue.
+ protected override void PreSerialize(XmlWriter writer)
+ {
+ if (_attributes != null)
+ {
+ IDictionaryEnumerator e = (IDictionaryEnumerator)_attributes.GetEnumerator();
+ while (e.MoveNext())
+ {
+ string xmlValue = (string)e.Value;
+ string xmlName = (string)e.Key;
+
+ if ((xmlValue != null) && (writer != null))
+ {
+ writer.WriteAttributeString(xmlName, xmlValue);
+ }
+ }
+ }
+ }
+
+ // Account for optional attributes from custom listeners.
+ protected internal override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
+ {
+ bool DataToWrite = base.SerializeElement(writer, serializeCollectionKey);
+ DataToWrite = DataToWrite || ((_attributes != null) && (_attributes.Count > 0));
+ return DataToWrite;
+ }
+
+ protected internal override void Unmerge(ConfigurationElement sourceElement,
+ ConfigurationElement parentElement,
+ ConfigurationSaveMode saveMode)
+ {
+ base.Unmerge(sourceElement, parentElement, saveMode);
+
+ // Unmerge the optional attributes cache as well
+ SourceElement le = sourceElement as SourceElement;
+ if ((le != null) && (le._attributes != null))
+ this._attributes = le._attributes;
+ }
+
+ internal void ResetProperties()
+ {
+ // Blow away any UnrecognizedAttributes that we have deserialized earlier.
+ if (_attributes != null)
+ {
+ _attributes.Clear();
+ _properties.Clear();
+ _properties.Add(_propName);
+ _properties.Add(_propSwitchName);
+ _properties.Add(_propSwitchValue);
+ _properties.Add(_propSwitchType);
+ _properties.Add(_propListeners);
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/SwitchElementsCollection.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/SwitchElementsCollection.cs
new file mode 100644
index 00000000000000..7cd92443d111cb
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/SwitchElementsCollection.cs
@@ -0,0 +1,112 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Configuration;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Xml;
+
+namespace System.Diagnostics
+{
+ [ConfigurationCollection(typeof(SwitchElement))]
+ internal sealed class SwitchElementsCollection : ConfigurationElementCollection
+ {
+ public new SwitchElement this[string name] => (SwitchElement)BaseGet(name);
+ public override ConfigurationElementCollectionType CollectionType => ConfigurationElementCollectionType.AddRemoveClearMap;
+ protected override ConfigurationElement CreateNewElement() => new SwitchElement();
+ protected override object GetElementKey(ConfigurationElement element) => ((SwitchElement)element).Name;
+ }
+
+ internal sealed class SwitchElement : ConfigurationElement
+ {
+ private static readonly ConfigurationPropertyCollection _properties = new ConfigurationPropertyCollection();
+ private static readonly ConfigurationProperty _propName = new("name", typeof(string), "", ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsKey);
+ private static readonly ConfigurationProperty _propValue = new("value", typeof(string), null, ConfigurationPropertyOptions.IsRequired);
+
+ private StringDictionary _attributes;
+
+ static SwitchElement()
+ {
+ _properties.Add(_propName);
+ _properties.Add(_propValue);
+ }
+
+ public StringDictionary Attributes => _attributes ??= new StringDictionary();
+
+ [ConfigurationProperty("name", DefaultValue = "", IsRequired = true, IsKey = true)]
+ public string Name => (string)this[_propName];
+
+ protected internal override ConfigurationPropertyCollection Properties => _properties;
+
+ [ConfigurationProperty("value", IsRequired = true)]
+ public string Value => (string)this[_propValue];
+
+ // Our optional attributes implementation is little convoluted as there is
+ // no such first class mechanism from the config system. We basically cache
+ // any "unrecognized" attribute here and serialize it out later.
+ protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
+ {
+ Attributes.Add(name, value);
+ return true;
+ }
+
+ // We need to serialize optional attributes here, a better place would have
+ // been inside SerializeElement but the base class implementation from
+ // ConfigurationElement doesn't take into account for derived class doing
+ // extended serialization, it basically writes out child element that
+ // forces the element closing syntax, so any attribute serialization needs
+ // to happen before normal element serialization from ConfigurationElement.
+ // This means we would write out custom attributes ahead of normal ones.
+ // The other alternative would be to re-implement the entire routine here
+ // which is an overkill and a maintenance issue.
+ protected override void PreSerialize(XmlWriter writer)
+ {
+ if (_attributes != null)
+ {
+ IDictionaryEnumerator e = (IDictionaryEnumerator)_attributes.GetEnumerator();
+ while (e.MoveNext())
+ {
+ string xmlValue = (string)e.Value;
+ string xmlName = (string)e.Key;
+
+ if ((xmlValue != null) && (writer != null))
+ {
+ writer.WriteAttributeString(xmlName, xmlValue);
+ }
+ }
+ }
+ }
+
+ // Account for optional attributes from custom listeners.
+ protected internal override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
+ {
+ bool DataToWrite = base.SerializeElement(writer, serializeCollectionKey);
+ DataToWrite = DataToWrite || ((_attributes != null) && (_attributes.Count > 0));
+ return DataToWrite;
+ }
+
+ protected internal override void Unmerge(ConfigurationElement sourceElement,
+ ConfigurationElement parentElement,
+ ConfigurationSaveMode saveMode)
+ {
+ base.Unmerge(sourceElement, parentElement, saveMode);
+
+ // Unmerge the optional attributes cache as well
+ SwitchElement le = sourceElement as SwitchElement;
+ if ((le != null) && (le._attributes != null))
+ this._attributes = le._attributes;
+ }
+
+ internal void ResetProperties()
+ {
+ // Blow away any UnrecognizedAttributes that we have deserialized earlier.
+ if (_attributes != null)
+ {
+ _attributes.Clear();
+ _properties.Clear();
+ _properties.Add(_propName);
+ _properties.Add(_propValue);
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/SystemDiagnosticsSection.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/SystemDiagnosticsSection.cs
new file mode 100644
index 00000000000000..e4b5af069fe35b
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/SystemDiagnosticsSection.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Configuration;
+
+namespace System.Diagnostics
+{
+ internal sealed class SystemDiagnosticsSection : ConfigurationSection
+ {
+ private static readonly ConfigurationPropertyCollection s_properties = new();
+ private static readonly ConfigurationProperty s_propSources = new("sources", typeof(SourceElementsCollection), new SourceElementsCollection(), ConfigurationPropertyOptions.None);
+ private static readonly ConfigurationProperty s_propSharedListeners = new("sharedListeners", typeof(SharedListenerElementsCollection), new SharedListenerElementsCollection(), ConfigurationPropertyOptions.None);
+ private static readonly ConfigurationProperty s_propSwitches = new("switches", typeof(SwitchElementsCollection), new SwitchElementsCollection(), ConfigurationPropertyOptions.None);
+ private static readonly ConfigurationProperty s_propTrace = new("trace", typeof(TraceSection), new TraceSection(), ConfigurationPropertyOptions.None);
+
+ static SystemDiagnosticsSection()
+ {
+ s_properties.Add(s_propSources);
+ s_properties.Add(s_propSharedListeners);
+ s_properties.Add(s_propSwitches);
+ s_properties.Add(s_propTrace);
+ }
+
+ protected internal override ConfigurationPropertyCollection Properties => s_properties;
+
+ [ConfigurationProperty("sources")]
+ public SourceElementsCollection Sources => (SourceElementsCollection)base[s_propSources];
+
+ [ConfigurationProperty("sharedListeners")]
+ public ListenerElementsCollection SharedListeners => (ListenerElementsCollection)base[s_propSharedListeners];
+
+ [ConfigurationProperty("switches")]
+ public SwitchElementsCollection Switches => (SwitchElementsCollection)base[s_propSwitches];
+
+ [ConfigurationProperty("trace")]
+ public TraceSection Trace => (TraceSection)base[s_propTrace];
+
+ protected internal override void InitializeDefault()
+ {
+ Trace.Listeners?.InitializeDefaultInternal();
+ }
+ }
+}
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/TraceConfiguration.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/TraceConfiguration.cs
new file mode 100644
index 00000000000000..d02b5a006b3da4
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/TraceConfiguration.cs
@@ -0,0 +1,200 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Diagnostics
+{
+ public static class TraceConfiguration
+ {
+ private static volatile bool s_registered;
+
+ ///
+ /// Register the configuration system to apply settings from configuration files
+ /// to and related classes.
+ ///
+ public static void Register()
+ {
+ if (!s_registered)
+ {
+ // Registering callbacks more than once is fine, but avoid the overhead common cases without taking a lock.
+ Trace.Refreshing += RefreshingConfiguration;
+ Switch.Initializing += InitializingSwitch;
+ TraceSource.Initializing += InitializingTraceSource;
+
+ ConfigureTraceSettings();
+
+ s_registered = true;
+ }
+ }
+
+ private static void RefreshingConfiguration(object sender, EventArgs e) => DiagnosticsConfiguration.Refresh();
+
+ private static void InitializingTraceSource(object sender, InitializingTraceSourceEventArgs e)
+ {
+ TraceSource traceSource = e.TraceSource;
+
+ // Ported from https://referencesource.microsoft.com/#System/compmod/system/diagnostics/TraceSource.cs,176
+ SourceElementsCollection sources = DiagnosticsConfiguration.Sources;
+
+ if (sources != null)
+ {
+ SourceElement sourceElement = sources[traceSource.Name];
+ if (sourceElement != null)
+ {
+ e.WasInitialized = true;
+
+ // First check if the type changed.
+ if (HasSourceSwitchTypeChanged())
+ {
+ if (!string.IsNullOrEmpty(sourceElement.SwitchName))
+ {
+ CreateSwitch(sourceElement.SwitchType, sourceElement.SwitchName);
+ }
+ else
+ {
+ CreateSwitch(sourceElement.SwitchType, traceSource.Name);
+
+ if (!string.IsNullOrEmpty(sourceElement.SwitchValue))
+ {
+ traceSource.Switch.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 != traceSource.Switch.DisplayName)
+ CreateSwitch(sourceElement.SwitchType, sourceElement.SwitchName);
+ else
+ {
+ traceSource.Switch.Refresh();
+ }
+ }
+ else
+ {
+ // The SwitchValue changed; just update our internalSwitch.
+ if (!string.IsNullOrEmpty(sourceElement.SwitchValue))
+ {
+ traceSource.Switch.Level = (SourceLevels)Enum.Parse(typeof(SourceLevels), sourceElement.SwitchValue);
+ }
+ else
+ {
+ traceSource.Switch.Level = SourceLevels.Off;
+ }
+ }
+
+ TraceListener[] newListenerCollection = new TraceListener[sourceElement.Listeners.Count];
+ int listnerOffset = 0;
+ foreach (ListenerElement listenerElement in sourceElement.Listeners)
+ {
+ TraceListener listener = traceSource.Listeners[listenerElement.Name];
+ if (listener != null)
+ {
+ newListenerCollection[listnerOffset++] = listenerElement.RefreshRuntimeObject(listener);
+ }
+ else
+ {
+ newListenerCollection[listnerOffset++] = listenerElement.GetRuntimeObject();
+ }
+ }
+
+ TraceUtils.CopyStringDictionary(sourceElement.Attributes, traceSource.Attributes);
+
+ traceSource.Listeners.Clear();
+ traceSource.Listeners.AddRange(newListenerCollection);
+ }
+ else
+ {
+ // There was no config, so clear whatever we have.
+ traceSource.Switch.Level = traceSource.DefaultLevel;
+ traceSource.Listeners.Clear();
+ traceSource.Attributes.Clear();
+ }
+
+ bool HasSourceSwitchTypeChanged()
+ {
+ string sourceTypeName = sourceElement.SwitchType;
+ Type currentType = traceSource.Switch.GetType();
+
+ if (string.IsNullOrEmpty(sourceTypeName))
+ {
+ // SourceSwitch is the default switch type.
+ return currentType != typeof(SourceSwitch);
+ }
+
+ if (sourceTypeName == currentType.FullName)
+ {
+ return false;
+ }
+
+ // Since there can be more than one valid AssemblyQualifiedName for a given Type this
+ // check can return true for some cases which can cause a minor side effect of a new
+ // Switch being created instead of just being refreshed.
+ return sourceElement.SwitchType != currentType.AssemblyQualifiedName;
+ }
+ }
+
+ void CreateSwitch(string typeName, string name)
+ {
+ if (!string.IsNullOrEmpty(typeName))
+ {
+ traceSource.Switch = (SourceSwitch)TraceUtils.GetRuntimeObject(typeName, typeof(SourceSwitch), name);
+ }
+ else
+ {
+ traceSource.Switch = new SourceSwitch(name, traceSource.DefaultLevel.ToString());
+ }
+ }
+ }
+
+ private static void ConfigureTraceSettings()
+ {
+ // Ported from https://referencesource.microsoft.com/#System/compmod/system/diagnostics/TraceInternal.cs,06360b4de5e221c2, https://referencesource.microsoft.com/#System/compmod/system/diagnostics/TraceInternal.cs,37
+
+ TraceSection traceSection = DiagnosticsConfiguration.SystemDiagnosticsSection?.Trace;
+
+ if (traceSection != null)
+ {
+ Trace.UseGlobalLock = traceSection.UseGlobalLock;
+ Trace.AutoFlush = traceSection.AutoFlush;
+ Trace.IndentSize = traceSection.IndentSize;
+
+ ListenerElementsCollection listeners = DiagnosticsConfiguration.SystemDiagnosticsSection?.Trace.Listeners;
+ if (listeners != null)
+ {
+ // If listeners were configured, replace the defaults with these.
+ Trace.Listeners.Clear();
+ foreach (var listener in listeners.GetRuntimeObject())
+ {
+ Trace.Listeners.Add(listener);
+ }
+ }
+ }
+ }
+
+ private static void InitializingSwitch(object sender, InitializingSwitchEventArgs e)
+ {
+ Switch sw = e.Switch;
+
+ // Ported from https://referencesource.microsoft.com/#System/compmod/system/diagnostics/Switch.cs,173
+ SwitchElementsCollection switchSettings = DiagnosticsConfiguration.SwitchSettings;
+ if (switchSettings != null)
+ {
+ SwitchElement mySettings = switchSettings[sw.DisplayName];
+
+ if (mySettings != null)
+ {
+ if (mySettings.Value != null)
+ {
+ sw.Value = mySettings.Value;
+ }
+ else
+ {
+ sw.Value = sw.DefaultValue;
+ }
+
+ TraceUtils.CopyStringDictionary(sw.Attributes, mySettings.Attributes);
+ }
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/TraceSection.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/TraceSection.cs
new file mode 100644
index 00000000000000..e5795150549d94
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/TraceSection.cs
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Configuration;
+
+namespace System.Diagnostics
+{
+ internal sealed class TraceSection : ConfigurationElement
+ {
+ private static readonly ConfigurationPropertyCollection s_properties = new();
+ private static readonly ConfigurationProperty s_propListeners = new("listeners", typeof(ListenerElementsCollection), null, ConfigurationPropertyOptions.None);
+ private static readonly ConfigurationProperty s_propAutoFlush = new("autoflush", typeof(bool), false, ConfigurationPropertyOptions.None);
+ private static readonly ConfigurationProperty s_propIndentSize = new("indentsize", typeof(int), 4, ConfigurationPropertyOptions.None);
+ private static readonly ConfigurationProperty s_propUseGlobalLock = new("useGlobalLock", typeof(bool), true, ConfigurationPropertyOptions.None);
+
+ static TraceSection()
+ {
+ s_properties.Add(s_propListeners);
+ s_properties.Add(s_propAutoFlush);
+ s_properties.Add(s_propIndentSize);
+ s_properties.Add(s_propUseGlobalLock);
+ }
+
+ [ConfigurationProperty("autoflush", DefaultValue = false)]
+ public bool AutoFlush => (bool)this[s_propAutoFlush];
+
+ [ConfigurationProperty("indentsize", DefaultValue = 4)]
+ public int IndentSize => (int)this[s_propIndentSize];
+
+ [ConfigurationProperty("listeners")]
+ public ListenerElementsCollection Listeners => (ListenerElementsCollection)this[s_propListeners];
+
+ [ConfigurationProperty("useGlobalLock", DefaultValue = true)]
+ public bool UseGlobalLock => (bool)this[s_propUseGlobalLock];
+
+ protected internal override ConfigurationPropertyCollection Properties => s_properties;
+ }
+}
diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/TraceUtils.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/TraceUtils.cs
new file mode 100644
index 00000000000000..99029ca290ef73
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Diagnostics/TraceUtils.cs
@@ -0,0 +1,214 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Configuration;
+using System.IO;
+using System.Reflection;
+using System.Globalization;
+using System.Collections;
+using System.Runtime.Versioning;
+using System.Collections.Specialized;
+
+namespace System.Diagnostics
+{
+ internal static class TraceUtils
+ {
+ private const string SystemDiagnostics = "System.Diagnostics.";
+
+ internal static object GetRuntimeObject(string className, Type baseType, string initializeData)
+ {
+ object newObject = null;
+ Type objectType = null;
+
+ if (className.Length == 0)
+ {
+ throw new ConfigurationErrorsException(SR.EmptyTypeName_NotAllowed);
+ }
+
+ if (className.StartsWith(SystemDiagnostics))
+ {
+ // Since the config file likely has just the FullName without the assembly name,
+ // map the FullName to the built in types.
+ objectType = MapToBuiltInTypes(className);
+ }
+
+ if (objectType == null)
+ {
+ objectType = Type.GetType(className);
+
+ if (objectType == null)
+ {
+ throw new ConfigurationErrorsException(SR.Format(SR.Could_not_find_type, className));
+ }
+ }
+
+ if (!baseType.IsAssignableFrom(objectType))
+ throw new ConfigurationErrorsException(SR.Format(SR.Incorrect_base_type, className, baseType.FullName));
+
+ Exception innerException = null;
+ try
+ {
+ if (string.IsNullOrEmpty(initializeData))
+ {
+ if (IsOwnedTL(objectType))
+ throw new ConfigurationErrorsException(SR.TL_InitializeData_NotSpecified);
+
+ // Create an object with parameterless constructor.
+ ConstructorInfo ctorInfo = objectType.GetConstructor(Array.Empty());
+ if (ctorInfo == null)
+ throw new ConfigurationErrorsException(SR.Format(SR.Could_not_get_constructor, className));
+ newObject = ctorInfo.Invoke(Array.Empty