Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
jemiller0 opened this issue Oct 23, 2017 · 66 comments · Fixed by #73087
Closed

Comments

@jemiller0
Copy link

jemiller0 commented Oct 23, 2017

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?

@jemiller0
Copy link
Author

I looked at the source code for TraceSource in .NET Core. It appears that the code does not read configuration settings from App.config. In addition, it looks like .NET Core doesn't even have a ConsoleTraceListener. So, it looks like you can't even log to the console using TraceSource even if you programmatically configured it.

@danmoseley
Copy link
Member

@brianrob could you please answer the questions above about trace source?

When TraceSource was written, we did not have configuration API's. Now we do, and TraceSource could presumably be changed to read app.config, but @brianrob note this would presumably mean pulling System.Configuration.ConfigurationManager library into the shared framework, as it is not currently in it.

@jemiller0
Copy link
Author

I can't even get it to work by programmatically adding a TextWriterTraceListener. The following code works with .NET Framework, but, does nothing with .NET Core.

using System.Diagnostics;

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

        static void Main(string[] args)
        {
            Trace.AutoFlush = true;
            traceSource.Listeners.Add(new TextWriterTraceListener("Trace.log"));
            traceSource.TraceEvent(TraceEventType.Information, 0, "Hello, world!");
        }
    }
}

As far as I can tell, TraceSource is just flat out broken and unusable in .NET Core.

@danmoseley
Copy link
Member

OK, @brianrob or @vancem need to give guidance...

@jemiller0
Copy link
Author

So, why is this being closed? TraceSource is basically completely non-usable as it stands now.

@danmoseley
Copy link
Member

Because the Close button is too close to the Comment button :)

@danmoseley danmoseley reopened this Oct 24, 2017
@MikeDempseyFL
Copy link

I'm no expert on tracing but I am currently trying to figure out how to turn on network tracing for sockets - replacing the app.config entries for system.diagnostics with whatever is required to have the same effect in .NET Core.

While looking into this I found that TextWriterTraceListener did not seem to work as expected, but by using DefaultTraceListner I could write the output I expected.

In your example you would simply replace:
traceSource.Listeners.Add(new TextWriterTraceListener("Trace.log"));
with:
traceSource.Listeners.Add(new DefaultTraceListener());
DefaultTraceListener dtl = (DefaultTraceListener) traceSource.Listeners["Default"];
dtl.LogFileName ="Trace.log";

To avoid writing to the debug output window twice add the following before this code:
traceSource.Listeners.Clear();

My guess is that this is not really what you want to do - you are just playing around with a very simplified version of tracing as a means of trying to figure out how this works in Core. If anyone can describe how to configure network tracing in Core I'm sure there would be a lot of folks interested.

@jemiller0
Copy link
Author

Do you know if that applies to all instances of TraceSource? If yes, then, maybe it would be possible to use. Otherwise, you need the configuration file because chances are you have many instances of TraceSource and configuring them programmatically isn't feasible.

@MikeDempseyFL
Copy link

In my own test I simply used
Trace.Listeners.Add()
rather than creating my own source so the issue is generic from that point of view.

@wilson0x4d
Copy link

wilson0x4d commented Mar 17, 2018

@danmosemsft @brianrob probably this is asked a million times for a million things, but, any idea when this bug might get picked up? It's a pretty powerful and often undervalued facility that is mostly useless from an operational standpoint because "configuration management" means changing code and redeploying (or we continue sticking to .NET Framework for these features.)

@jemiller0
Copy link
Author

ConfigurationManager is there now. However, it has been lobotomized and only supports things like ConnectionStrings. And App.config doesn't work in certain contexts, like if you have a unit test assembly, it is not read. System.Diagnostics.TraceSource doesn't use App.config in .NET Core. So, you have to programmatically configure every single instance you have, making it for practical purposes unusable.

@danmoseley
Copy link
Member

@JeremyKuhne also

@brianrob
Copy link
Member

From #16332: In general, we are not expanding support for TraceSource. However, if there is someone who is interested in contributing support for System.Configuration, I think that is OK.

I am marking this up-for-grabs. Next step is to propose a design.

@JeremyKuhne
Copy link
Member

If/when somebody picks this up feel free to contact me about how to make the relevant changes to System.Configuration. It isn't very easy to navigate.

@igalnassi
Copy link

I have been using TraceSource very successfully in .Net Framework with the initial configuration done through app.config and support for runtime updates on the log levels / listeners etc. Now, I have to write my own logic to handle initial configuration from a config file? This seems wrong.

Is there a logger that can be used for both .Net Framework and .Net Core that would work the same way and have ways to configure initial state from a config file?

@vancem
Copy link
Contributor

vancem commented Feb 5, 2019

@MarcoDorantes
Copy link

I have been using TraceSource very successfully in .Net Framework with the initial configuration done through app.config and support for runtime updates on the log levels / listeners etc. Now, I have to write my own logic to handle initial configuration from a config file? This seems wrong.

Is there a logger that can be used for both .Net Framework and .Net Core that would work the same way and have ways to configure initial state from a config file?

Hi, @igalnassi .
For what it worth, and for your information, using .NET Core 3.0, the configuration at app.config section <system.diagnostics> still does not have effect on System.Diagnostics.Trace class methods (like WriteLine). But, now I am able to manually add, for example, a System.Diagnostics.TextWriterTraceListener, to the Trace.Listeners collection and now the Trace.WriteLine method worked again as traditionally expected.

Best regards,

@jemiller0
Copy link
Author

