{"id":13702518,"url":"https://github.com/unchase/Unchase.FluentPerformanceMeter","last_synced_at":"2025-05-05T04:31:05.891Z","repository":{"id":97217746,"uuid":"223410661","full_name":"unchase/Unchase.FluentPerformanceMeter","owner":"unchase","description":":hammer: Make the exact performance measurements of the public methods for public classes using this NuGet Package with fluent interface. Requires .Net Standard 2.0+. It is an Open Source project under Apache-2.0 License.","archived":false,"fork":false,"pushed_at":"2021-02-16T08:31:42.000Z","size":959,"stargazers_count":42,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-17T22:46:25.497Z","etag":null,"topics":["benchmark","benchmarking","csharp","dot-net","performance"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unchase.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-11-22T13:33:52.000Z","updated_at":"2024-12-08T05:54:06.000Z","dependencies_parsed_at":"2023-07-18T01:45:44.779Z","dependency_job_id":null,"html_url":"https://github.com/unchase/Unchase.FluentPerformanceMeter","commit_stats":null,"previous_names":["unchase/unchase.performancemeter"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unchase%2FUnchase.FluentPerformanceMeter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unchase%2FUnchase.FluentPerformanceMeter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unchase%2FUnchase.FluentPerformanceMeter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unchase%2FUnchase.FluentPerformanceMeter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unchase","download_url":"https://codeload.github.com/unchase/Unchase.FluentPerformanceMeter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252439525,"owners_count":21748023,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["benchmark","benchmarking","csharp","dot-net","performance"],"created_at":"2024-08-02T21:00:37.038Z","updated_at":"2025-05-05T04:31:05.356Z","avatar_url":"https://github.com/unchase.png","language":"C#","readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.nuget.org/packages/Unchase.FluentPerformanceMeter/\"\u003e\n    \u003cimg src=\"img/logo.png\" alt=\"Unchase Fluent Performance Meter Logo\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n----------\n\n[Russian](README_RU.md) | [English](README.md)\n\n\n----------\n\n\n**Unchase Fluent Performance Meter** is an open-source and cross-platform *.Net Standard 2.0* library that is designed for the method’s performance measurement.\n\nThe library can be used in .NET Core and .NET Framework applications that support *.Net Standard 2.0*, and it allows for:\n\n* [**Making the exact performance measurements**](#SimpleSamples) of the ***public* methods** for ***public* classes** in your code and in the [codes of the utilized libraries](#SampleExternal) (and recording the exact start and end times of the measurements);\n\n* [**Adding Custom Data**](#SampleCustomData) 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;\n\n* [**Splitting the measurement**](#SampleCustomData) of the method performance **into separate steps** and storing personal data for each step. In addition, you can [set the minimum execution time](#SampleIgnore) 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);\n\n* [**Excluding individual parts of the code**](#SampleIgnore) 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);\n\n* [**Adding custom Commands**](#SampleCustomCommands), 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);\n\n* [**Adding custom Exception Handler**](#SampleCustomExceptionHandler) for the code executed in the context of the method’s performance measurement  (can be used for all measurements, or for each measurement individually);\n\n* [**Setting the Cache Time**](#SampleSettingCacheTime) for storing the method’s performance measurement results after which they will be deleted;\n\n* [**Adding the info about**](#SampleSetCallerAndSourceWithAborting) **the method’s Caller** 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);\n\n* [**Adding the info about**](#SampleSetCallerAndSourceWithAborting) the **place** where the performance measurement was started to the measurement results (referencing the file name and the exact line number where the method was called);\n\n* [**Aborting the method’s performance measurement**](#SampleSetCallerAndSourceWithAborting) **before the process is finished on its own**.\n\nThe 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.\n\n\u003e The project is developed and maintained by [Nikolay Chebotov (**Unchase**)](https://github.com/unchase).\n\n## Builds status\n\n|Status|Value|\n|:----|:---:|\n|Build|[![Build status](https://ci.appveyor.com/api/projects/status/9md9r7j6ex7xa0jf)](https://ci.appveyor.com/project/unchase/unchase.fluentperformancemeter)\n|Buid History|![Build history](https://buildstats.info/appveyor/chart/unchase/unchase-fluentperformancemeter)\n|GitHub Release|[![GitHub release](https://img.shields.io/github/release/unchase/Unchase.fluentperformancemeter.svg)](https://github.com/unchase/Unchase.fluentperformancemeter/releases/latest)\n|GitHub Release Date|[![GitHub Release Date](https://img.shields.io/github/release-date/unchase/Unchase.fluentperformancemeter.svg)](https://github.com/unchase/Unchase.fluentperformancemeter/releases/latest)\n|GitHub Release Downloads|[![Github Releases](https://img.shields.io/github/downloads/unchase/Unchase.fluentperformancemeter/total.svg)](https://github.com/unchase/Unchase.fluentperformancemeter/releases/latest)\n|Nuget Version|[![NuGet Version](http://img.shields.io/nuget/v/Unchase.fluentperformancemeter.svg?style=flat)](https://www.nuget.org/packages/Unchase.fluentperformancemeter/) \n|Nuget Downloads|[![Nuget Downloads](https://img.shields.io/nuget/dt/Unchase.fluentperformancemeter.svg)](https://www.nuget.org/packages/Unchase.fluentperformancemeter/)\n|Nuget Version (AspNetCore.MVC)|[![NuGet Version](http://img.shields.io/nuget/v/Unchase.fluentperformancemeter.aspnetcore.mvc.svg?style=flat)](https://www.nuget.org/packages/Unchase.fluentperformancemeter.aspnetcore.mvc/) \n|Nuget Downloads (AspNetCore.MVC)|[![Nuget Downloads](https://img.shields.io/nuget/dt/Unchase.fluentperformancemeter.aspnetcore.mvc.svg)](https://www.nuget.org/packages/Unchase.fluentperformancemeter.aspnetcore.mvc/)\n\n## Table of content\n\n* [Getting started](#Start)\n* [Examples of usage](#SimpleSamples)\n\t* [Method's performance measurement](#SimpleSamples)\n      * [Using DI to Get Performance measurement results (v2.1.0)](#UsingDISamples)\n      * [Using DI to Get Performance measurement instance (v2.1.2)](#UsingDIInstanceSamples)\n\t* [Method's performance measurement with `DiagnosticSource` (v1.1.0)](#DiagnosticSourceSample)\n    * [Method's performance measurement with `WatchingPerformanceAttribute` attribute (v2.0.0)](#WatchingPerformanceSample)\n\t* [Measuring the performance of an external library method](#SampleExternal)\n\t* [Adding Custom Data and spliting into Steps](#SampleCustomData)\n\t* [Excluding the measurement](#SampleIgnore)\n\t* [Adding custom Commands and Actions](#SampleCustomCommands)\n\t* [Adding Exception Handlers](#SampleCustomExceptionHandler)\n\t* [Setting the Cache Time](#SampleSettingCacheTime)\n\t* [Adding the info about the caller and call place origin (and aborting the measurement)](#SampleSetCallerAndSourceWithAborting)\n\n## \u003ca name=\"Start\"\u003e\u003c/a\u003e Getting started\n\nTo use the library, install [*NuGet* package](https://www.nuget.org/packages/Unchase.FluentPerformanceMeter/) into your project:\n\n#### Manually with the *NuGet* Package Manager:\n\n```powershell\nInstall-Package Unchase.FluentPerformanceMeter\n```\n\n#### Using the .NET CLI:\n\n```powershell\ndotnet add package Unchase.FluentPerformanceMeter --version {version}\n```\n\n\u003e Where {version} is the version of the package you want to install. \n\u003e For example, `dotnet add package Unchase.FluentPerformanceMeter --version 1.0.0`\n\n## \u003ca name=\"SimpleSamples\"\u003e\u003c/a\u003e Examples of usage\n\n### Method's performance measurement\n\nThe 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.\nSince [*v1.0.5*](https://github.com/unchase/Unchase.FluentPerformanceMeter/releases/tag/v1.0.5), you can also use `.WatchingMethod().Start(SimpleWatchingMethodStart)` or `.StartWatching(SimpleWatchingMethodStart)` with the method name.\n\n\u003e All examples of using the library can be found in the `Unchase.FluentPerformanceMeter.Test*` projects of this repository.\n\n```csharp\n/// \u003csummary\u003e\n/// Test GET method with simple performance watching.\n/// \u003c/summary\u003e\n[HttpGet(\"SimpleWatchingMethodStart\")]\npublic ActionResult SimpleWatchingMethodStart()\n{\t\n    // for C# 8 you can use:\n    //using var pm = PerformanceMeter\u003cPerformanceMeterController\u003e.StartWatching();\n\n    using (PerformanceMeter\u003cPerformanceMeterController\u003e.WatchingMethod().Start())\n    {\n        // put your code with some logic here\n\n        return Ok();\n    }\n}\n```\n\nTo get the performance measurements results of public methods of the controller class `PerformanceMeterController` you can call the following method:\n\n```csharp\n/// \u003csummary\u003e\n/// Get methods performance info for this controller.\n/// \u003c/summary\u003e\n/// \u003creturns\u003eReturns methods performance info.\u003c/returns\u003e\n[HttpGet(\"GetPerformanceInfo\")]\n[IgnoreMethodPerformance]\npublic ActionResult\u003cIPerformanceInfo\u003e GetPerformanceInfo()\n{\n    return Ok(PerformanceMeter\u003cPerformanceMeterController\u003e.PerformanceInfo);\n}\n```\n\n\u003e The attribute `IgnoreMethodPerformance` is intended so that the method marked by it is not taken into account when measuring performance.\n\nAfter calling the method `SimpleWatchingMethodStart` and calling `GetPerformanceInfo` we receive:\n\n```json\n{\n  \"methodCalls\": [\n    {\n      \"methodName\": \"SimpleWatchingMethodStart\",\n      \"elapsed\": \"00:00:00.0016350\",\n      \"caller\": \"unknown\",\n      \"startTime\": \"2019-12-06T10:27:27.3385385Z\",\n      \"endTime\": \"2019-12-06T10:27:27.3401735Z\",\n      \"customData\": {},\n      \"steps\": []\n    }\n  ],\n  \"totalActivity\": [\n    {\n      \"methodName\": \"SimpleWatchingMethodStart\",\n      \"callsCount\": 1\n    }\n  ],\n  \"currentActivity\": [\n    {\n      \"methodName\": \"SimpleWatchingMethodStart\",\n      \"callsCount\": 0\n    }\n  ],\n  \"uptimeSince\": \"2019-12-06T10:27:27.3370183Z\",\n  \"className\": \"Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController\",\n  \"methodNames\": [\n    \"SimpleWatchingMethodStart\"\n  ],\n  \"customData\": {},\n  \"timerFrequency\": 10000000\n}\n```\n\n#### \u003ca name=\"UsingDISamples\"\u003e\u003c/a\u003e Using DI to Get Performance measurement results\n\nStarting 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.\nTo do this, add the following code to `Startap.cs`:\n\n```csharp\npublic void ConfigureServices(IServiceCollection services)\n{\n    // ...\n    \n    // adds a singleton service to the specified IPerformanceInfo\u003cMeasurableController\u003e with DI\n    services.AddSingleton(s =\u003e PerformanceMeter\u003cMeasurableController\u003e.PerformanceInfo);\n    // ... the same for another classes (controllers)\n\n    // ...\n}\n```\n\nThen, using DI, you can get the results, for example, as follows:\n\n```csharp\n[ApiController]\n[Route(\"api/v1/[controller]\")]\npublic class PerformanceMeterController : ControllerBase\n{\n    private readonly IPerformanceInfo\u003cPerformanceMeterController\u003e _performanceInfo;\n\n    public PerformanceMeterController(IPerformanceInfo\u003cPerformanceMeterController\u003e performanceInfo)\n    {\n        _performanceInfo = performanceInfo;\n    }\n\n    // ...\n\n    /// \u003csummary\u003e\n    /// Get methods performance info for this controller.\n    /// \u003c/summary\u003e\n    /// \u003creturns\u003eReturns methods performance info.\u003c/returns\u003e\n    [HttpGet(\"GetPerformanceInfoV2\")]\n    [IgnoreMethodPerformance]\n    public ActionResult\u003cIPerformanceInfo\u003e GetPerformanceInfoV2()\n    {\n        return Ok(_performanceInfo);\n    }\n\n    // ...\n}\n```\n\n#### \u003ca name=\"UsingDIInstanceSamples\"\u003e\u003c/a\u003e Using DI to Get Performance measurement instance\n\nStarting with [*v2.1.2*](https://github.com/unchase/Unchase.FluentPerformanceMeter/releases/tag/v2.1.2), it became possible to get the performance measurement of the class instance using the built-in **DI** in *ASP.NET Core* application.\nTo do this, add the following code to `Startap.cs`:\n\n```csharp\npublic void ConfigureServices(IServiceCollection services)\n{\n    // ...\n\n    services.AddPerformanceMeter\u003cPerformanceMeterController\u003e(options =\u003e\n    {\n        // ...\n\n        // adds a scope service of the PerformanceMeter of concrete class for DI\n        // use it with \".WithSettingData.CallerFrom(IHttpContextAccessor)\"\n        options.RegisterPerformanceMeterScope = true;\n    }\n\n    // ...\n}\n```\n\nThen, using DI, you can get the performance measurement instance, for example, as follows:\n\n```csharp\n[ApiController]\n[Route(\"api/v1/[controller]\")]\npublic class PerformanceMeterController : ControllerBase\n{\n    [HttpGet(\"WatchingMethodUsingDI\")]\n    public IActionResult WatchingMethodUsingDI()\n    {\n        // method performance info will reach with HttpContextAccessor (required for DI)\n        using (PerformanceMeter\u003cPerformanceMeterController\u003e\n            .WatchingMethod(nameof(WatchingMethodUsingDI))\n            .WithSettingData\n                .CallerFrom(_httpContextAccessor)\n            .Start())\n        {\n            GetPerformanceMeterUsingDI();\n\n            return Ok();\n        }\n    }\n\n    private void GetPerformanceMeterUsingDI()\n    {\n        // get instance of PerformanceMeter\u003cPerformanceMeterController\u003e using DI\n        var pm = HttpContext.RequestServices.GetRequiredService(typeof(PerformanceMeter\u003cPerformanceMeterController\u003e)) as PerformanceMeter\u003cPerformanceMeterController\u003e;\n\n        // we can use \"pm\" for adding steps or for other operations\n        // ...\n    }\n\n    // ...\n}\n```\n\n### \u003ca name=\"DiagnosticSourceSample\"\u003e\u003c/a\u003e Method's performance measurement with `DiagnosticSource`\n\nStarting 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`:\n\n```csharp\npublic void ConfigureServices(IServiceCollection services)\n{\n    // ...\n    \n    // allows to measure methods performance for class \"MeasurableClass\" and \"MeasurableSecondClass\"\n    services.AddPerformanceDiagnosticObserver\u003cMeasurableClass\u003e();\n    services.AddPerformanceDiagnosticObserver\u003cMeasurableSecondClass\u003e();\n    // ... the same for another classes\n\n    services.AddMvc();\n\n    // ...\n}\n\npublic void Configure(IApplicationBuilder app, IHostingEnvironment env)\n{\n    // ...\n\n    app.UsePerformanceDiagnosticObserver();\n\n    app.UseMvc();\n}\n```\n\nThen mark with the attribute `WatchingWithDiagnosticSourceAttribute` either individual methods:\n\n```csharp\n[HttpGet(\"SimpleWatchingMethodStart\")]\n[WatchingWithDiagnosticSource]\npublic ActionResult SimpleWatchingMethodStart()\n{\t\n    return Ok();\n}\n```\n\nor the whole class:\n\n```csharp\n[ApiController]\n[Route(\"api/v1/[controller]\")]\n[Produces(\"application/json\")]\n[SwaggerTag(\"Unchase.PerformanceMeter Test WebAPI Controller\")]\n[WatchingWithDiagnosticSource]\npublic class PerformanceMeterController : ControllerBase\n{\n    // measurable methods\n}\n```\n\nStarting 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:\n\n```csharp\n[HttpPost(\"SimpleWatchingMethodStartWithArgs\")]\n[WatchingWithDiagnosticSource]\n[AddMethodArgumentsToCustomData(\"actionArguments\")]\npublic ActionResult SimpleWatchingMethodStartWithArgs(DTOArgument arg)\n{\n    return Ok();\n}\n```\n\nAfter calling the method `SimpleWatchingMethodStartWithArgs` and calling `GetPerformanceInfo` we receive:\n\n```json\n{\n  \"methodCalls\": [\n    {\n      \"methodName\": \"SimpleWatchingMethodStartWithArgs\",\n      \"elapsed\": \"00:00:00.0016350\",\n      \"caller\": \"unknown\",\n      \"startTime\": \"2019-12-06T10:27:27.3385385Z\",\n      \"endTime\": \"2019-12-06T10:27:27.3401735Z\",\n      \"customData\": {\n        \"actionArguments\": {\n          \"arg\": {\n            \"data\": \"\u003cstring_in_DTOArgument\u003e\"\n          }\n        }\n      },\n      \"steps\": []\n    }\n  ],\n  \"totalActivity\": [\n    {\n      \"methodName\": \"SimpleWatchingMethodStartWithArgs\",\n      \"callsCount\": 1\n    }\n  ],\n  \"currentActivity\": [\n    {\n      \"methodName\": \"SimpleWatchingMethodStartWithArgs\",\n      \"callsCount\": 0\n    }\n  ],\n  \"uptimeSince\": \"2019-12-06T10:27:27.3370183Z\",\n  \"className\": \"Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController\",\n  \"methodNames\": [\n    \"SimpleWatchingMethodStartWithArgs\"\n  ],\n  \"customData\": {},\n  \"timerFrequency\": 10000000\n}\n```\n\n### \u003ca name=\"WatchingPerformanceSample\"\u003e\u003c/a\u003e Method's performance measurement with `WatchingPerformanceAttribute` attribute\n\nStarting 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`:\n\n```csharp\npublic void ConfigureServices(IServiceCollection services)\n{\n    // ...\n    \n    // allows to measure methods performance for class \"MeasurableController\" with configuring options\n    services.AddPerformanceMeter\u003cMeasurableController\u003e(options =\u003e \n    {\n        // ALL of this is optional. You can simply call .AddPerformanceMeter\u003cMeasurableController\u003e() for all defaults\n        // Defaults: In-Memory for 5 minutes, everything watched, every user can see\n\n        // excludes a method from performance watching\n        options.ExcludeMethod(nameof(MeasurableController.MeasurableAction));\n\n        // to control which requests are watched, use the Func\u003cHttpRequest, bool\u003e option:\n        options.ShouldWatching = request =\u003e request.HttpContext.User.IsInRole(\"Dev\");\n\n        // allows to add custom data from custom attributes (\"MethodCustomDataAttribute\", \"MethodCallerAttribute\") to performance watching\n        options.AddCustomDataFromCustomAttributes = false;\n\n        // allows to use \"IgnoreMethodPerformanceAttribute\" for excluding from performance watching\n        options.UseIgnoreMethodPerformanceAttribute = false;\n\n        // allows to watch actions performance annotated with special attribute (\"WatchingPerformanceAttribute\")\n        options.WatchForAnnotatedWithAttributeOnly = false;\n\n        // excludes a path from being watched\n        options.IgnorePath(\"/some_path\");\n\n        // allows to add route path to custom data (with \"pm_path\" key)\n        options.AddRoutePathToCustomData = false;\n\n        // set cache time for the watched performance results for the controller class\n        options.SetMethodCallsCacheTime(5);\n\n        // adds common custom data (anonymous class) to class performance information\n        options.AddCustomData(\"Custom anonymous class\", new { Name = \"Custom Name\", Value = 1 });\n\n        // set default exception handler for the controller class\n        options.SetDefaultExceptionHandler((ex) =\u003e Debug.WriteLine(ex.Message));\n    });\n    // ... and for \"MeasurableSecondController\" (without configuring options)\n    services.AddPerformanceMeter\u003cMeasurableSecondController\u003e();\n    // ... the same for another controllers\n\n    services.AddMvc();\n\n    // ...\n}\n\npublic void Configure(IApplicationBuilder app, IHostingEnvironment env)\n{\n    // ...\n\n    app.UseRouting();\n\n    app.UseEndpoints(c =\u003e\n    {\n        c.MapControllers();\n\n        // use performance watching for concrete controller (for example, \"MeasurableController\")\n        app.UsePerformanceMeterFor\u003cMeasurableController\u003e();\n        // ... the same for another controllers\n    });\n}\n```\n\nThen mark with the attribute `WatchingPerformanceAttribute` either individual methods:\n\n```csharp\n[HttpGet(\"SimpleWatchingMethodStartWatchingPerformanceAttribute\")]\n[WatchingPerformance]\npublic ActionResult SimpleWatchingMethodStartWatchingPerformanceAttribute()\n{\t\n    return Ok();\n}\n```\n\nor the whole class:\n\n```csharp\n[ApiController]\n[Route(\"api/v1/[controller]\")]\n[Produces(\"application/json\")]\n[SwaggerTag(\"Unchase.PerformanceMeter Test WebAPI Controller\")]\n[WatchingPerformance]\npublic class PerformanceMeterController : ControllerBase\n{\n    // measurable methods (actions)\n}\n```\n\n### \u003ca name=\"SampleExternal\"\u003e\u003c/a\u003e Measuring the performance of an external library method\n\nTo 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:\n\n```csharp\n[HttpGet(\"GetThreadSleepPerformance\")]\npublic ActionResult\u003cstring\u003e GetThreadSleepPerformance()\n{\n    using (PerformanceMeter\u003cThread\u003e.WatchingMethod(nameof(Thread.Sleep)).Start())\n    {\n        Thread.Sleep(1000);\n    }\n\n    return Ok(PerformanceMeter\u003cThread\u003e.PerformanceInfo.MethodCalls.FirstOrDefault(ta =\u003e ta.MethodName == nameof(Thread.Sleep))?.Elapsed);\n}\n```\n\nThe executed method will return:\n\n```\n\"00:00:01.0033040\"\n```\n\nYou can get performance info related to calling this method through the call:\n\n```csharp\n/// \u003csummary\u003e\n/// Get methods performance info for Thread class.\n/// \u003c/summary\u003e\n/// \u003creturns\u003eReturns Thread methods performance info.\u003c/returns\u003e\n[HttpGet(\"GetThreadPerformanceInfo\")]\n[IgnoreMethodPerformance]\npublic ActionResult\u003cIPerformanceInfo\u003e GetThreadPerformanceInfo()\n{\n    return Ok(PerformanceMeter\u003cThread\u003e.PerformanceInfo);\n}\n```\n\nIn response to the call of this method you will see:\n\n```json\n{\n  \"methodCalls\": [\n    {\n      \"methodName\": \"Sleep\",\n      \"elapsed\": \"00:00:01.0033040\",\n      \"caller\": \"unknown\",\n      \"startTime\": \"2019-12-06T13:08:09.336624Z\",\n      \"endTime\": \"2019-12-06T13:08:10.339928Z\",\n      \"customData\": {},\n      \"steps\": []\n    }\n  ],\n  \"totalActivity\": [\n    {\n      \"methodName\": \"Abort\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Abort\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"ResetAbort\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Suspend\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Resume\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"BeginCriticalRegion\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"EndCriticalRegion\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"BeginThreadAffinity\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"EndThreadAffinity\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"AllocateDataSlot\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"AllocateNamedDataSlot\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetNamedDataSlot\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"FreeNamedDataSlot\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetData\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"SetData\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"SetApartmentState\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"TrySetApartmentState\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetCompressedStack\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"SetCompressedStack\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetCurrentProcessorId\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetDomain\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetDomainID\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetHashCode\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Interrupt\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Join\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Join\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Join\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"MemoryBarrier\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Sleep\",\n      \"callsCount\": 1\n    },\n    {\n      \"methodName\": \"Sleep\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"SpinWait\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Yield\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Start\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Start\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetApartmentState\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"DisableComObjectEagerCleanup\",\n      \"callsCount\": 0\n    }\n  ],\n  \"currentActivity\": [\n    {\n      \"methodName\": \"Abort\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Abort\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"ResetAbort\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Suspend\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Resume\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"BeginCriticalRegion\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"EndCriticalRegion\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"BeginThreadAffinity\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"EndThreadAffinity\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"AllocateDataSlot\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"AllocateNamedDataSlot\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetNamedDataSlot\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"FreeNamedDataSlot\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetData\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"SetData\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"SetApartmentState\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"TrySetApartmentState\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetCompressedStack\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"SetCompressedStack\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetCurrentProcessorId\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetDomain\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetDomainID\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetHashCode\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Interrupt\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Join\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Join\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Join\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"MemoryBarrier\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Sleep\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Sleep\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"SpinWait\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Yield\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Start\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"Start\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileRead\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"VolatileWrite\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"GetApartmentState\",\n      \"callsCount\": 0\n    },\n    {\n      \"methodName\": \"DisableComObjectEagerCleanup\",\n      \"callsCount\": 0\n    }\n  ],\n  \"uptimeSince\": \"2019-12-06T13:08:09.3357028Z\",\n  \"className\": \"System.Threading.Thread\",\n  \"methodNames\": [\n    \"Abort\",\n    \"ResetAbort\",\n    \"Suspend\",\n    \"Resume\",\n    \"BeginCriticalRegion\",\n    \"EndCriticalRegion\",\n    \"BeginThreadAffinity\",\n    \"EndThreadAffinity\",\n    \"AllocateDataSlot\",\n    \"AllocateNamedDataSlot\",\n    \"GetNamedDataSlot\",\n    \"FreeNamedDataSlot\",\n    \"GetData\",\n    \"SetData\",\n    \"SetApartmentState\",\n    \"TrySetApartmentState\",\n    \"GetCompressedStack\",\n    \"SetCompressedStack\",\n    \"GetCurrentProcessorId\",\n    \"GetDomain\",\n    \"GetDomainID\",\n    \"GetHashCode\",\n    \"Interrupt\",\n    \"Join\",\n    \"MemoryBarrier\",\n    \"Sleep\",\n    \"SpinWait\",\n    \"Yield\",\n    \"Start\",\n    \"VolatileRead\",\n    \"VolatileWrite\",\n    \"GetApartmentState\",\n    \"DisableComObjectEagerCleanup\"\n  ],\n  \"customData\": {},\n  \"timerFrequency\": 10000000\n}\n```\n\n### \u003ca name=\"SampleCustomData\"\u003e\u003c/a\u003e Adding Custom Data and splitting into Steps\n\nYou 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:\n\n```csharp\n[ApiController]\n[Route(\"api/v1/[controller]\")]\npublic class PerformanceMeterController : ControllerBase\n{\n    /// \u003csummary\u003e\n    /// Static constructor.\n    /// \u003c/summary\u003e\n    static PerformanceMeterController()\n    {\n        // add common custom data (string) to class performance information\n        PerformanceMeter\u003cPerformanceMeterController\u003e.AddCustomData(\"Tag\", \"CustomTag\");\n\n        // add common custom data (anonymous class) to class performance information\n        PerformanceMeter\u003cPerformanceMeterController\u003e.AddCustomData(\"Custom anonymous class\", new { Name = \"Custom Name\", Value = 1 });\n    }\n\n    // ... actions\n}\n```\n\nIn addition, you can add Custom Data for a specific measurement using the extension method `.WithSettingData.CustomData(\"\u003ckey\u003e\", \u003cvalue\u003e)` (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(\"\u003cstep_name\u003e\")`) using the extension method `.AddCustomData(\"\u003ckey\u003e\", \u003cvalue\u003e)`:\n\n```csharp\n/// \u003csummary\u003e\n/// Test GET method with simple performance watching (with steps).\n/// \u003c/summary\u003e\n[HttpGet(\"SimpleStartWatchingWithSteps\")]\n[MethodCustomData(\"Custom data from attribute\", \"Attr\")]\npublic ActionResult SimpleStartWatchingWithSteps()\n{\n    using (var pm = PerformanceMeter\u003cPerformanceMeterController\u003e\n        .WatchingMethod()\n        .WithSettingData\n            .CustomData(\"coins\", 1)\n            .CustomData(\"Coins sets\", new \n            { \n                Gold = \"Many\",\n                Silver = 5\n            })\n        .Start())\n    {\n        // put your code with some logic here\n\n        // add \"Step 1\"\n        using (pm.Step(\"Step 1\"))\n        {\n            Thread.Sleep(1000);\n        }\n\n        // add \"Step 2\" with custom data\n        using (var pmStep = pm.Step(\"Step 2\").AddCustomData(\"step2 custom data\", \"data!\"))\n        {\n            // add \"Step 3 in Step 2\"\n            using (pm.Step(\"Step 3 in Step 2\"))\n            {\n                Thread.Sleep(1000);\n            }\n\n            // add custom data to \"Step 2\"\n            pmStep.AddCustomData(\"step2 another custom data\", \"data2!\");\n\n            // get and remove custom data from \"Step 2\"\n            var customData = pmStep.GetAndRemoveCustomData\u003cstring\u003e(\"step2 custom data\");\n\n            // get custom data from \"Step 2\" (without removing)\n            var anotherCustomData = pmStep.GetCustomData\u003cstring\u003e(\"step2 another custom data\");\n        \n            // ...\n        }\n    }\n}\n```\n\nAs a result, when `GetPerformanceInfo` is called we get:\n\n```json\n{\n  \"methodCalls\": [\n    {\n      \"methodName\": \"SimpleStartWatchingWithSteps\",\n      \"elapsed\": \"00:00:02.0083031\",\n      \"caller\": \"unknown\",\n      \"startTime\": \"2019-12-06T11:58:18.9006891Z\",\n      \"endTime\": \"2019-12-06T11:58:20.9089922Z\",\n      \"customData\": {\n        \"Coins sets\": {\n          \"gold\": \"Many\",\n          \"silver\": 5\n        },\n        \"coins\": 1,\n        \"Custom data from attribute\": \"Attr\"\n      },\n      \"steps\": [\n        {\n          \"stepName\": \"Step 1\",\n          \"elapsed\": \"00:00:01.0009758\",\n          \"startTime\": \"2019-12-06T11:58:18.9018272Z\",\n          \"endTime\": \"2019-12-06T11:58:19.902803Z\",\n          \"customData\": {}\n        },\n        {\n          \"stepName\": \"Step 3 in Step 2\",\n          \"elapsed\": \"00:00:01.0004549\",\n          \"startTime\": \"2019-12-06T11:58:19.9046523Z\",\n          \"endTime\": \"2019-12-06T11:58:20.9051072Z\",\n          \"customData\": {}\n        },\n        {\n          \"stepName\": \"Step 2\",\n          \"elapsed\": \"00:00:01.0029596\",\n          \"startTime\": \"2019-12-06T11:58:19.904534Z\",\n          \"endTime\": \"2019-12-06T11:58:20.9074936Z\",\n          \"customData\": {\n            \"step2 another custom data\": \"data2!\"\n          }\n        }\n      ]\n    }\n  ],\n  \"totalActivity\": [\n    {\n      \"methodName\": \"SimpleStartWatchingWithSteps\",\n      \"callsCount\": 1\n    }\n  ],\n  \"currentActivity\": [\n    {\n      \"methodName\": \"SimpleStartWatchingWithSteps\",\n      \"callsCount\": 0\n    }\n  ],\n  \"uptimeSince\": \"2019-12-06T11:58:18.8801249Z\",\n  \"className\": \"Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController\",\n  \"methodNames\": [\n    \"SimpleStartWatchingWithSteps\"\n  ],\n  \"customData\": {\n    \"Tag\": \"CustomTag\",\n    \"Custom anonymous class\": {\n      \"name\": \"Custom Name\",\n      \"value\": 1\n    }\n  },\n  \"timerFrequency\": 10000000\n}\n```\n\n### \u003ca name=\"SampleIgnore\"\u003e\u003c/a\u003e Excluding the measurement\n\nYou can exclude individual parts of the method’s performance measurement (using `.Ignore()` or `.Executing().WithoutWatching().Start(\u003cAction\u003e)` extension methods), and also skip saving individual Steps (using `.StepIf(\"\u003cstep_name\u003e\", \u003cminSaveMs\u003e)` extension method), if they do not satisfy the condition (the step execution time will be taken into the method execution time):\n\n```csharp\nusing (var pm = PerformanceMeter\u003cPerformanceMeterController\u003e.WatchingMethod().Start())\n{\n    // put your code with some logic here\n\n    // sleep 1 sec\n    Thread.Sleep(1000);\n\n    // ignore this block in performance watching\n    using (pm.Ignore())\n    {\n        Thread.Sleep(5000);\n    }\n\n    // skip this step with minSaveMs (do not save the step results, but take into account its duration)\n    using (pm.StepIf(\"Skipped step\", minSaveMs: 1000))\n    {\n        Thread.Sleep(500);\n    }\n\n    // execute action without performance watching\n    pm.Executing().WithoutWatching().Start(() =\u003e \n    {\n        Thread.Sleep(2000);\n    });\n\n    return Ok();\n}\n```\n\nAs a result, we get:\n\n```json\n{\n  \"methodCalls\": [\n    {\n      \"methodName\": \"SimpleStartWatchingWithIgnored\",\n      \"elapsed\": \"00:00:01.5080227\",\n      \"caller\": \"unknown\",\n      \"startTime\": \"2019-12-06T12:34:36.9187359Z\",\n      \"endTime\": \"2019-12-06T12:34:38.4267586Z\",\n      \"customData\": {},\n      \"steps\": []\n    }\n  ],\n  \"totalActivity\": [\n    {\n      \"methodName\": \"SimpleStartWatchingWithIgnored\",\n      \"callsCount\": 1\n    }\n  ],\n  \"currentActivity\": [\n    {\n      \"methodName\": \"SimpleStartWatchingWithIgnored\",\n      \"callsCount\": 0\n    }\n  ],\n  \"uptimeSince\": \"2019-12-06T12:34:36.9035129Z\",\n  \"className\": \"Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController\",\n  \"methodNames\": [\n    \"SimpleStartWatchingWithIgnored\"\n  ],\n  \"customData\": { },\n  \"timerFrequency\": 10000000\n}\n```\n\n### \u003ca name=\"SampleCustomCommands\"\u003e\u003c/a\u003e Adding custom Commands and Actions\n\nTo 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. \nIn 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:\n\n```csharp\n/// \u003csummary\u003e\n/// Custom executed command.\n/// \u003c/summary\u003e\npublic class ExecutedCommand : IPerformanceCommand\n{\n    /// \u003csummary\u003e\n    /// Executed commad name.\n    /// \u003c/summary\u003e\n    public string CommandName =\u003e this.GetType().Name;\n\n    private string _customString { get; }\n\n    internal bool IsCommandExecuted { get; private set; }\n\n    /// \u003csummary\u003e\n    /// Constructor.\n    /// \u003c/summary\u003e\n    /// \u003cremarks\u003e\n    /// You can pass any data through the command constructor.\n    /// \u003c/remarks\u003e\n    /// \u003cparam name=\"customString\"\u003e\u003c/param\u003e\n    public ExecutedCommand(string customString) \n    {\n        this._customString = customString;\n    }\n\n    /// \u003csummary\u003e\n    /// Execute command.\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"performanceInfo\"\u003e\u003csee cref=\"IPerformanceInfo\"/\u003e.\u003c/param\u003e\n    public void Execute(IPerformanceInfo performanceInfo)\n    {\n        // for example, write to the debug console some information\n        Debug.WriteLine(this.CommandName);\n        Debug.WriteLine(this._customString);\n        Debug.WriteLine($\"Method names count: {performanceInfo.MethodNames.Count}\");\n        this.IsCommandExecuted = true;\n    }\n}\n```\n\nYou can add a custom Command (IPerformanceCommand) and an Action so that they are executed at the end of the measurement the following way:\n\n```csharp\n// custom \"ExecutedCommand\" will be executed after performance watching is completed\nusing (PerformanceMeter\u003cPerformanceMeterController\u003e\n    .WatchingMethod()\n    .WithExecutingOnComplete\n        .Command(new ExecutedCommand(\"bla-bla-bla\"))\n        .Action((pi) =\u003e\n        {\n            Debug.WriteLine($\"Class name: {pi.ClassName}\");\n        })\n    .Start())\n{\n    return Ok();\n}\n```\n\nAs a result, at the end of the measurement the *Debug* console will display:\n\n```\nExecutedCommand\nbla-bla-bla\nMethod names count: 13\nClass name: Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController\n```\n\n### \u003ca name=\"SampleCustomExceptionHandler\"\u003e\u003c/a\u003e Adding Exception Handlers\n\nIf 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:\n\n```csharp\nusing (var pm = PerformanceMeter\u003cPerformanceMeterController\u003e.StartWatching())\n{\n    // execute an action that throws the exception to be handled by the exception handler\n    pm.Executing()\n        .WithExceptionHandler((ex) =\u003e Debug.WriteLine(ex.Message))\n        .Start(() =\u003e throw new Exception(\"Exception\"));\n\n    // execute action throws custom Exception with exception handler\n    pm.Executing\u003cCustomException\u003e()\n       .WithExceptionHandler((ex) =\u003e { Debug.WriteLine(ex.Message); })\n       .Start(() =\u003e\n       {\n           throw new CustomException(\"Custom exception was occured!\");\n       });\n\n    return Ok();\n}\n```\n\nWhere the `CustomException` class is (for example):\n\n```csharp\n/// \u003csummary\u003e\n/// Custom exception.\n/// \u003c/summary\u003e\npublic class CustomException : Exception\n{\n    public CustomException(string message) : base(message) { }\n\n    public CustomException(string message, Exception innerException) : base(message, innerException) { }\n\n    public CustomException() { }\n}\n```\n\nAs a result, the following will be displayed in the *Debug* console:\n\n```\nException\nCustom exception was occured!\n```\n\nIn 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:\n\n```csharp\n[ApiController]\n[Route(\"api/v1/[controller]\")]\npublic class PerformanceMeterController : ControllerBase\n{\n    /// \u003csummary\u003e\n    /// Static constructor.\n    /// \u003c/summary\u003e\n    static PerformanceMeterController()\n    {\n        // set default exception handler for PerformanceMeterController class\n        PerformanceMeter\u003cPerformanceMeterController\u003e.SetDefaultExceptionHandler((ex) =\u003e Debug.WriteLine(ex.Message));\n    }\n\n    // ... actions\n}\n```\n\n### \u003ca name=\"SampleSettingCacheTime\"\u003e\u003c/a\u003e Setting the Cache Time\n\nYou 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:\n\n```csharp\n[ApiController]\n[Route(\"api/v1/[controller]\")]\npublic class PerformanceMeterController : ControllerBase\n{\n    /// \u003csummary\u003e\n    /// Static constructor.\n    /// \u003c/summary\u003e\n    static PerformanceMeterController()\n    {\n        // set cache time for PerformanceMeterController class\n        PerformanceMeter\u003cPerformanceMeterController\u003e.SetMethodCallsCacheTime(5);\n    }\n\n    // ... actions\n}\n```\n\n### \u003ca name=\"SampleSetCallerAndSourceWithAborting\"\u003e\u003c/a\u003e Adding the info about the caller and call place origin (and aborting the measurement)\n\n* You can specify who is calling the method using the extension method `.CallerFrom(\"\u003ccaller_name\u003e\")` (either a string value or *IHttpContextAccessor* is passed to it) or a special attribute `[MethodCaller (\"\u003ccaller_name\u003e\")]` for the method. Moreover, if both the attribute and the extension method are used, then the value will be taken from the latter.\n\n* To add a call source for a performance measurement, use the extension method `.WithSettingData.CallerSourceData()`.\n\n* To stop/abort the performance measurement inside the *using* block, use the `.StopWatching()` extension method or the `Dispose()` method directly:\n\n```csharp\n[HttpPost(\"StartWatchingWithCallerName\")]\n[MethodCaller(\"testCaller\")]\npublic ActionResult\u003cstring\u003e StartWatchingWithCallerName([FromBody] string value)\n{\n    // the method’s performance info will be amended with the caller's name (if internal HttpContextAccessor is null)\n    using (var pm = PerformanceMeter\u003cPerformanceMeterController\u003e\n        .WatchingMethod()\n        .WithSettingData\n            .CallerSourceData()\n            .CallerFrom(\"Test caller\")\n        .Start())\n    {\n        pm.StopWatching(); // stop watching here (or you can use \"pm.Dispose();\")\n        Thread.Sleep(2000);\n\n        return Ok(value);\n    }\n}\n```\n\nAs a result of calling the `GetPerformanceInfo` method, you will get:\n\n```json\n{\n  \"methodCalls\": [\n    {\n      \"methodName\": \"StartWatchingWithCallerName\",\n      \"elapsed\": \"00:00:00.0019172\",\n      \"caller\": \"Test caller\",\n      \"startTime\": \"2019-12-06T13:35:45.3164507Z\",\n      \"endTime\": \"2019-12-06T13:35:45.3183679Z\",\n      \"customData\": {\n        \"customData123\": 123,\n        \"callerSourceLineNumber\": 525,\n        \"callerSource\": \"D:\\\\GitHub\\\\Unchase.FluentPerformanceMeter\\\\Unchase.FluentPerformanceMeter.TestWebAPI\\\\Controllers\\\\PerformanceMeterController.cs\"\n      },\n      \"steps\": []\n    }\n  ],\n  \"totalActivity\": [\n    {\n      \"methodName\": \"StartWatchingWithCallerName\",\n      \"callsCount\": 1\n    }\n  ],\n  \"currentActivity\": [\n    {\n      \"methodName\": \"StartWatchingWithCallerName\",\n      \"callsCount\": 0\n    }\n  ],\n  \"uptimeSince\": \"2019-12-06T13:35:45.2601668Z\",\n  \"className\": \"Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController\",\n  \"methodNames\": [\n    \"StartWatchingWithCallerName\"\n  ],\n  \"customData\": { },\n  \"timerFrequency\": 10000000\n}\n```\n\n## HowTos\n\n- [ ] Add HowTos in a future\n- [ ] ... [request for HowTo you need](https://github.com/unchase/Unchase.FluentPerformanceMeter/issues/new?title=DOC)\n\n## Roadmap\n\nSee the [changelog](CHANGELOG.md) for the further development plans and version history.\n\n## Thank me!\n\nIf you like what I am doing and you would like to thank me, please consider:\n\n[![Buy me a coffe!](img/buymeacoffe.png)](https://www.buymeacoffee.com/nikolaychebotov)\n\nThank you for your support!\n\n----------\n\nCopyright \u0026copy; 2019 [Nikolay Chebotov (**Unchase**)](https://github.com/unchase) - Provided under the [Apache License 2.0](LICENSE.md).\n\n","funding_links":["https://www.buymeacoffee.com/nikolaychebotov"],"categories":["Profiler","分析器","Performance tools"],"sub_categories":["Profiling"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funchase%2FUnchase.FluentPerformanceMeter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funchase%2FUnchase.FluentPerformanceMeter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funchase%2FUnchase.FluentPerformanceMeter/lists"}