How to measure a method's performance in C# applications
Tags: dotnet, csharp, performance, benchmarking
Published: 2020-01-12 17:55:07

This How-To describes how to make the exact performance measurements of the *public* methods for *public* classes in your C# code and in the codes of the utilized libraries.

> [Unchase Fluent Performance Meter](https://github.com/unchase/Unchase.FluentPerformanceMeter) is an open-source and cross-platform .Net Standard 2.0 library that is designed for the method's performance measurement.

The library can be used in .NET Core and .NET Framework applications that support *.Net Standard 2.0*, and it allows for:

* [**Making the exact performance measurements**](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#examples-of%C2%A0usage) of the ***public* methods** for ***public* classes** in your code and [in the codes of the utilized libraries](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#measuring-the-performance-of-an-external-library%C2%A0method) (and recording the exact start and end times of the measurements);
* [**Adding Custom Data**](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-custom-data-and-splitting-into%C2%A0steps) to the measurement results. For example, the input parameters' values of the method and the result, or context of the method's execution, or *corellationId*, that can be used for linking several measurements of the methods' performance;
* [**Splitting the measurement**](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-custom-data-and-splitting-into%C2%A0steps) of the method performance **into separate steps** and storing personal data for each step. In addition, you can set the minimum execution time which will be used as a reference for saving the step into the measurement (if a step is completed faster, it will not be saved);
* [**Excluding individual parts of the code**](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#excluding-the-measurement) from the performance measurement (for example, calls to the individual methods the execution time of which does not have to be saved for the measurement);
* [**Adding custom Commands**](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-custom-commands-and%C2%A0actions), which are guaranteed **to be executed immediately at the end of the method's performance measurement** (for example, to add post-processing of the obtained results, such as logging or writing data to the storage);
* [**Adding custom Exception Handler**](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-exception-handlers) for the code executed in the context of the method's performance measurement (can be used for all measurements, or for each measurement individually);
* [**Setting the Cache Time**](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#setting-the-cache%C2%A0time) for storing the method's performance measurement results after which they will be deleted;
* [**Adding the info about the method's Caller**](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-the-info-about-the-caller-and-call-place-origin-and-aborting-the-measurement) to the measurement results which is possible by means of *IHttpContextAccesor* or specifying the Caller in the code (for example, you can specify the name of the external service that called the method);
* [**Adding the info about the place**](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-the-info-about-the-caller-and-call-place-origin-and-aborting-the-measurement) where the performance measurement was started to the measurement results (referencing the file name and the exact line number where the method was called);
* [**Aborting the method's performance measurement before the process is finished on its own.**](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-the-info-about-the-caller-and-call-place-origin-and-aborting-the-measurement)

The data obtained as a result of the method's performance measurement can be used to analyze the performance of the application (of its individual parts: both internal - native code, and external - the code of used libraries) and displayed in a graphical form convenient for you.

---

## **Table of content**
1. [Getting started](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#getting-started)
2. [Examples of usage](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#examples-of%C2%A0usage)

* [Method's performance measurement](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#methods-performance-measurement)
  * [Using DI to Get Performance measurement results (v2.1.0)](#using-di-to-get-performance-measurement-results)
* [Method's performance measurement with `DiagnosticSource` (v1.1.0)](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#methods-performance-measurement-with-raw-diagnosticsource-endraw-)
* [Method's performance measurement with `WatchingPerformanceAttribute` attribute (v2.0.0)](#methods-performance-measurement-with-raw-watchingperformanceattribute-endraw-attribute)
* [Measuring the performance of an external library method](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#measuring-the-performance-of-an-external-library%C2%A0method)
* [Adding Custom Data and splitting into Steps](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-custom-data-and-splitting-into%C2%A0steps)
* [Excluding the measurement](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#excluding-the-measurement)
* [Adding custom Commands and Actions](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-custom-commands-and%C2%A0actions)
* [Adding Exception Handlers](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-exception-handlers)
* [Setting the Cache Time](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#setting-the-cache%C2%A0time)
* [Adding the info about the caller and call place origin (and aborting the measurement)](https://dev.to/unchase/how-to-measure-a-method-s-performance-in-c-applications-3jji#adding-the-info-about-the-caller-and-call-place-origin-and-aborting-the-measurement)

## **Getting started**
To use the library, install [NuGet package](https://www.nuget.org/packages/Unchase.FluentPerformanceMeter/) into your project:
**Manually with the NuGet Package Manager:**
```
Install-Package Unchase.FluentPerformanceMeter
```
**Using the .NET CLI:**
```
dotnet add package Unchase.FluentPerformanceMeter --version {version}
```
> Where {version} is the version of the package you want to install. For example, `dotnet add package Unchase.FluentPerformanceMeter --version 1.0.0`

## **Examples of usage**
### **Method's performance measurement**
The following simple library usage example (without configuration and additional settings) meant to demonstrate how to measure a method's performance (Action) `SimpleWatchingMethodStart` for the Controller `PerformanceMeterController` in *Asp.Net Core 2.2 WebAPI* application. You can use the extension method `.WatchingMethod().Start()` or `.StartWatching()` for this.

Since v1.0.5, you can also use `.WatchingMethod().Start(SimpleWatchingMethodStart)` or `.StartWatching(SimpleWatchingMethodStart)` with the method name.

```csharp
/// <summary>
/// Test GET method with simple performance watching.
/// </summary>
[HttpGet("SimpleWatchingMethodStart")]
public ActionResult SimpleWatchingMethodStart()
{	
    // for C# 8 you can use:
    //using var pm = PerformanceMeter<PerformanceMeterController>.StartWatching();

    using (PerformanceMeter<PerformanceMeterController>.WatchingMethod().Start())
    {
        // put your code with some logic here

        return Ok();
    }
}
```

To get the performance measurements results of public methods of the controller class `PerformanceMeterController` you can call the following method:

```csharp
/// <summary>
/// Get methods performance info for this controller.
/// </summary>
/// <returns>Returns methods performance info.</returns>
[HttpGet("GetPerformanceInfo")]
[IgnoreMethodPerformance]
public ActionResult<IPerformanceInfo> GetPerformanceInfo()
{
    return Ok(PerformanceMeter<PerformanceMeterController>.PerformanceInfo);
}
```

> The attribute `IgnoreMethodPerformance` is intended so that the method marked by it is not taken into account when measuring performance.

After calling the method `SimpleWatchingMethodStart` and calling `GetPerformanceInfo` we receive:

```json
{
  "methodCalls": [
    {
      "methodName": "SimpleWatchingMethodStart",
      "elapsed": "00:00:00.0016350",
      "caller": "unknown",
      "startTime": "2019-12-06T10:27:27.3385385Z",
      "endTime": "2019-12-06T10:27:27.3401735Z",
      "customData": {},
      "steps": []
    }
  ],
  "totalActivity": [
    {
      "methodName": "SimpleWatchingMethodStart",
      "callsCount": 1
    }
  ],
  "currentActivity": [
    {
      "methodName": "SimpleWatchingMethodStart",
      "callsCount": 0
    }
  ],
  "uptimeSince": "2019-12-06T10:27:27.3370183Z",
  "className": "Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController",
  "methodNames": [
    "SimpleWatchingMethodStart"
  ],
  "customData": {},
  "timerFrequency": 10000000
}
```

#### Using DI to Get Performance measurement results

Starting with [*v2.1.0*](https://github.com/unchase/Unchase.FluentPerformanceMeter/releases/tag/v2.1.0), it became possible to get the performance measurements results of public methods of the class using the built-in **DI** in *ASP.NET Core* application.
To do this, add the following code to `Startap.cs`:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    // ...
    
    // adds a singleton service to the specified IPerformanceInfo<MeasurableController> with DI
    services.AddSingleton(s => PerformanceMeter<MeasurableController>.PerformanceInfo);
    // ... the same for another classes (controllers)

    // ...
}
```

Then, using DI, you can get the results, for example, as follows:

```csharp
[ApiController]
[Route("api/v1/[controller]")]
public class PerformanceMeterController : ControllerBase
{
    private readonly IPerformanceInfo<PerformanceMeterController> _performanceInfo;

    public PerformanceMeterController(IPerformanceInfo<PerformanceMeterController> performanceInfo)
    {
        _performanceInfo = performanceInfo;
    }

    // ...

    /// <summary>
    /// Get methods performance info for this controller.
    /// </summary>
    /// <returns>Returns methods performance info.</returns>
    [HttpGet("GetPerformanceInfoV2")]
    [IgnoreMethodPerformance]
    public ActionResult<IPerformanceInfo> GetPerformanceInfoV2()
    {
        return Ok(_performanceInfo);
    }

    // ...
}
```

### **Method's performance measurement with `DiagnosticSource`**
Starting with *[v1.1.0](https://github.com/unchase/Unchase.FluentPerformanceMeter/releases/tag/v1.1.0)*, it became possible to measure the performance of methods in an *AspNetCore MVC* application using the `DiagnosticSource` and the special `WatchingWithDiagnosticSourceAttribute` attribute. To do this, add the *NuGet* package [`Unchase.FluentPerformanceMeter.AspNetCore.Mvc`](https://www.nuget.org/Unchase.FluentPerformanceMeter.AspNetCore.Mvc) to the project and add the following code to `Startap.cs`:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    // ...
    
    // allows to measure methods performance for class "MeasurableClass" and "MeasurableSecondClass"
    services.AddPerformanceDiagnosticObserver<MeasurableClass>();
    services.AddPerformanceDiagnosticObserver<MeasurableSecondClass>();
    // ... the same for another classes
    services.AddMvc();
    // ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ...
    app.UsePerformanceDiagnosticObserver();
    app.UseMvc();
}
```

Then mark with the attribute `WatchingWithDiagnosticSourceAttribute` either individual methods:

```csharp
[HttpGet("SimpleWatchingMethodStart")]
[WatchingWithDiagnosticSource]
public ActionResult SimpleWatchingMethodStart()
{	
    return Ok();
}
```

or the whole class:

```csharp
[ApiController]
[Route("api/v1/[controller]")]
[Produces("application/json")]
[SwaggerTag("Unchase.PerformanceMeter Test WebAPI Controller")]
[WatchingWithDiagnosticSource]
public class PerformanceMeterController : ControllerBase
{
    // measurable methods
}
```

Starting with version *[v1.2.0](https://github.com/unchase/Unchase.FluentPerformanceMeter/releases/tag/v1.2.0)*, it became possible to add invocation arguments to custom method performance measurement data in an *AspNetCore MVC* application using the special `AddMethodArgumentsToCustomDataAttribute` attribute in conjunction with the `WatchingWithDiagnosticSourceAttribute` attribute:

```csharp
[HttpPost("SimpleWatchingMethodStartWithArgs")]
[WatchingWithDiagnosticSource]
[AddMethodArgumentsToCustomData("actionArguments")]
public ActionResult SimpleWatchingMethodStartWithArgs(DTOArgument arg)
{
    return Ok();
}
```

After calling the method `SimpleWatchingMethodStartWithArgs` and calling `GetPerformanceInfo` we receive:

```json
{
  "methodCalls": [
    {
      "methodName": "SimpleWatchingMethodStartWithArgs",
      "elapsed": "00:00:00.0016350",
      "caller": "unknown",
      "startTime": "2019-12-06T10:27:27.3385385Z",
      "endTime": "2019-12-06T10:27:27.3401735Z",
      "customData": {
        "actionArguments": {
          "arg": {
            "data": "<string_in_DTOArgument>"
          }
        }
      },
      "steps": []
    }
  ],
  "totalActivity": [
    {
      "methodName": "SimpleWatchingMethodStartWithArgs",
      "callsCount": 1
    }
  ],
  "currentActivity": [
    {
      "methodName": "SimpleWatchingMethodStartWithArgs",
      "callsCount": 0
    }
  ],
  "uptimeSince": "2019-12-06T10:27:27.3370183Z",
  "className": "Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController",
  "methodNames": [
    "SimpleWatchingMethodStartWithArgs"
  ],
  "customData": {},
  "timerFrequency": 10000000
}
```

### Method's performance measurement with `WatchingPerformanceAttribute` attribute

Starting with [*v2.0.0*](https://github.com/unchase/Unchase.FluentPerformanceMeter/releases/tag/v2.0.0), it became possible to measure the performance of methods (actions) in an *AspNetCore MVC* application using the special `WatchingPerformanceAttribute` attribute, as well as configure the methods performance watching for the controllers in `Startup.cs`. To do this, add the *NuGet* package [`Unchase.FluentPerformanceMeter.AspNetCore.Mvc`](https://www.nuget.org/Unchase.FluentPerformanceMeter.AspNetCore.Mvc) to the project and add the following code to `Startap.cs`:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    // ...
    
    // allows to measure methods performance for class "MeasurableController" with configuring options
    services.AddPerformanceMeter<MeasurableController>(options => 
    {
        // ALL of this is optional. You can simply call .AddPerformanceMeter<MeasurableController>() for all defaults
        // Defaults: In-Memory for 5 minutes, everything watched, every user can see

        // excludes a method from performance watching
        options.ExcludeMethod(nameof(MeasurableController.MeasurableAction));

        // to control which requests are watched, use the Func<HttpRequest, bool> option:
        options.ShouldWatching = request => request.HttpContext.User.IsInRole("Dev");

        // allows to add custom data from custom attributes ("MethodCustomDataAttribute", "MethodCallerAttribute") to performance watching
        options.AddCustomDataFromCustomAttributes = false;

        // allows to use "IgnoreMethodPerformanceAttribute" for excluding from performance watching
        options.UseIgnoreMethodPerformanceAttribute = false;

        // allows to watch actions performance annotated with special attribute ("WatchingPerformanceAttribute")
        options.WatchForAnnotatedWithAttributeOnly = false;

        // excludes a path from being watched
        options.IgnorePath("/some_path");

        // allows to add route path to custom data (with "pm_path" key)
        options.AddRoutePathToCustomData = false;

        // set cache time for the watched performance results for the controller class
        options.SetMethodCallsCacheTime(5);

        // adds common custom data (anonymous class) to class performance information
        options.AddCustomData("Custom anonymous class", new { Name = "Custom Name", Value = 1 });

        // set default exception handler for the controller class
        options.SetDefaultExceptionHandler((ex) => Debug.WriteLine(ex.Message));
    });
    // ... and for "MeasurableSecondController" (without configuring options)
    services.AddPerformanceMeter<MeasurableSecondController>();
    // ... the same for another controllers

    services.AddMvc();

    // ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ...

    app.UseRouting();

    app.UseEndpoints(c =>
    {
        c.MapControllers();

        // use performance watching for concrete controller (for example, "MeasurableController")
        app.UsePerformanceMeterFor<MeasurableController>();
        // ... the same for another controllers
    });
}
```

Then mark with the attribute `WatchingPerformanceAttribute` either individual methods:

```csharp
[HttpGet("SimpleWatchingMethodStartWatchingPerformanceAttribute")]
[WatchingPerformance]
public ActionResult SimpleWatchingMethodStartWatchingPerformanceAttribute()
{	
    return Ok();
}
```

or the whole class:

```csharp
[ApiController]
[Route("api/v1/[controller]")]
[Produces("application/json")]
[SwaggerTag("Unchase.PerformanceMeter Test WebAPI Controller")]
[WatchingPerformance]
public class PerformanceMeterController : ControllerBase
{
    // measurable methods (actions)
}
```

### **Measuring the performance of an external library method**
To measure the performance of a public method of a *public* class of a third-party library, you should explicitly specify the class itself and the name of its method:

```csharp
[HttpGet("GetThreadSleepPerformance")]
public ActionResult<string> GetThreadSleepPerformance()
{
    using (PerformanceMeter<Thread>.WatchingMethod(nameof(Thread.Sleep)).Start())
    {
        Thread.Sleep(1000);
    }

    return Ok(PerformanceMeter<Thread>.PerformanceInfo.MethodCalls.FirstOrDefault(ta => ta.MethodName == nameof(Thread.Sleep))?.Elapsed);
}
```

The executed method will return:

```
"00:00:01.0033040"
```

You can get performance info related to calling this method through the call:

```csharp
/// <summary>
/// Get methods performance info for Thread class.
/// </summary>
/// <returns>Returns Thread methods performance info.</returns>
[HttpGet("GetThreadPerformanceInfo")]
[IgnoreMethodPerformance]
public ActionResult<IPerformanceInfo> GetThreadPerformanceInfo()
{
    return Ok(PerformanceMeter<Thread>.PerformanceInfo);
}
```

In response to the call of this method you will see:

```json
{
  "methodCalls": [
    {
      "methodName": "Sleep",
      "elapsed": "00:00:01.0033040",
      "caller": "unknown",
      "startTime": "2019-12-06T13:08:09.336624Z",
      "endTime": "2019-12-06T13:08:10.339928Z",
      "customData": {},
      "steps": []
    }
  ],
  "totalActivity": [
    {
      "methodName": "Abort",
      "callsCount": 0
    },
    ...
 ],
"currentActivity": [
    {
      "methodName": "Abort",
      "callsCount": 0
    },
    ...
 ],
"uptimeSince": "2019-12-06T13:08:09.3357028Z",
  "className": "System.Threading.Thread",
  "methodNames": [
    "Abort",
    ...
  ],
  "customData": {},
  "timerFrequency": 10000000
}
```

### **Adding Custom Data and splitting into Steps**
You can add Custom Data for all methods' performance measurements of a particular class. Let's take the static constructor of the `PerformanceMeterController` controller class as an example:

```csharp
[ApiController]
[Route("api/v1/[controller]")]
public class PerformanceMeterController : ControllerBase
{
    /// <summary>
    /// Static constructor.
    /// </summary>
    static PerformanceMeterController()
    {
        // add common custom data (string) to class performance information
        PerformanceMeter<PerformanceMeterController>.AddCustomData("Tag", "CustomTag");

        // add common custom data (anonymous class) to class performance information
        PerformanceMeter<PerformanceMeterController>.AddCustomData("Custom anonymous class", new { Name = "Custom Name", Value = 1 });
    }

    // ... actions
}
```

In addition, you can add Custom Data for a specific measurement using the extension method `.WithSettingData.CustomData("<key>", <value>)` (which can be also utilized through the special attribute `MethodCustomDataAttribute`) and for each Step of this measurement (that was added using the extension method `.Step("<step_name>")`) using the extension method `.AddCustomData("<key>", <value>)`:

```csharp
/// <summary>
/// Test GET method with simple performance watching (with steps).
/// </summary>
[HttpGet("SimpleStartWatchingWithSteps")]
[MethodCustomData("Custom data from attribute", "Attr")]
public ActionResult SimpleStartWatchingWithSteps()
{
    using (var pm = PerformanceMeter<PerformanceMeterController>
        .WatchingMethod()
        .WithSettingData
            .CustomData("coins", 1)
            .CustomData("Coins sets", new 
            { 
                Gold = "Many",
                Silver = 5
            })
        .Start())
    {
        // put your code with some logic here

        // add "Step 1"
        using (pm.Step("Step 1"))
        {
            Thread.Sleep(1000);
        }

        // add "Step 2" with custom data
        using (var pmStep = pm.Step("Step 2").AddCustomData("step2 custom data", "data!"))
        {
            // add "Step 3 in Step 2"
            using (pm.Step("Step 3 in Step 2"))
            {
                Thread.Sleep(1000);
            }

            // add custom data to "Step 2"
            pmStep.AddCustomData("step2 another custom data", "data2!");

            // get and remove custom data from "Step 2"
            var customData = pmStep.GetAndRemoveCustomData<string>("step2 custom data");
            // get custom data from "Step 2" (without removing)
            var anotherCustomData = pmStep.GetCustomData<string>("step2 another custom data");
        
            // ...
        }
    }
}
```

As a result, when `GetPerformanceInfo` is called we get:

```json
{
  "methodCalls": [
    {
      "methodName": "SimpleStartWatchingWithSteps",
      "elapsed": "00:00:02.0083031",
      "caller": "unknown",
      "startTime": "2019-12-06T11:58:18.9006891Z",
      "endTime": "2019-12-06T11:58:20.9089922Z",
      "customData": {
        "Coins sets": {
          "gold": "Many",
          "silver": 5
        },
        "coins": 1,
        "Custom data from attribute": "Attr"
      },
      "steps": [
        {
          "stepName": "Step 1",
          "elapsed": "00:00:01.0009758",
          "startTime": "2019-12-06T11:58:18.9018272Z",
          "endTime": "2019-12-06T11:58:19.902803Z",
          "customData": {}
        },
        {
          "stepName": "Step 3 in Step 2",
          "elapsed": "00:00:01.0004549",
          "startTime": "2019-12-06T11:58:19.9046523Z",
          "endTime": "2019-12-06T11:58:20.9051072Z",
          "customData": {}
        },
        {
          "stepName": "Step 2",
          "elapsed": "00:00:01.0029596",
          "startTime": "2019-12-06T11:58:19.904534Z",
          "endTime": "2019-12-06T11:58:20.9074936Z",
          "customData": {
            "step2 another custom data": "data2!"
          }
        }
      ]
    }
  ],
  "totalActivity": [
    {
      "methodName": "SimpleStartWatchingWithSteps",
      "callsCount": 1
    }
  ],
  "currentActivity": [
    {
      "methodName": "SimpleStartWatchingWithSteps",
      "callsCount": 0
    }
  ],
  "uptimeSince": "2019-12-06T11:58:18.8801249Z",
  "className": "Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController",
  "methodNames": [
    "SimpleStartWatchingWithSteps"
  ],
  "customData": {
    "Tag": "CustomTag",
    "Custom anonymous class": {
      "name": "Custom Name",
      "value": 1
    }
  },
  "timerFrequency": 10000000
}
```

### **Excluding the measurement**
You can exclude individual parts of the method's performance measurement (using `.Ignore()` or `.Executing().WithoutWatching().Start(<Action>)` extension methods), and also skip saving individual Steps (using `.StepIf("<step_name>", <minSaveMs>)` extension method), if they do not satisfy the condition (the step execution time will be taken into the method execution time):

```csharp
using (var pm = PerformanceMeter<PerformanceMeterController>.WatchingMethod().Start())
{
    // put your code with some logic here

    // sleep 1 sec
    Thread.Sleep(1000);

	// ignore this block in performance watching
    using (pm.Ignore())
    {
        Thread.Sleep(5000);
    }

    // skip this step with minSaveMs (do not save the step results, but take into account its duration)
    using (pm.StepIf("Skipped step", minSaveMs: 1000))
    {
        Thread.Sleep(500);
    }

    // execute action without performance watching
    pm.Executing().WithoutWatching().Start(() => 
    {
        Thread.Sleep(2000);
    });

    return Ok();
}
```

As a result, we get:

```json
{
  "methodCalls": [
    {
      "methodName": "SimpleStartWatchingWithIgnored",
      "elapsed": "00:00:01.5080227",
      "caller": "unknown",
      "startTime": "2019-12-06T12:34:36.9187359Z",
      "endTime": "2019-12-06T12:34:38.4267586Z",
      "customData": {},
      "steps": []
    }
  ],
  "totalActivity": [
    {
      "methodName": "SimpleStartWatchingWithIgnored",
      "callsCount": 1
    }
  ],
  "currentActivity": [
    {
      "methodName": "SimpleStartWatchingWithIgnored",
      "callsCount": 0
    }
  ],
  "uptimeSince": "2019-12-06T12:34:36.9035129Z",
  "className": "Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController",
  "methodNames": [
    "SimpleStartWatchingWithIgnored"
  ],
  "customData": { },
  "timerFrequency": 10000000
}
```

### **Adding custom Commands and Actions**
To add a custom Command that will be guaranteed to be executed upon completion of the method's performance measurement, it is required to create a command class that will implement the `IPerformanceCommand` interface. In this case, you can transfer arbitrary data through the constructor of the created command that will be used when the command is executed. For example:

```csharp
/// <summary>
/// Custom executed command.
/// </summary>
public class ExecutedCommand : IPerformanceCommand
{
    /// <summary>
    /// Executed commad name.
    /// </summary>
    public string CommandName => this.GetType().Name;

    private string _customString { get; }

    internal bool IsCommandExecuted { get; private set; }

    /// <summary>
    /// Constructor.
    /// </summary>
    /// <remarks>
    /// You can pass any data through the command constructor.
    /// </remarks>
    /// <param name="customString"></param>
    public ExecutedCommand(string customString) 
    {
        this._customString = customString;
    }

    /// <summary>
    /// Execute command.
    /// </summary>
    /// <param name="performanceInfo"><see cref="IPerformanceInfo"/>.</param>
    public void Execute(IPerformanceInfo performanceInfo)
    {
        // for example, write to the debug console some information
        Debug.WriteLine(this.CommandName);
        Debug.WriteLine(this._customString);
        Debug.WriteLine($"Method names count: {performanceInfo.MethodNames.Count}");
        this.IsCommandExecuted = true;
    }
}
```

You can add a custom Command (IPerformanceCommand) and an Action so that they are executed at the end of the measurement the following way:

```csharp
// custom "ExecutedCommand" will be executed after performance watching is completed
using (PerformanceMeter<PerformanceMeterController>
    .WatchingMethod()
    .WithExecutingOnComplete
        .Command(new ExecutedCommand("bla-bla-bla"))
        .Action((pi) =>
        {
            Debug.WriteLine($"Class name: {pi.ClassName}");
        })
    .Start())
{
    return Ok();
}
```

As a result, at the end of the measurement the Debug console will display:

```
ExecutedCommand
bla-bla-bla
Method names count: 13
Class name: Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController
```

### **Adding Exception Handlers**
If you need to handle exceptions that may occur during the execution of a part of the method for which performance is measured, you need to add an Exception Handler as follows:

```csharp
using (var pm = PerformanceMeter<PerformanceMeterController>.StartWatching())
{
    // execute an action that throws the exception to be handled by the exception handler
    pm.Executing()
        .WithExceptionHandler((ex) => Debug.WriteLine(ex.Message))
        .Start(() => throw new Exception("Exception"));

    // execute action throws custom Exception with exception handler
    pm.Executing<CustomException>()
       .WithExceptionHandler((ex) => { Debug.WriteLine(ex.Message); })
       .Start(() =>
       {
           throw new CustomException("Custom exception was occured!");
       });

    return Ok();
}
```

Where the `CustomException` class is (for example):

```csharp
/// <summary>
/// Custom exception.
/// </summary>
public class CustomException : Exception
{
    public CustomException(string message) : base(message) { }

    public CustomException(string message, Exception innerException) : base(message, innerException) { }

    public CustomException() { }
}
```

As a result, the following will be displayed in the *Debug* console:

```
Exception
Custom exception was occured!
```

In addition, you can specify an Exception Handler that will be used by default to measure the performance of any method of the given class, for example, through the static constructor of the `PerformanceMeterController` controller class:

```csharp
[ApiController]
[Route("api/v1/[controller]")]
public class PerformanceMeterController : ControllerBase
{
    /// <summary>
    /// Static constructor.
    /// </summary>
    static PerformanceMeterController()
    {
        // set default exception handler for PerformanceMeterController class
        PerformanceMeter<PerformanceMeterController>.SetDefaultExceptionHandler((ex) => Debug.WriteLine(ex.Message));
    }

    // ... actions
}
```

### **Setting the Cache Time**
You can set the Cache Time for storing the results of the method's performance measurement, after which these results will be deleted. For each class that is to be measured, this time is set separately. For example, the time can be set through the static constructor of the `PerformanceMeterController` controller class:

```csharp
[ApiController]
[Route("api/v1/[controller]")]
public class PerformanceMeterController : ControllerBase
{
    /// <summary>
    /// Static constructor.
    /// </summary>
    static PerformanceMeterController()
    {
        // set cache time for PerformanceMeterController class
        PerformanceMeter<PerformanceMeterController>.SetMethodCallsCacheTime(5);
    }

    // ... actions
}
```

### **Adding the info about the caller and call place origin (and aborting the measurement)**
* You can specify who is calling the method using the extension method `.CallerFrom("<caller_name>")` (either a string value or *IHttpContextAccessor* is passed to it) or a special attribute `[MethodCaller ("<caller_name>")]` for the method. Moreover, if both the attribute and the extension method are used, then the value will be taken from the latter.
* To add a call source for a performance measurement, use the extension method `.WithSettingData.CallerSourceData()`.
* To stop/abort the performance measurement inside the *using* block, use the `.StopWatching()` extension method or the `Dispose()` method directly:

```csharp
[HttpPost("StartWatchingWithCallerName")]
[MethodCaller("testCaller")]
public ActionResult<string> StartWatchingWithCallerName([FromBody] string value)
{
    // the method's performance info will be amended with the caller's name (if internal HttpContextAccessor is null)
    using (var pm = PerformanceMeter<PerformanceMeterController>
        .WatchingMethod()
        .WithSettingData
            .CallerSourceData()
            .CallerFrom("Test caller")
        .Start())
    {
        pm.StopWatching(); // stop watching here (or you can use "pm.Dispose();")
        Thread.Sleep(2000);

        return Ok(value);
    }
}
```

As a result of calling the `GetPerformanceInfo` method, you will get:

```json
{
  "methodCalls": [
    {
      "methodName": "StartWatchingWithCallerName",
      "elapsed": "00:00:00.0019172",
      "caller": "Test caller",
      "startTime": "2019-12-06T13:35:45.3164507Z",
      "endTime": "2019-12-06T13:35:45.3183679Z",
      "customData": {
        "customData123": 123,
        "callerSourceLineNumber": 525,
        "callerSource": "D:\\GitHub\\Unchase.FluentPerformanceMeter\\Unchase.FluentPerformanceMeter.TestWebAPI\\Controllers\\PerformanceMeterController.cs"
      },
      "steps": []
    }
  ],
  "totalActivity": [
    {
      "methodName": "StartWatchingWithCallerName",
      "callsCount": 1
    }
  ],
  "currentActivity": [
    {
      "methodName": "StartWatchingWithCallerName",
      "callsCount": 0
    }
  ],
  "uptimeSince": "2019-12-06T13:35:45.2601668Z",
  "className": "Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController",
  "methodNames": [
    "StartWatchingWithCallerName"
  ],
  "customData": { },
  "timerFrequency": 10000000
}
```

---

Enjoy programming with [Unchase](https://dev.to/unchase)!