Search code examples
asp.netazurewcfazure-application-insightstrace

System.Diagnostics.TraceSource not sending data to Application Insights


I have application insights set up in my project, which works fine, it sends data to Azure without having any issue, now I am trying to get some trace logs into telemetry sent to azure using System.Diagnostics.SourceTrace which is implemented in an internal nuget package that is referenced in the Webhost application (this nuget package doesn't contain a reference to app insights), the thing is... for some reason, it's just not reaching that code, well, it does and it doesn't at the same time, when I am debugging in the output window I can see there is an event created when it hits the Sytem.Diagnostics.TraceEvent() method, but it shows up like this

Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Message","time":"2021-09-01T22:43:18.7652108Z","tags":{"ai.cloud.roleInstance":

Which makes me think that for some reason the telemetryclient is losing the reference to the instrumentation key or something like that, and I am not sure how to fix this, because it's only happening there.

EDIT: Here's how we set up the trace sources, this code lives in the web.config file of the webhost application, which references to another project that is referencing a nuget package where the logging happens.

        <source name="MySource" switchValue="Error, Information, Warning">
          <listeners>
            <add name="AppInsights"  type="Microsoft.ApplicationInsights.TraceListener.ApplicationInsightsTraceListener, Microsoft.ApplicationInsights.TraceListener" />
          </listeners>
        </source>

I debugged the class where logging happens, when I evaluate the telemetryconfiguration object, it's missing the instrumentation key (which is strange, because most telemetry works fine)

here's the code where we setup the telemetryClient:

public void Initialize()
        {
            if (_initialized) return;
            lock (_initializationLock)
            {
                if (_initialized) return;
                var iKey = ApplicationInsightsConfiguration.InstrumentationKey;

                //Call this even if ikey is null or empty
                MonitoringSettings = new Settings(iKey);

                //If we don't have a key we can't do anything
                if (string.IsNullOrEmpty(iKey))
                {
                    Logger.Info($"No Application Insights telemetry key is available (Production flag: {SystemSettings.IsProductionServer})");
                    TelemetryConfiguration.Active.DisableTelemetry = true;
                    return;
                }

                //Set telemetry key
                TelemetryConfiguration.Active.InstrumentationKey = iKey;

                //Set up custom telemetry initializers
                //We need to initialize it before we send the non-prod custom event, so that the event will contain all required info
                SetUpTelemetryInitializers();

                //Disable telemetry reporting if it is not production instance
                //If overridden in web.ApplicationInsightsConfiguration explicitly, allow telemetry reporting
                if (ApplicationInsightsConfiguration.ForceSendTelemetry)
                {
                    Client.TrackEvent("ForceSendTelemetry enabled.");
                }

                //Set up custom telemetry filtration
                SetUpTelemetryProcessors();

                //send the license information if it has not already been sent for this Middleware instance startup
                SendLicenseConfiguration();

                //Track the event
                Client.TrackEvent("Telemetry Opt In", MonitoringSettings.GetAsDictionary());

                _initialized = true;
            }
        }

It's worth mentioning that, if I add the telemetry key into the app config, the tracelistener works... For some reason, when we add it programatically it's missing the reference to the original telemetryconfiguration object with the correct instrumentation key, and I am thinking that is happening because I am creating a new TraceSource object with the listener for appinsights which includes a new instance of configuration.


Solution

  • Thank you Jiayao. Posting your suggestions as an answer to help other community members.

    properties that enable applications to trace the execution of code and associate trace messages with their source.

    public class TraceSource
    

    Below code helps you in understanding the Tracesource Class and demonstrates switch and filter usage.

    // The following configuration file can be used with this sample.
    // When using a configuration file #define ConfigFile.
    //            <source name="TraceTest" switchName="SourceSwitch" switchType="System.Diagnostics.SourceSwitch" >
    //                    <add name="console" type="System.Diagnostics.ConsoleTraceListener" initializeData="false" />
    //                    <remove name ="Default" />
    //            <!-- You can set the level at which tracing is to occur -->
    //            <add name="SourceSwitch" value="Warning" />
    //            <!-- You can turn tracing off -->
    //            <!--add name="SourceSwitch" value="Off" -->
    //        <trace autoflush="true" indentsize="4"></trace>
    #define TRACE
    //#define ConfigFile
    
    using System;
    using System.Collections;
    using System.Diagnostics;
    using System.Reflection;
    using System.IO;
    using System.Security.Permissions;
    
    namespace Testing
    {
        class TraceTest
        {
            // Initialize the trace source.
            static TraceSource ts = new TraceSource("TraceTest");
            [SwitchAttribute("SourceSwitch", typeof(SourceSwitch))]
            static void Main()
            {
                try
                {
                    // Initialize trace switches.
    #if(!ConfigFile)
                    SourceSwitch sourceSwitch = new SourceSwitch("SourceSwitch", "Verbose");
                    ts.Switch = sourceSwitch;
                    int idxConsole = ts.Listeners.Add(new System.Diagnostics.ConsoleTraceListener());
                    ts.Listeners[idxConsole].Name = "console";
    #endif
                    DisplayProperties(ts);
                    ts.Listeners["console"].TraceOutputOptions |= TraceOptions.Callstack;
                    ts.TraceEvent(TraceEventType.Warning, 1);
                    ts.Listeners["console"].TraceOutputOptions = TraceOptions.DateTime;
                    // Issue file not found message as a warning.
                    ts.TraceEvent(TraceEventType.Warning, 2, "File Test not found");
                    // Issue file not found message as a verbose event using a formatted string.
                    ts.TraceEvent(TraceEventType.Verbose, 3, "File {0} not found.", "test");
                    // Issue file not found message as information.
                    ts.TraceInformation("File {0} not found.", "test");
                    ts.Listeners["console"].TraceOutputOptions |= TraceOptions.LogicalOperationStack;
                    // Issue file not found message as an error event.
                    ts.TraceEvent(TraceEventType.Error, 4, "File {0} not found.", "test");
                    // Test the filter on the ConsoleTraceListener.
                    ts.Listeners["console"].Filter = new SourceFilter("No match");
                    ts.TraceData(TraceEventType.Error, 5,
                        "SourceFilter should reject this message for the console trace listener.");
                    ts.Listeners["console"].Filter = new SourceFilter("TraceTest");
                    ts.TraceData(TraceEventType.Error, 6,
                        "SourceFilter should let this message through on the console trace listener.");
                    ts.Listeners["console"].Filter = null;
                    // Use the TraceData method.
                    ts.TraceData(TraceEventType.Warning, 7, new object());
                    ts.TraceData(TraceEventType.Warning, 8, new object[] { "Message 1", "Message 2" });
                    // Activity tests.
                    ts.TraceEvent(TraceEventType.Start, 9, "Will not appear until the switch is changed.");
                    ts.Switch.Level = SourceLevels.ActivityTracing | SourceLevels.Critical;
                    ts.TraceEvent(TraceEventType.Suspend, 10, "Switch includes ActivityTracing, this should appear");
                    ts.TraceEvent(TraceEventType.Critical, 11, "Switch includes Critical, this should appear");
                    ts.Flush();
                    ts.Close();
                    Console.WriteLine("Press any key to exit.");
                    Console.Read();
                }
                catch (Exception e)
                {
                    // Catch any unexpected exception.
                    Console.WriteLine("Unexpected exception: " + e.ToString());
                    Console.Read();
                }
            }
            public static void DisplayProperties(TraceSource ts)
            {
                Console.WriteLine("TraceSource name = " + ts.Name);
                Console.WriteLine("TraceSource switch level = " + ts.Switch.Level);
                Console.WriteLine("TraceSource switch = " + ts.Switch.DisplayName);
                SwitchAttribute[] switches = SwitchAttribute.GetAll(typeof(TraceTest).Assembly);
                for (int i = 0; i < switches.Length; i++)
                {
                    Console.WriteLine("Switch name = " + switches[i].SwitchName);
                    Console.WriteLine("Switch type = " + switches[i].SwitchType);
                }
    #if(ConfigFile)
                // Get the custom attributes for the TraceSource.
                Console.WriteLine("Number of custom trace source attributes = "
                    + ts.Attributes.Count);
                foreach (DictionaryEntry de in ts.Attributes)
                    Console.WriteLine("Custom trace source attribute = "
                        + de.Key + "  " + de.Value);
                // Get the custom attributes for the trace source switch.
                foreach (DictionaryEntry de in ts.Switch.Attributes)
                    Console.WriteLine("Custom switch attribute = "
                        + de.Key + "  " + de.Value);
    #endif
                Console.WriteLine("Number of listeners = " + ts.Listeners.Count);
                foreach (TraceListener traceListener in ts.Listeners)
                {
                    Console.Write("TraceListener: " + traceListener.Name + "\t");
                    // The following output can be used to update the configuration file.
                    Console.WriteLine("AssemblyQualifiedName = " +
                        (traceListener.GetType().AssemblyQualifiedName));
                }
            }
        }
    }
    

    For further information verify TraceSource Class.