Project: Grafana Pyroscope (Observability and Analysis / Observability)
File: dotnet.md

title: ".NET" menuTitle: ".NET" description: "Instrumenting .NET applications for continuous profiling." weight: 40 aliases: /docs/phlare/latest/configure-client/language-sdks/dotnet/ Our .NET Profiler is a powerful tool designed to enhance the performance analysis and optimization of .NET applications. It seamlessly integrates with Pyroscope, offering real-time insights into the resource usage and bottlenecks within your .NET codebase. This integration empowers developers to pinpoint inefficiencies, improve application speed, and ensure resource-efficient operation. {{< admonition type="note" >}} Refer to for a list of profile types supported by each language. {{< /admonition >}} The .NET Profiler supports the following profiling types: CPU Wall time Allocations Lock contention Exceptions Our .NET profiler works with the following .NET versions: .NET 6 .NET 7 .NET 8 To capture and analyze profiling data, you need either a hosted Pyroscope OSS server or a hosted (requires a free Grafana Cloud account). The Pyroscope server can be a local server for development or a remote server for production use. Obtain `Pyroscope.Profiler.Native.so` and `Pyroscope.Linux.ApiWrapper.x64.so` from the : ```bash curl -s -L https://github.com/grafana/pyroscope-dotnet/releases/download/v0.8.14-pyroscope/pyroscope.0.8.14-glibc-x86_64.tar.gz | tar xvz -C . ``` Or copy them from the . We have `glibc` and `musl` versions: ```dockerfile COPY --from=pyroscope/pyroscope-dotnet:0.8.14-glibc /Pyroscope.Profiler.Native.so ./Pyroscope.Profiler.Native.so COPY --from=pyroscope/pyroscope-dotnet:0.8.14-glibc /Pyroscope.Linux.ApiWrapper.x64.so ./Pyroscope.Linux.ApiWrapper.x64.so ```` Set the following required environment variables to enable profiler ```shell PYROSCOPEAPPLICATIONNAME=rideshare.dotnet.app PYROSCOPESERVERADDRESS=http://localhost:4040 PYROSCOPEPROFILINGENABLED=1 CORECLRENABLEPROFILING=1 CORECLR_PROFILER={BD1A650D-AC5D-4896-B64F-D6FA25D6B26A} CORECLRPROFILERPATH=Pyroscope.Profiler.Native.so LD_PRELOAD=Pyroscope.Linux.ApiWrapper.x64.so ``` {{< admonition type="note" >}} Since .NET version 8 the environment variable `DOTNETEnableDiagnostics=0` (or its legacy equivalent `COMPlusEnableDiagnostics=0`) will also disable the profiler. In order to get the previous behaviour (allowing profiling, but switch off IPC and Debugging) the following environment variables should be set instead: ```shell DOTNET_EnableDiagnostics=1 DOTNETEnableDiagnosticsIPC=0 DOTNETEnableDiagnosticsDebugger=0 DOTNETEnableDiagnosticsProfiler=1 ``` {{< /admonition >}} With a managed helper you can interact with the Pyroscope profiler from .NET runtime. You can add labels, turn on/off profiling types, and more. To use it, first, add the Pyroscope dependency: ```shell dotnet add package Pyroscope ``` &nbsp; You can add labels to the profiling data to filter the data in the UI. Common labels include: `hostname` `region` `team` `api_endpoint` Create a `LabelSet` and wrap a piece of code with `Pyroscope.LabelsWrapper`. ```cs var labels = Pyroscope.LabelSet.Empty.BuildUpon() .Add("key1", "value1") .Build(); Pyroscope.LabelsWrapper.Do(labels, () => { SlowCode(); }); ``` Labels can be nested. For nesting LabelSets use `LabelSet.BuildUpon` on non-empty set. ```cs var labels = Pyroscope.LabelSet.Empty.BuildUpon() .Add("key1", "value1") .Build(); Pyroscope.LabelsWrapper.Do(labels, () => { var labels2 = labels.BuildUpon() .Add("key2", "value2") .Build(); Pyroscope.LabelsWrapper.Do(labels2, () => { SlowCode(); }); FastCode(); }); ``` It is possible to dynamically enable/disable specific profiling types. Profiling types have to be configured prior. ```cs // Enables or disables CPU/wall profiling dynamically. // This function works in conjunction with the PYROSCOPEPROFILINGCPU_ENABLED and // PYROSCOPEPROFILINGWALLTIME_ENABLED environment variables. If CPU/wall profiling is not // configured, this function will have no effect. Pyroscope.Profiler.Instance.SetCPUTrackingEnabled(enabled); // Enables or disables allocation profiling dynamically. // This function works in conjunction with the PYROSCOPEPROFILINGALLOCATION_ENABLED environment

