
Souvent, il est nécessaire de collecter des statistiques sur les performances des méthodes d'application en temps réel (lorsque l'application est déjà en cours d'exécution) afin d'identifier ses goulots d'étranglement et de voir quelle partie de l'application ralentit.
En outre, il serait intéressant de sauvegarder le contexte d'appel dans lequel les performances sont suivies (comme les arguments de l'appel de méthode et les données arbitraires ajoutées par le développeur), en plus des données de performance elles-mêmes (heure de développement de la méthode, date de début et de fin de son appel).
Eh bien, la "cerise sur le gâteau" peut être considérée comme la commodité et la simplicité de l'outil utilisé, ce qui est également important.
Pour résoudre ces problèmes, une bibliothèque .NET multiplateforme open source Unchase.FluentPerformanceMeter a été développée .
Présentation
Ainsi, cet article est une description de l'utilisation de Unchase Fluent Performance Meter , une bibliothèque multiplateforme open source .Net Standard 2.0 conçue pour calculer les performances des méthodes.
La bibliothèque peut être utilisée dans les applications .NET Core et .NET Framework qui prennent en charge .Net Standard 2.0 et permet:
- Effectuer des mesures précises des performances des méthodes publiques pour les classes publiques de votre code et du code des bibliothèques utilisées (en fixant l'heure exacte du début et de la fin de la mesure);
- Ajoutez des données supplémentaires ( données personnalisées) aux résultats de mesure. Par exemple, les valeurs des paramètres d'entrée de la méthode et le résultat; ou données de contexte d'exécution de méthode; ou corellationId , par lequel il sera possible de relier plusieurs mesures de la performance des méthodes;
- Divisez la mesure des performances de la méthode en étapes distinctes (étapes) avec la fixation de vos propres données pour chaque étape. De plus, vous pouvez définir le temps d'exécution minimum , à partir duquel l'étape sera prise en compte dans la mesure (si l'étape est terminée plus rapidement, elle ne sera pas mesurée);
- Exclure des parties individuelles du code de la mesure des performances (par exemple, les appels à des méthodes individuelles dont le temps d'exécution n'a pas besoin d'être pris en compte lors de la mesure);
- Ajoutez vos propres commandes (commandes), dont l'exécution est garantie immédiatement après la fin de la mesure des performances de la méthode (par exemple, pour ajouter un traitement supplémentaire des résultats obtenus, comme l'enregistrement ou l'écriture de données dans le stockage);
- Ajoutez votre propre gestionnaire d'exceptions pour le code exécuté dans le contexte de la mesure des performances de la méthode (tous deux communs à toutes les mesures et à chaque mesure séparément);
- Définissez le temps de stockage des résultats des mesures de la productivité des méthodes, après quoi les résultats seront supprimés;
- Ajouter des données sur qui appelle la méthode (appelant) via IHttpContextAccesor ou la tâche de l'appelant dans le code aux résultats de mesure (par exemple, vous pouvez spécifier le nom du service externe qui a appelé la méthode);
- Ajouter aux résultats des mesures des données sur le lieu où la mesure des performances a été lancée (nom de fichier et numéro de ligne avec l'emplacement de l'appel dans le code);
- Interrompre la mesure des performances de la méthode avant la fin de son exécution .
Les données obtenues à la suite de la mesure des performances des méthodes peuvent être utilisées pour analyser les performances de l'application (ses parties individuelles, à la fois internes - code natif et externes - le code des bibliothèques utilisées) et affichées sous une forme graphique qui vous convient.
Table des matières
Pour commencer
Pour utiliser la bibliothèque, installez le package NuGet dans votre projet:
Utilisation manuelle du gestionnaire de packages NuGet :
Install-Package Unchase.FluentPerformanceMeter
Utilisation de la CLI .NET:
dotnet add package Unchase.FluentPerformanceMeter --version {version}
OĂą {version} est la version du package que vous souhaitez installer.
Par exemple, dotnet add package Unchase.FluentPerformanceMeter --version 1.0.0
Exemples d'utilisation
Mesure de la performance de la méthode
Voici un exemple simple d'utilisation de la bibliothèque (sans configuration ni paramètres supplémentaires) pour mesurer les performances de la SimpleWatchingMethodStart
(Action) SimpleWatchingMethodStart
contrĂ´leur SimpleWatchingMethodStart
(Controller) PerformanceMeterController
Asp.Net Core 2.2 WebAPI . Pour ce faire, vous pouvez utiliser la méthode d'extension .WatchingMethod().Start()
ou une fonctionnalité similaire .StartWatching()
.
Depuis la version 1.0.5, vous pouvez également utiliser .WatchingMethod().Start(SimpleWatchingMethodStart)
ou .StartWatching(SimpleWatchingMethodStart)
avec le nom de la méthode.
Tous les exemples d'utilisation de la bibliothèque peuvent être trouvés dans les projets Unchase.FluentPerformanceMeter.Test*
de ce référentiel .
/// <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(); } }
Pour obtenir les résultats des mesures de performances des méthodes publiques de la classe de contrôleur PerformanceMeterController
, vous pouvez appeler la méthode suivante:
/// <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); }
Après avoir appelé la méthode SimpleWatchingMethodStart
lors de l'appel de GetPerformanceInfo
nous obtenons:
{ "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 }
À partir de la version v1.1.0 , il est devenu possible de mesurer les performances des méthodes dans une application AspNetCore MVC à l'aide de DiagnosticSource
et de l'attribut spécial WatchingWithDiagnosticSourceAttribute
.
Pour ce faire, ajoutez le package Unchase.FluentPerformanceMeter.AspNetCore.Mvc au projet NuGet et ajoutez le code suivant Ă Startap.cs
:
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(); }
Marquez ensuite avec l'attribut WatchingWithDiagnosticSourceAttribute
ou des méthodes individuelles:
[HttpGet("SimpleWatchingMethodStart")] [WatchingWithDiagnosticSource] public ActionResult SimpleWatchingMethodStart() { return Ok(); }
ou toute la classe:
[ApiController] [Route("api/v1/[controller]")] [Produces("application/json")] [SwaggerTag("Unchase.PerformanceMeter Test WebAPI Controller")] [WatchingWithDiagnosticSource] public class PerformanceMeterController : ControllerBase { // measurable methods }
À partir de la version v1.2.0 , il est devenu possible d'ajouter des arguments d'appel aux données de mesure des performances de la méthode utilisateur dans une application AspNetCore MVC à l'aide de l'attribut spécial AddMethodArgumentsToCustomDataAttribute
en conjonction avec l'attribut WatchingWithDiagnosticSourceAttribute
:
[HttpPost("SimpleWatchingMethodStartWithArgs")] [WatchingWithDiagnosticSource] [AddMethodArgumentsToCustomData("actionArguments")] public ActionResult SimpleWatchingMethodStartWithArgs(DTOArgument arg) { return Ok(); }
Après avoir appelé la méthode SimpleWatchingMethodStartWithArgs
lors de l'appel de GetPerformanceInfo
nous obtenons:
{ "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 }
Mesurer les performances de la méthode utilisée par la bibliothèque
Pour mesurer les performances de la méthode publique de la classe publique de la bibliothèque tierce utilisée, vous devez définir explicitement la classe elle-même et son nom de méthode:
[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); }
La méthode exécutée renverra:
"00:00:01.0033040"
Vous pouvez obtenir des données sur l'appel de cette méthode via un appel:
/// <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); }
L'attribut IgnoreMethodPerformance
conçu de sorte que la méthode qu'il IgnoreMethodPerformance
n'est pas prise en compte lors de la mesure des performances.
En réponse à l'appel à cette méthode sera:
{ "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 }, // ... { "methodName": "Sleep", "callsCount": 1 } // ... ], "currentActivity": [ { "methodName": "Abort", "callsCount": 0 }, // ... { "methodName": "Sleep", "callsCount": 1 } // ... ], "uptimeSince": "2019-12-06T13:08:09.3357028Z", "className": "System.Threading.Thread", "methodNames": [ "Abort", // ... "Sleep", // ... ], "customData": {}, "timerFrequency": 10000000 }
Ajout de données supplémentaires (données personnalisées) et de pagination (étapes)
Vous pouvez ajouter des données supplémentaires (données personnalisées) pour toutes les mesures de performances des méthodes d'une classe particulière. Par exemple, dans le constructeur statique de la classe de contrôleur PerformanceMeterController
:
[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 }
En outre, vous pouvez ajouter des données supplémentaires (données personnalisées) pour une mesure spécifique à l'aide de la méthode d'extension .WithSettingData.CustomData("<key>", <value>)
(y compris via un attribut spécial de la méthode MethodCustomDataAttribute
) et pour chaque étape (étape ) de cette mesure, ajoutée à l'aide de la .Step("<step_name>")
extension .Step("<step_name>")
, Ă l'aide de la .AddCustomData("<key>", <value>)
extension .AddCustomData("<key>", <value>)
:
/// <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"); // ... } } }
Par conséquent, lorsque vous appelez GetPerformanceInfo
nous obtenons:
{ "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 }
Exception de la mesure (Ignorer)
Vous pouvez ignorer des parties individuelles de la méthode pour mesurer les performances (à l'aide de .Ignore()
ou .Executing().WithoutWatching().Start(<Action>)
), et ne pas enregistrer les étapes individuelles (méthode d'extension .StepIf("<step_name>", <minSaveMs>)
) s'ils ne remplissent pas la condition (le temps d'exécution de l'étape sera pris en compte dans le temps d'exécution de la méthode):
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 (not save, but consider duration in method performance watching) using (pm.StepIf("Skipped step", minSaveMs: 1000)) { Thread.Sleep(500); } // execute action without performance watching pm.Executing().WithoutWatching().Start(() => { Thread.Sleep(2000); }); return Ok(); }
En conséquence, nous obtenons:
{ "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 }
Ajout de commandes et d'actions
Pour ajouter une commande dont l'exécution sera garantie à la fin de la mesure des performances d'une méthode, vous devez créer une classe de commande qui implémentera l'interface IPerformanceCommand
.
Dans le même temps, vous pouvez transférer des données arbitraires via le constructeur de la commande créée qui sera utilisée lors de son exécution. Par exemple:
/// <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; } }
Vous pouvez ajouter une commande (IPerformanceCommand) et une action (Action) pour qu'elles soient exécutées à la fin de la mesure, de la manière suivante:
// 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(); }
Par conséquent, à la fin de la mesure des performances de la méthode dans la console de débogage , il affichera:
ExecutedCommand bla-bla-bla Method names count: 13 Class name: Unchase.FluentPerformanceMeter.TestWebAPI.Controllers.PerformanceMeterController
Ajout de gestionnaires d'exceptions
Si vous devez gérer les exceptions qui peuvent se produire lors de l'exécution d'une partie de la méthode dont les performances sont surveillées, vous devez ajouter un gestionnaire d'exceptions comme suit:
using (var pm = PerformanceMeter<PerformanceMeterController>.StartWatching()) { // execute action throws Exception with 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(); }
OĂą se trouve la classe CustomException
, par exemple:
/// <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() { } }
Par conséquent, la console de débogage affichera:
Exception Custom exception was occured!
En outre, vous pouvez spécifier un gestionnaire d'exceptions qui sera utilisé par défaut pour mesurer les performances de toute méthode de cette classe, par exemple, via le constructeur statique de la classe de contrôleur PerformanceMeterController
:
[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 }
Réglage de la durée de stockage des données (Set Cache Time)
Vous pouvez définir la durée de stockage des données des mesures de performances des méthodes, après quoi ces données seront supprimées. Pour chaque classe pour laquelle la mesure est effectuée, ce temps est défini séparément. Par exemple, l'heure peut être définie via le constructeur statique de la classe de contrôleur PerformanceMeterController
:
[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 }
Ajout de données sur la méthode d'appel et le lieu de l'appel (et interruption de la mesure des performances)
Vous pouvez spécifier qui appelle la méthode à l'aide de la méthode d'extension .CallerFrom("<caller_name>")
(une chaîne ou IHttpContextAccessor lui est transmise ) ou un attribut spécial de la méthode [MethodCaller("<caller_name>")]
. De plus, si à la fois l'attribut et la méthode d'extension sont utilisés, la valeur sera prise de cette dernière.
La méthode d'extension .WithSettingData.CallerSourceData()
utilisée pour ajouter un point d'appel pour mesurer les performances.
Pour arrêter de mesurer les performances à l'intérieur du bloc using
, utilisez .StopWatching()
méthode d'extension .StopWatching()
ou la méthode Dispose()
:
[HttpPost("StartWatchingWithCallerName")] [MethodCaller("testCaller")] public ActionResult<string> StartWatchingWithCallerName([FromBody] string value) { // method performance info will reach with caller 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); } }
À la suite de l'appel de la méthode GetPerformanceInfo
, GetPerformanceInfo
obtiendrez:
{ "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 }
Conclusion
Ainsi, la solution présentée nous permet d'automatiser partiellement la collecte d'informations sur les performances des méthodes d'application .NET d'une manière plutôt pratique. Les données collectées peuvent être visualisées d'une manière qui convient mieux à chaque développeur spécifique.
Bien sûr, la bibliothèque ne résout pas tous les problèmes liés à la collecte de données sur les performances des méthodes, mais cherche à simplifier les plus courants.
La bibliothèque est une solution open-source, toutes les suggestions et suggestions pour son développement ne sont que les bienvenues, tout comme les rapports de bogues (tout le monde peut suivre le lien ; l'auteur sera reconnaissant pour toute aide!).