dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.27k stars 4.73k forks source link

System.Diagnostics.TraceSource doesn't read configuration from App.config in .NET Core? #23937

Closed jemiller0 closed 2 years ago

jemiller0 commented 7 years ago

The following code outputs "Hello, world!" as a trace message to the console when in a .NET Framework console application. When the same code is used in a .NET Core console application, it outputs nothing.

Program.cs

using System.Diagnostics;

namespace ConsoleApp5
{
    class Program
    {
        private readonly static TraceSource traceSource = new TraceSource("ConsoleApp5", SourceLevels.All);

        static void Main(string[] args)
        {
            traceSource.TraceEvent(TraceEventType.Information, 0, "Hello, world!");
        }
    }
}

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <add name="consoleTraceListener" />
        <add name="textWriterTraceListener" />
      </listeners>
    </trace>
    <sharedListeners>
      <add name="consoleTraceListener" type="System.Diagnostics.ConsoleTraceListener" traceOutputOptions="DateTime,ThreadId" />
      <add name="textWriterTraceListener" type="System.Diagnostics.TextWriterTraceListener" traceOutputOptions="DateTime,ThreadId" initializeData="Trace.log" />
    </sharedListeners>
    <sources>
      <source name="ConsoleApp5" switchValue="Verbose">
        <listeners>
          <add name="consoleTraceListener" />
          <add name="textWriterTraceListener" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
</configuration>

Also, I found that if I try to read a connection string using System.Configuration.ConfigurationManager and I have the system.diagnostics section in the App.config, I receive the following error. I'm assuming that .NET Core doesn't have a machine.config where the system.diagnostics section would normally be defined?

Unhandled Exception: System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize ---> System.Configuration.ConfigurationErrorsException: Unrecognized configuration section system.diagnostics. (C:\Users\jemiller\Documents\Visual Studio 2017\Projects\Library2\LibraryConsoleApplication\bin\Debug\netcoreapp2.0\LibraryConsoleApplication.dll.config line 6)
   at System.Configuration.ConfigurationSchemaErrors.ThrowIfErrors(Boolean ignoreLocal)
   at System.Configuration.BaseConfigurationRecord.ThrowIfParseErrors(ConfigurationSchemaErrors schemaErrors)
   at System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey)
   --- End of inner exception stack trace ---
   at System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey)
   at System.Configuration.ClientConfigurationSystem.PrepareClientConfigSystem(String sectionName)
   at System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)
   at System.Configuration.ConfigurationManager.GetSection(String sectionName)
   at System.Configuration.ConfigurationManager.get_ConnectionStrings()
   at LibraryLibrary.LibraryContext..ctor(String name) in C:\Users\jemiller\Documents\Visual Studio 2017\Projects\Library2\LibraryLibrary\LibraryContext.cs:line 43
   at LibraryLibrary.LibraryContext..ctor() in C:\Users\jemiller\Documents\Visual Studio 2017\Projects\Library2\LibraryLibrary\LibraryContext.cs:line 39
   at LibraryConsoleApplication.Program.Main(String[] args) in C:\Users\jemiller\Documents\Visual Studio 2017\Projects\Library2\LibraryConsoleApplication\Program.cs:line 13

Is there I way I could specify the section similar to the following to get it to work? That is how it is defined in machine.config for .NET Framework.

  <configSections>
    <section name="system.diagnostics" type="System.Diagnostics.SystemDiagnosticsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
  </configSections>

Is there some other way in .NET Core to specify that configuration settings for system.diagnostics?

MSACATS commented 2 years ago

Okay taking a step back - I don't think these comments trying to persuade us that "we're holding it wrong" (i.e. that this isn't an issue) are productive.

So can we brainstorm solutions?

I think we can presume it's not likely that we're going to bring back the app.config model. So what is realistic?

Can we have an app pass in xml-like contents to configure the System.Diagnostics stack? So that even if we're not bringing back app.config per se, an app could still pass through a chunk of configuration XML (e.g. that it may read from a standalone config file...) to configure TraceSource/TraceSwitch?

e.g. some call like System.Diagnostics.ConfigureFromXml(TextReader) or something along those lines?

jkotas commented 2 years ago

The logic to read the TraceSource initialization xml should live in https://github.com/dotnet/runtime/tree/main/src/libraries/System.Configuration.ConfigurationManager/src . Would it make sense to do it implicitly when the ConfigurationManager is initialized (no new method required)?

TraceSource would have to expose a few new public methods to make it possible for System.Configuration.ConfigurationManager to initialize it. For example, a new method to return all TraceSwitches instance. These new public methods would have to go through an API review.

MarcoDorantes commented 2 years ago
  1. stick on .NET Framework 4.8 forever

Mark S - ACA, no, those points are not a fair summary of that comment of mine: I mentioned the fact that net48 is still fully supported but that would be a temporary condition. My point is that an app could still be using net48 tracing in the meantime you research for the evolution of tracing, e.g., build or buy, for your own app. So, “1. stick on .NET Framework 4.8 forever” is not what I am saying.

ericstj commented 2 years ago

@jkotas what you suggest could work, however I don't think anything in the app would force ConfigurationManager to be initialized, so user code could start tracing before any one touches configuration. TraceSouce itself might need some light-up code that probes for Configuration via reflection to force that to load.

jkotas commented 2 years ago

The app can explicitly initialize ConfigurationManager in its Main method. Adding a single to Main method should not be prohibitive, and gives the app a full control. I would avoid any magic light-up - it can help as much as it can hurt.

ericstj commented 2 years ago