It would be great if Microsoft would add this functionality back in. The new logging API that requires dependency injection and other third party libraries is a monstrosity. You should not have to rely on third party software for something as fundamental as logging. Also, you shouldn't have to rely on overly complicated design patterns such as dependency injection. TraceSource was straight forward and easy to use. The new way isn't. Maybe it's fine for an ASP.NET application, where it originated. It is not fine for a simple console application.

@jemiller0
Copy link
Author

I should say, maybe I would be fine with configuring it programmatically. That is, if you could configure it globally. The way it stands now, you can't. You have to configure each TraceSource instance, which typically are going to be all over the place. For practical purposes, it makes it unusable. Unfortunately, Microsoft doesn't care. They would rather just push you into using the new piece of garbage.

@igalnassi
Copy link

igalnassi commented Oct 29, 2019 via email

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the Future milestone Jan 31, 2020
@maryamariyan maryamariyan added the untriaged New issue has not been triaged by the area owner label Feb 23, 2020
@tommcdon tommcdon removed the untriaged New issue has not been triaged by the area owner label Apr 6, 2020
@jbe2277
Copy link
Contributor

jbe2277 commented Jun 7, 2020

I agree with @jemiller0 's proposal to allow configuring it programmatically. This would allow us to use TraceSource when the TraceSource instance is not available to the application.

Maybe you find my Wiki page interesting: Logging with .NET Standard 2.0. It describes how to use TraceSource and to expose the TraceSource instance. This way it can be used by .NET Core and it might be combined with 3rd party logger like NLog.

@obiwanjacobi
Copy link

I feel TraceSource should be removed until it works. It is very misleading to put in code that compiles but does not work as expected. It wastes the time of the developer.
Not sure if there are more cases like these - better to include only stuff that actually works.
You could put it in a prerelease/experimental package, but it must be very clear what the deal is.

@jemiller0
Copy link
Author

@obiwanjacobi I agree and disagree. The current implementation is brain dead and I think it should be fixed. However, I'm currently using the current brain dead solution with hacks. So, I would prefer that it stay in there and not be removed. It is better to just fix it.

@RussKie
Copy link
Member

RussKie commented Jun 29, 2022

@stephentoub I'm talking about inability to configure tracing via app.config in .NET.

@MSACATS
Copy link

MSACATS commented Jun 29, 2022

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
Copy link
Member

jkotas commented Jun 30, 2022

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
Copy link

  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
Copy link
Member

ericstj commented Jul 7, 2022

@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
Copy link
Member

jkotas commented Jul 7, 2022

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
Copy link
Member

ericstj commented Jul 7, 2022

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.

@ViktorHofer ViktorHofer modified the milestones: Future, 7.0.0 Jul 13, 2022
@RussKie
Copy link
Member

RussKie commented Jul 18, 2022

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
Copy link
Member

RussKie commented Jul 18, 2022

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

@deeprobin
Copy link
Contributor

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
Copy link
Member

ericstj commented Jul 19, 2022

@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 ericstj removed the help wanted [up-for-grabs] Good issue for external contributors label Jul 19, 2022
@ericstj
Copy link
Member

ericstj commented Jul 19, 2022

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

@"<configuration>
<configSections>
<section name='appSettings' type='System.Configuration.AppSettingsSection, System.Configuration.ConfigurationManager' restartOnExternalChanges='false' requirePermission='false' />
<section name='connectionStrings' type='System.Configuration.ConnectionStringsSection, System.Configuration.ConfigurationManager' requirePermission='false' />
<section name='mscorlib' type='System.Configuration.IgnoreSection, System.Configuration.ConfigurationManager' allowLocation='false' />
<section name='runtime' type='System.Configuration.IgnoreSection, System.Configuration.ConfigurationManager' allowLocation='false' />
<section name='assemblyBinding' type='System.Configuration.IgnoreSection, System.Configuration.ConfigurationManager' allowLocation='false' />
<section name='satelliteassemblies' type='System.Configuration.IgnoreSection, System.Configuration.ConfigurationManager' allowLocation='false' />
<section name='startup' type='System.Configuration.IgnoreSection, System.Configuration.ConfigurationManager' allowLocation='false' />
</configSections>
<configProtectedData defaultProvider='RsaProtectedConfigurationProvider'>
<providers>
<add name = 'RsaProtectedConfigurationProvider' type='System.Configuration.RsaProtectedConfigurationProvider, System.Configuration.ConfigurationManager' description='Uses RsaCryptoServiceProvider to encrypt and decrypt' keyContainerName='NetFrameworkConfigurationKey' cspProviderName='' useMachineContainer='true' useOAEP='false' />
<add name = 'DataProtectionConfigurationProvider' type='System.Configuration.DpapiProtectedConfigurationProvider, System.Configuration.ConfigurationManager' description='Uses CryptProtectData and CryptUnProtectData Windows APIs to encrypt and decrypt' useMachineProtection='true' keyEntropy='' />
</providers>
</configProtectedData>
<connectionStrings>
<add name = 'LocalSqlServer' connectionString='data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true' providerName='System.Data.SqlClient' />
</connectionStrings>
</configuration>";

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
Copy link
Member

RussKie commented Jul 20, 2022

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
Copy link
Member

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
Copy link
Member

RussKie commented Aug 11, 2022

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

@jkotas
Copy link
Member

jkotas commented Aug 11, 2022

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
Copy link
Member

RussKie commented Aug 11, 2022

Thank you @jkotas

@ghost ghost locked as resolved and limited conversation to collaborators Sep 11, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.