// If allocation profiling is not configured, this function will have no effect. Pyroscope.Profiler.Instance.SetAllocationTrackingEnabled(enabled); // Enables or disables contention profiling dynamically. // This function works in conjunction with the PYROSCOPEPROFILINGLOCK_ENABLED environment variable. // If contention profiling is not configured, this function will have no effect. Pyroscope.Profiler.Instance.SetContentionTrackingEnabled(enabled); // Enables or disables exception profiling dynamically. // This function works in conjunction with the PYROSCOPEPROFILINGEXCEPTION_ENABLED environment variable. // If exception profiling is not configured, this function will have no effect. Pyroscope.Profiler.Instance.SetExceptionTrackingEnabled(enabled); ``` It is possible to dynamically change authorization credentials: ```cs // Set Basic authorization username and password. Clears any previous authorization credentials. Pyroscope.Profiler.Instance.SetBasicAuth(basicAuthUser, BasicAuthPassword); ``` Here is a simple exposing these APIs as HTTP endpoints. | ENVIRONMENT VARIABLE | Type | DESCRIPTION | |-|--|--| | PYROSCOPEPROFILINGLOG_DIR | String | Sets the directory for .NET Profiler logs. Defaults to /var/log/pyroscope/. | | PYROSCOPE_LABELS | String | Static labels to apply to an uploaded profile. Must be a list of key:value separated by commas such as: layer:api or team:intake. | | PYROSCOPESERVERADDRESS | String | Address of the Pyroscope Server | | PYROSCOPEPROFILINGENABLED | Boolean | If set to true, enables the .NET Profiler. Defaults to false. | | PYROSCOPEPROFILINGWALLTIME_ENABLED | Boolean | If set to false, disables the Wall time profiling. Defaults to false. | | PYROSCOPEPROFILINGCPU_ENABLED | Boolean | If set to false, disables the CPU profiling. Defaults to true. | | PYROSCOPEPROFILINGEXCEPTION_ENABLED | Boolean | If set to true, enables the Exceptions profiling. Defaults to false. | | PYROSCOPEPROFILINGALLOCATION_ENABLED | Boolean | If set to true, enables the Allocations profiling. Defaults to false. | | PYROSCOPEPROFILINGLOCK_ENABLED | Boolean | If set to true, enables the Lock Contention profiling. Defaults to false. | | PYROSCOPEBASICAUTH_USER | String | For HTTP Basic Authentication, use this to send profiles to authenticated server, for example Grafana Cloud | | PYROSCOPEBASICAUTH_PASSWORD | String | For HTTP Basic Authentication, use this to send profiles to authenticated server, for example Grafana Cloud | | PYROSCOPETENANTID | String | Only needed if using multi-tenancy in Pyroscope. | To send profiling data from your .NET application, configure your environment for either Pyroscope OSS or Grafana Cloud Profiles using the following steps: ```bash export CORECLRENABLEPROFILING=1 export CORECLR_PROFILER={BD1A650D-AC5D-4896-B64F-D6FA25D6B26A} export CORECLRPROFILERPATH=/dotnet/Pyroscope.Profiler.Native.so export LD_PRELOAD=/dotnet/Pyroscope.Linux.ApiWrapper.x64.so export PYROSCOPEPROFILINGENABLED=1 export PYROSCOPEAPPLICATIONNAME=example.dotnet.app export PYROSCOPESERVERADDRESS=<URL> export PYROSCOPEBASICAUTH_USER=<User> export PYROSCOPEBASICAUTH_PASSWORD=<Password> ``` To configure the .NET SDK to send data to Grafana Cloud Profiles or Pyroscope, replace the `<URL>` placeholder with the appropriate server URL. This could be the Grafana Cloud URL or your own custom Pyroscope server URL. If you need to send data to Grafana Cloud, you'll have to configure HTTP Basic authentication. Replace `<User>` with your Grafana Cloud stack user and `<Password>` with your Grafana Cloud API key. If your open source Pyroscope server has multi-tenancy enabled, you'll need to specify a tenant ID. Replace `<TenantID>` with your Pyroscope tenant ID.