Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • Guided learning
      Receive custom learning paths powered by our AI assistant.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

Tracing .NET Core applications

<p>&nbsp;</p> <quillbot-extension-portal></quillbot-extension-portal>

December 23, 2019
Tom Deseyn
Related topics:
.NET
Related products:
Developer Toolset

    In this article, we’ll look at different ways of collecting and inspecting events from the .NET Core runtime and base class library (BCL).

    EventListener

    The EventListener class allows us to get events of the running application. Let’s learn how to use it with an example application. Our application performs an HTTP get and prints the length of the received response.

    using var httpClient = new HttpClient();
    string response = await httpClient.GetStringAsync("http://redhatloves.net/");
    Console.WriteLine($"Received response with length {response.Length}");
    

    The .NET Core base classes (like HttpClient) record events using the EventSource class. Each EventSource has a name. EventSources are created when we are using some functionality. To know what sources are available, we’ll create an EventListener and override OnEventSourceCreated.

    sealed class EventSourceCreatedListener : EventListener
    {
        protected override void OnEventSourceCreated(EventSource eventSource)
        {
            base.OnEventSourceCreated(eventSource);
            Console.WriteLine($"New event source: {eventSource.Name}");
        }
    }
    

    When we instantiate this class, the base constructor will call OnEventSourceCreated for all existing sources. When new sources are added (for example, when we first use HttpClient) OnEventSourceCreated will be called for them, too.

    We add an instance to our application:

    using var eventSourceListener = new EventSourceCreatedListener();
    using var httpClient = new HttpClient();
    ...
    

    When we run the application this is this output:

    New event source: Microsoft-Windows-DotNETRuntime
    New event source: System.Runtime
    New event source: Microsoft-System-Net-Http
    New event source: System.Diagnostics.Eventing.FrameworkEventSource
    New event source: Microsoft-Diagnostics-DiagnosticSource
    New event source: Microsoft-System-Net-Sockets
    New event source: Microsoft-System-Net-NameResolution
    New event source: System.Threading.Tasks.TplEventSource
    New event source: System.Buffers.ArrayPoolEventSource
    New event source: Microsoft-System-Net-Security
    New event source: System.Collections.Concurrent.ConcurrentCollectionsEventSource
    

    The Microsoft-Windows-DotNETRuntime and System.Runtime are present when we create the EventSourceCreatedListener. The other event sources are created when we use the HttpClient. There is an event source for HTTP (Microsoft-System-Net-Http), we also see an event source for the name resolution (Microsoft-System-Net-NameResolution), security (Microsoft-System-Net-Security) and sockets (Microsoft-System-Net-Sockets). Let’s print events from the HTTP source to Console with another listener.

    We create an EventListener that prints events for a specific EventSource:

    sealed class EventSourceListener : EventListener
    {
        private readonly string _eventSourceName;
        private readonly StringBuilder _messageBuilder = new StringBuilder();
    
        public EventSourceListener(string name)
        {
            _eventSourceName = name;
        }
    
        protected override void OnEventSourceCreated(EventSource eventSource)
        {
            base.OnEventSourceCreated(eventSource);
    
            if (eventSource.Name == _eventSourceName)
            {
                EnableEvents(eventSource, EventLevel.LogAlways, EventKeywords.All);
            }
        }
    
        protected override void OnEventWritten(EventWrittenEventArgs eventData)
        {
            base.OnEventWritten(eventData);
    
            string message;
            lock (_messageBuilder)
            {
                _messageBuilder.Append("<- Event ");
                _messageBuilder.Append(eventData.EventSource.Name);
                _messageBuilder.Append(" - ");
                _messageBuilder.Append(eventData.EventName);
                _messageBuilder.Append(" : ");
                _messageBuilder.AppendJoin(',', eventData.Payload);
                _messageBuilder.AppendLine(" ->");
                message = _messageBuilder.ToString();
                _messageBuilder.Clear();
            }
            Console.WriteLine(message);
        }
    }
    

    In OnEventSourceCreated we enable the receiving of events for the specific source. We’re not filtering for a specific log level (EventLevel.LogAlways) or specific keywords (EventKeywords.All). In OnEventWritten we print the event to the Console.

    Now we add an instance to our program for the Microsoft-System-Net-Http EventSource:

    using var httpEventListener = new EventSourceListener("Microsoft-System-Net-Http");
    ...
    

    When we run our program again we get a lot of events from the HTTP EventSource. For example, we see that our request gets redirected and we see information about the SSL negotiation.

    …
    Event Microsoft-System-Net-Http - HandlerMessage : 55530882,6044116,0,SendAsyncCore,Received response: StatusCode: 301, ReasonPhrase: 'Moved Permanently'
    …
    <- Event Microsoft-System-Net-Http - HandlerMessage : 16294043,41622463,0,TraceConnection,HttpConnection(HttpConnectionPool https://developers.redhat.com:443). SslProtocol:Tls12, NegotiatedApplicationProtocol:, NegotiatedCipherSuite:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherAlgorithm:Aes256, CipherStrength:256, HashAlgorithm:None, HashStrength:0, KeyExchangeAlgorithm:DiffieHellman, KeyExchangeStrength:0, LocalCertificate:, RemoteCertificate:[Subject]
      CN=openshift.redhat.com, O=Red Hat, L=Raleigh, S=North Carolina, C=US
    
    [Issuer]
      CN=GeoTrust RSA CA 2018, OU=www.digicert.com, O=DigiCert Inc, C=US
    …
    

    The EventSource named Microsoft-Windows-DotNETRuntime provides events from the runtime like the Garbage Collector (GC), just in time compiler (JIT) and ThreadPool.

    dotnet-trace

    dotnet-trace is a new global tool with .NET Core 3.0. With dotnet-trace we can create a trace from a running application.

    To install dotnet-trace run:

    dotnet tool install --global dotnet-trace
    

    A trace can be created with the following command:

    dotnet trace collect -p <pid> [--buffersize 256] [-o <outputpath>] [--providers <providerlist>] [--profile <profilename>] [--format NetTrace]
    

    We need to specify the <pid> of the program that we want to trace. Optionally, we can specify a buffer size (default: 256MB). The output path is the filename for the trace file. providers and profile allow us to specify the events we are interested in. A profile is a pre-defined number of providers. You can see the available profiles using the list-profiles subcommand:

    $ dotnet trace list-profiles
    cpu-sampling     - Useful for tracking CPU usage and general .NET runtime information. This is the default option if no profile or providers are specified.
    gc-verbose       - Tracks GC collections and samples object allocations.
    gc-collect       - Tracks GC collections only at very low overhead.
    

    As indicated in the output, if you don’t specify providers and a profile, the cpu-samping profile is used by default.

    Multiple providers can be specified using a comma to separate them. By default, events will be captured for any keyword and level verbose. We can change this by appending a :[<keyword_hex_nr>]:<loglevel_nr>] suffix to the provider name. The keyword_hex_nr may be omitted to indicate all keywords. For example, to trace the Microsoft-System-Net-Http EventSource at EventLevel.Informational, we can specify: --providers Microsoft-System-Net-Http::4. An asterisk may be used to trace all events (i.e. --providers '*').

    Some providers can be configured with additional key-value pairs. These are added to the provider name :[key1=value1][;key2=value2]. When the EventSource has associated EventCounters, the report interval can be set using the EventCounterIntervalSec key.

    The default format (nettrace) is meant for the Windows PerfView tool. If you are doing performance analysis, you can use the speedscope format which can be opened on any platform using the https://www.speedscope.app/ website, or offline using the npm speedscopeapp package. When you are logging in the speedscope format, the trace is first captured into the nettrace format and then converted into the speedscope format. This conversion can also be done on the command line using dotnet trace convert.

    For example, this is how you can trace a .NET Core application named web for the default profile (cpu-sampling) and output a speedscope file.

    $ dotnet trace collect -p $(pidof web) --format speedscope
    
    Provider Name                           Keywords            Level               Enabled By
    Microsoft-System-Net-Http               0xFFFFFFFFFFFFFFFF  Verbose(5)          --providers
    
    Process        : /tmp/web/bin/Debug/netcoreapp3.0/web
    Output File    : /tmp/trace/trace.nettrace
    
    [00:00:00:08]   Recording trace 2.3301   (MB)
    Press  or  to exit...
    
    Trace completed.
    Writing:        /tmp/trace/trace.speedscope.json
    Conversion complete
    

    As indicated by the tool, you can stop tracing using Ctrl+C or Enter. For more information about using speedscope, check out Adam Sitnick’s blog post.

    EventPipe environment variables

    dotnet-trace enables the trace output when an application is running. By using environment variables we can also enable tracing from the application start. This also allows us to trace a tree of processes.

    Name Description
    COMPlus_EnableEventPipe Enable/disable event pipe.
    COMPlus_EventPipeOutputPath The full path excluding filename for the trace file.
    COMPlus_EventPipeCircularMB Circular buffer size in megabytes (default: 1024 MB).
    COMPlus_EventPipeConfig Configuration for EventPipe.

    The EventPipeConfig has the same format as the dotnet trace providers argument. The EventPipeOutputPath refers to a folder. This allows multiple processes to write a trace file by including the process pid in the filename. The EventPipe uses the nettrace format.

    For example, to capture all events from a console application, we can run:

    COMPlus_EnableEventPipe=1 COMPlus_EventPipeConfig=* dotnet console.dll
    

    TraceEvent library

    If you want to write tools that process these trace files, you can use the Microsoft.Diagnostics.Tracing.TraceEvent library. This library also allows us to read the nettrace files on Linux:

    # add the package:
    #      dotnet add package Microsoft.Diagnostics.Tracing.TraceEvent
    
    using System;
    using Microsoft.Diagnostics.Tracing;
    using Microsoft.Diagnostics.Tracing.Etlx;
    using Microsoft.Diagnostics.Tracing.Parsers;
    
    namespace console
    {
        class Program
        {
    
            static void Main(string[] args)
            {
                if (args.Length < 1)
                {
                    Console.WriteLine("You must specify a nettrace file.");
                    return;
                }
    
                using (var traceLog = new TraceLog(TraceLog.CreateFromEventPipeDataFile(args[0])))
                {
                    var traceSource = traceLog.Events.GetSource();
    
                    traceSource.AllEvents += delegate (TraceEvent data) {
                        Console.WriteLine("{0}", data);
                    };
    
                    traceSource.Process();
    
               }
            }
        }
    }
    

    Microsoft.Extensions.Logging

    ASP.NET Core uses the Microsoft.Extensions.Logging package for logging. You can find more information about this in Logging in .NET Core and ASP.NET Core. Multiple sinks can be configured as Logging providers. The EventSource logger provider (which is included by default) allows to include the ASP.NET Core log output in the trace output. The EventSource section describes how to use the Microsoft-Extensions-Logging EventSource with dotnet-trace, and control the format, and log level of the individual loggers.

    dotnet-counters

    dotnet-counters is a new global tool with .NET Core 3.0. With dotnet-counters we can live monitor EventCounters of a running .NET Core application.
    To install counters run:

    dotnet tool install --global dotnet-counters
    

    Monitoring can be performed with the following command.

    dotnet-counters monitor -p <pid> [--refreshInterval <intervalSec>] [<counter_list>]
    

    When no counter list is specified, a default set of .NET runtime counters are shown information about the GC, ThreadPool, lock contention, active timers, and exception count.

    <counter_list> can be specified as a space separated list of provider_name[:counter_name]. When the <counter_name> is omitted, all counters of the provider are included.

    Conclusion

    In this article, you’ve learned how you can capture events from the .NET Core runtime and BCL using EventListener, dotnet-trace, and EventPipe environment variables. Using the LoggingEventSource we can including events from Microsoft.Extensions.Logging (like ASP.NET Core). To view and analyze these events you can use PerfView on windows, the cross-platform speedscope, or build your own cross-platform tools using Microsoft.Diagnostics.Tracing.TraceEvent. With dotnet-counters we can get a live view of EventCounters.

    Last updated: February 13, 2024

    Recent Posts

    • Every layer counts: Defense in depth for AI agents with Red Hat AI

    • Fun in the RUN instruction: Why container builds with distroless images can surprise you

    • Trusted software factory: Building trust in the agentic AI era

    • Build a zero trust AI pipeline with OpenShift and RHEL CVMs

    • Red Hat Hardened Images: Top 5 benefits for software developers

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Chat Support

    Please log in with your Red Hat account to access chat support.