I see what you mean, perhaps an explicit method in Configuration would take some magic out of it - avoid folks relying on someone else initializing config and not realizing it.

RussKie commented 2 years ago

The app can explicitly initialize ConfigurationManager in its Main method. Adding a single to Main method should not be prohibitive, and gives the app a full control. I would avoid any magic light-up - it can help as much as it can hurt.

This feels like a sensible workaround, however it appears there's something else is broken if you go down this route:

System.Configuration.ConfigurationErrorsException
  HResult=0x80131902
  Message=Configuration system failed to initialize
  Source=System.Configuration.ConfigurationManager
  StackTrace:
   at System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey)
   at System.Configuration.ClientConfigurationSystem.PrepareClientConfigSystem(String sectionName)
   at System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)
   at System.Configuration.ConfigurationManager.GetSection(String sectionName)
   at System.Configuration.ConfigurationManager.get_AppSettings()
   at WinFormsApp1.Program.Main() in D:\Development\!Tests\WinFormsApp1\Program.cs:line 16

  This exception was originally thrown at this call stack:
    System.Configuration.ConfigurationSchemaErrors.ThrowIfErrors(bool)
    System.Configuration.BaseConfigurationRecord.ThrowIfParseErrors(System.Configuration.ConfigurationSchemaErrors)
    System.Configuration.BaseConfigurationRecord.ThrowIfInitErrors()
    System.Configuration.ClientConfigurationSystem.EnsureInit(string)

Inner Exception 1:
ConfigurationErrorsException: Unrecognized configuration section system.diagnostics. (D:\Development\!Tests\WinFormsApp1\bin\Debug\net6.0-windows\WinFormsApp1.dll.config line 3)

Here's a repro that is working for .NET Framework and is failing for .NET: WinFormsApp1.zip

RussKie commented 2 years ago

The latter looks like the root cause of several other issues we thought were caused by the CPS, e.g.: https://github.com/dotnet/project-system/issues/6784 and https://github.com/dotnet/project-system/issues/7448.

deeprobin commented 2 years ago

The app can explicitly initialize ConfigurationManager in its Main method. Adding a single to Main method should not be prohibitive, and gives the app a full control. I would avoid any magic light-up - it can help as much as it can hurt.

This feels like a sensible workaround, however it appears there's something else is broken if you go down this route:

System.Configuration.ConfigurationErrorsException
  HResult=0x80131902
  Message=Configuration system failed to initialize
  Source=System.Configuration.ConfigurationManager
  StackTrace:
   at System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey)
   at System.Configuration.ClientConfigurationSystem.PrepareClientConfigSystem(String sectionName)
   at System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)
   at System.Configuration.ConfigurationManager.GetSection(String sectionName)
   at System.Configuration.ConfigurationManager.get_AppSettings()
   at WinFormsApp1.Program.Main() in D:\Development\!Tests\WinFormsApp1\Program.cs:line 16

  This exception was originally thrown at this call stack:
    System.Configuration.ConfigurationSchemaErrors.ThrowIfErrors(bool)
    System.Configuration.BaseConfigurationRecord.ThrowIfParseErrors(System.Configuration.ConfigurationSchemaErrors)
    System.Configuration.BaseConfigurationRecord.ThrowIfInitErrors()
    System.Configuration.ClientConfigurationSystem.EnsureInit(string)

Inner Exception 1:
ConfigurationErrorsException: Unrecognized configuration section system.diagnostics. (D:\Development\!Tests\WinFormsApp1\bin\Debug\net6.0-windows\WinFormsApp1.dll.config line 3)

Here's a repro that is working for .NET Framework and is failing for .NET: WinFormsApp1.zip

I'll investigate in this.

ericstj commented 2 years ago

@deeprobin please hold off for now. We need API to fix this.

@RussKie that's because ConfigurationManager doesn't have the type registration for the section. I've added that in a prototype here; https://github.com/ericstj/runtime/tree/addDiagConfig. We'll need some more work to refine API on this and test it if we want to make the change. Removing up-for-grabs for now.

ericstj commented 2 years ago

The latter looks like the root cause of several other issues we thought were caused by the CPS

Whenever you see ConfigurationErrorsException: Unrecognized configuration section it means the app.config used a named section that wasn't registered. In .NETFramework that would happen in machine.config. In .NETCore we don't have a machine.config, instead we have an implicit one https://github.com/dotnet/runtime/blob/cd75ae127698b66821b5a2d364aa5ff7aa1a4a2a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ImplicitMachineConfigHost.cs#L75-L94 Since we don't have all the same sections supported in .NETCore many of those sections are not registered at all and produce this exception. The right fix in most cases is to remove the unused portion of the app.config. Perhaps we should improve that exception message so that it lets the developer know what to do.

RussKie commented 2 years ago

In .NETFramework that would happen in machine.config. In .NETCore we don't have a machine.config, instead we have an implicit one

Ah, now it starts clicking together. It's been many years since I had to deal with this. Thank you for clarifying this.

danmoseley commented 2 years ago

Thanks @steveharter for fixing this as a DCR. Hopefully this allows the folks above to migrate from .NET Framework to .NET 7. Commenters above, please let us know if this doesn't unblock you.

RussKie commented 2 years ago

@danmoseley please excuse my ignorance, what's DCR?

jkotas commented 2 years ago

Design Change Request. It is Microsoft acronym used to describe features that got added after feature complete date. DCRs need some level of approval, the feature teams cannot just go ahead and implement them.

RussKie commented 2 years ago

Thank you @jkotas