├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── CNAME ├── LICENSE.md ├── README.md ├── README_RU.md ├── Unchase.FluentPerformanceMeter.AspNetCore.Mvc ├── Attributes │ ├── AddMethodArgumentsToCustomDataAttribute.cs │ ├── WatchingPerformanceAttribute.cs │ └── WatchingWithDiagnosticSourceAttribute.cs ├── Common │ └── PerformanceMeterCommonMethods.cs ├── DiagnosticSource │ ├── PerformanceDiagnosticObserver.cs │ └── PerformanceDiagnosticObserverBase.cs ├── Extensions │ ├── PerformanceDiagnosticSourceExtensions.cs │ └── PerformanceMeterMvcOptionsExtensions.cs ├── PerformanceMeterMiddleware.cs ├── PerformanceMeterMvcOptions.cs ├── Unchase.FluentPerformanceMeter.AspNetCore.Mvc.csproj └── Unchase.FluentPerformanceMeter.AspNetCore.Mvc.xml ├── Unchase.FluentPerformanceMeter.Test.Common ├── Commands │ ├── CustomDataCommand.cs │ └── ExecutedCommand.cs ├── CustomException.cs ├── FakeService.cs └── Unchase.FluentPerformanceMeter.Test.Common.csproj ├── Unchase.FluentPerformanceMeter.TestWebAPI ├── Controllers │ └── PerformanceMeterController.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── SwaggerExamples │ └── ResponseExamples.cs ├── Unchase.FluentPerformanceMeter.TestWebAPI.csproj ├── Unchase.FluentPerformanceMeter.TestWebAPI.xml ├── appsettings.Development.json └── appsettings.json ├── Unchase.FluentPerformanceMeter.TestWebAPI31 ├── Controllers │ └── PerformanceMeterController.cs ├── OpenApiExamples │ └── ResponseExamples.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Unchase.FluentPerformanceMeter.TestWebAPI31.csproj ├── Unchase.FluentPerformanceMeter.TestWebAPI31.xml ├── appsettings.Development.json └── appsettings.json ├── Unchase.FluentPerformanceMeter.Tests ├── PerformanceMeterTests.cs ├── TestMoq.cs └── Unchase.FluentPerformanceMeter.Tests.csproj ├── Unchase.FluentPerformanceMeter.sln ├── Unchase.FluentPerformanceMeter ├── Attributes │ ├── IgnoreMethodPerformanceAttribute.cs │ ├── MethodCallerAttribute.cs │ └── MethodCustomDataAttribute.cs ├── Builders │ ├── CodeExecutorBuilder.cs │ ├── ExecutedCommandsBuilder.cs │ ├── PerformanceMeterBuilder.cs │ └── SettingsBuilder.cs ├── Extensions │ ├── CommonExtensions.cs │ ├── PerformanceInfoStepExtensions.cs │ ├── PerformanceMeterBaseOptionsExtensions.cs │ └── PerformanceMeterExtensions.cs ├── Models │ ├── IPerformanceCommand.cs │ ├── IPerformanceInfo.cs │ ├── IPerformanceInfoStepData.cs │ ├── MethodCallInfo.cs │ ├── MethodCallsCount.cs │ ├── PerformanceInfo.cs │ └── PerformanceInfoStepData.cs ├── Performance.cs ├── PerformanceInfoStep.cs ├── PerformanceMeter.cs ├── PerformanceMeterBaseOptions.cs ├── StringBuilderCache.cs ├── Unchase.FluentPerformanceMeter.csproj └── Unchase.FluentPerformanceMeter.xml ├── _config.yml ├── appveyor.yml └── img ├── buymeacoffe.png ├── charts1.png ├── charts2.png ├── charts3.png ├── charts4.png ├── icon.png └── logo.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Road map 2 | 3 | - [ ] Fix open [issues](https://github.com/unchase/Unchase.FluentPerformanceMeter/issues/) 4 | - [ ] Gather [feedback](https://github.com/unchase/Unchase.FluentPerformanceMeter/issues/new) for plan further features to implement 5 | 6 | # Change log 7 | 8 | These are the changes to each version that has been released on the official [NuGet Gallery (Common)](https://www.nuget.org/packages/Unchase.FluentPerformanceMeter) and [NuGet Gallery (MVC)](https://www.nuget.org/packages/Unchase.FluentPerformanceMeter.AspNetCore.Mvc). 9 | 10 | ## v2.1.3 `(2021-02-16)` 11 | 12 | - [x] Update nuget-dependencies for test project 13 | 14 | ## v2.1.2 `(2020-06-05)` 15 | 16 | - [x] Add feature: allows to get PerformanceMeter of concrete class using DI with `RegisterPerformanceMeterScope` option setted to true (by default is true) 17 | 18 | ## v2.1.1 `(2020-05-02)` 19 | 20 | - [x] Add feature: allows to iterate executing Actions with `iterations` parameter in `Start` extension methods 21 | - [x] Update nuget-dependencies 22 | 23 | ## v2.1.0 `(2020-03-14)` 24 | 25 | - [x] Add feature: allows to to get the performance measurements results using the built-in **DI** 26 | 27 | ## v2.0.0 `(2020-03-08)` 28 | 29 | - [x] Add feature: 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` 30 | - [x] Add code refactoring 31 | 32 | ## v1.2.3 `(2020-03-03)` 33 | 34 | - [x] Replace all `Task` by `ValueTask` 35 | - [x] Add `Unchase.FluentPerformanceMeter.TestWebAPI31` (.NET Core 3.1 WebApi example project) 36 | - [x] Add `Interlocked.CompareExchange` for incrementing and decrementing calls counter 37 | 38 | ## v1.2.2 `(2020-02-20)` 39 | 40 | - [x] Add xml-comments to nuget packages 41 | 42 | ## v1.2.1 `(2020-01-28)` 43 | 44 | - [x] Add `HandleException` to `PerformanceDiagnosticObserver` class 45 | - [x] Add `GetExceptionHandler` and `GetDefaultExceptionHandler` methods to `PerformanceMeter` class 46 | 47 | **BREAKING CHANGES:** 48 | 49 | - [x] Rename `PerformanceClassDiagnosticObserver` to `PerformanceDiagnosticObserver` 50 | - [x] Should use `services.AddPerformanceDiagnosticObserver();` instead of `services.AddPerformanceDiagnosticObserver>();` in `ConfigureServices` method in `Startup.cs` 51 | 52 | ## v1.2.0 `(2020-01-18)` 53 | 54 | - [x] Add `TryAddCustomData` method into `PerformanceMeter` class 55 | - [x] Add `AddMethodArgumentsToCustomDataAttribute` attribute for adding arguments to measurement custom data with `DiagnosticSource` 56 | 57 | ## v1.1.2 `(2020-01-11)` 58 | 59 | - [x] Add `GetCustomData` method into `PerformanceMeter` and `PerformanceInfoStep` classes 60 | 61 | ## v1.1.0 `(2020-01-06)` 62 | 63 | [x] Add [`Unchase.FluentPerformanceMeter.AspNetCore.Mvc`](https://www.nuget.org/Unchase.FluentPerformanceMeter.AspNetCore.Mvc) project that allows to observe method's performance measurement with `DiagnosticSource` in *AspNetCore* project 64 | 65 | ## v1.0.5 `(2019-12-30)` 66 | 67 | - [x] Add more method overloads (with Action and Func parameter) for `PerformanceMeter.WatchingMethod` and extension method `PerformanceMeter.StartWatching` 68 | 69 | ## v1.0.0 `(2019-12-11)` 70 | 71 | - [x] Release version of `Unchase Fluent Performance Meter` *NuGet* package on the official [NuGet Gallery](https://www.nuget.org/Unchase.FluentPerformanceMeter) -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | performance.unchase.ru 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2019 Nikolay Chebotov (Unchase) 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/Attributes/AddMethodArgumentsToCustomDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Filters; 2 | using System; 3 | 4 | namespace Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Attributes 5 | { 6 | /// 7 | /// Attribute to adding action arguments to the measurement custom data with . 8 | /// 9 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 10 | public class AddMethodArgumentsToCustomDataAttribute : ActionFilterAttribute 11 | { 12 | #region Fileds 13 | 14 | private string _argumentsKey; 15 | 16 | #endregion 17 | 18 | #region Constructors 19 | 20 | /// 21 | /// Constructor for . 22 | /// 23 | /// Key for arguments in custom data storage. 24 | public AddMethodArgumentsToCustomDataAttribute(string argumentsKey = "arguments") 25 | { 26 | this._argumentsKey = argumentsKey; 27 | } 28 | 29 | #endregion 30 | 31 | #region Methods 32 | 33 | /// 34 | /// Call before the action executes, after model binding is complete. 35 | /// 36 | /// . 37 | public override void OnActionExecuting(ActionExecutingContext context) 38 | { 39 | if (context.HttpContext.Items.TryGetValue("PerformanceMeter", out var performanceMeter)) 40 | { 41 | var tryAddCustomDataMethod = performanceMeter.GetType().GetMethod("TryAddCustomData"); 42 | tryAddCustomDataMethod?.Invoke(performanceMeter, new object[] { this._argumentsKey, context.ActionArguments }); 43 | } 44 | 45 | base.OnActionExecuting(context); 46 | } 47 | 48 | #endregion 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/Attributes/WatchingPerformanceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Attributes 4 | { 5 | /// 6 | /// Attribute to indicate that performance watching will be performed with diagnostic source. 7 | /// 8 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)] 9 | public class WatchingPerformanceAttribute : Attribute { } 10 | } 11 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/Attributes/WatchingWithDiagnosticSourceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Attributes 4 | { 5 | /// 6 | /// Attribute to indicate that performance watching will be performed with diagnostic source. 7 | /// 8 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)] 9 | public class WatchingWithDiagnosticSourceAttribute : Attribute { } 10 | } 11 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/Common/PerformanceMeterCommonMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.Mvc.Controllers; 6 | using Microsoft.AspNetCore.Routing; 7 | using Unchase.FluentPerformanceMeter.Attributes; 8 | using Unchase.FluentPerformanceMeter.Builders; 9 | using Unchase.FluentPerformanceMeter.Extensions; 10 | 11 | namespace Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Common 12 | { 13 | internal static class PerformanceMeterCommonMethods 14 | { 15 | #region Properties 16 | 17 | internal static string QueryStringCustomDataKey { get; set; } = "pm_queryString"; 18 | 19 | internal static string UserIdentityNameCustomDataKey { get; } = "pm_userIdentityName"; 20 | 21 | internal static string PathCustomDataKey { get; } = "pm_path"; 22 | 23 | #endregion 24 | 25 | #region Methods 26 | 27 | internal static bool ShouldWatching(HttpRequest request, PerformanceMeterMvcOptions options) 28 | where TClass : ControllerBase 29 | { 30 | foreach (var ignored in options.IgnoredPaths) 31 | { 32 | if (ignored != null && request.Path.Value.Contains(ignored, StringComparison.OrdinalIgnoreCase)) 33 | { 34 | return false; 35 | } 36 | } 37 | 38 | return options.ShouldWatching?.Invoke(request) ?? true; 39 | } 40 | 41 | internal static bool CheckExcludedMethods(ControllerActionDescriptor controllerActionDescriptor, 42 | PerformanceMeterMvcOptions options) where TClass : ControllerBase 43 | { 44 | return options?.ExcludedMethods?.Contains(controllerActionDescriptor.ActionName) ?? false; 45 | } 46 | 47 | internal static bool CheckAnnotatedAttribute(ControllerActionDescriptor controllerActionDescriptor, 48 | PerformanceMeterMvcOptions options, Type attributeType) where TClass : ControllerBase 49 | { 50 | return (options == null || options.WatchForAnnotatedWithAttributeOnly) 51 | && !controllerActionDescriptor.MethodInfo 52 | .GetCustomAttributes(attributeType, false).Any() 53 | && !controllerActionDescriptor.ControllerTypeInfo 54 | .GetCustomAttributes(attributeType, false).Any(); 55 | } 56 | 57 | internal static bool CheckIgnoreMethodPerformanceAttribute( 58 | ControllerActionDescriptor controllerActionDescriptor, PerformanceMeterMvcOptions options) 59 | where TClass : ControllerBase 60 | { 61 | return (options == null || options.UseIgnoreMethodPerformanceAttribute) 62 | && controllerActionDescriptor.MethodInfo 63 | .GetCustomAttributes(typeof(IgnoreMethodPerformanceAttribute), false).Any(); 64 | } 65 | 66 | internal static SettingsBuilder AddCustomDataFromCustomAttributes(this SettingsBuilder settingsBuilder, HttpContext context, ControllerActionDescriptor controllerActionDescriptor, PerformanceMeterMvcOptions options) where TClass : ControllerBase 67 | { 68 | if (options == null || options.AddCustomDataFromCustomAttributes) 69 | { 70 | foreach (MethodCustomDataAttribute methodCustomData in controllerActionDescriptor.MethodInfo.GetCustomAttributes(typeof(MethodCustomDataAttribute), false)) 71 | { 72 | settingsBuilder = settingsBuilder.CustomData(methodCustomData.Key, methodCustomData.Value); 73 | } 74 | if (context.Request.QueryString.HasValue) 75 | settingsBuilder = settingsBuilder.CustomData(QueryStringCustomDataKey, context.Request.QueryString.Value); 76 | if (context.User.Identity.IsAuthenticated) 77 | settingsBuilder = settingsBuilder.CustomData(UserIdentityNameCustomDataKey, context.User.Identity.Name); 78 | 79 | // add caller from attributes 80 | settingsBuilder = settingsBuilder.CallerFrom(context.Connection?.RemoteIpAddress?.ToString() ?? context.Connection?.LocalIpAddress?.ToString()); 81 | foreach (MethodCallerAttribute methodCaller in controllerActionDescriptor.MethodInfo.GetCustomAttributes(typeof(MethodCallerAttribute), false)) 82 | { 83 | settingsBuilder = settingsBuilder.CallerFrom(methodCaller.Caller); 84 | } 85 | } 86 | 87 | // add route path to custom data 88 | if (options == null || options.AddRoutePathToCustomData) 89 | { 90 | var url = StringBuilderCache.Get() 91 | .Append(context.Request.Scheme) 92 | .Append("://") 93 | .Append(context.Request.Host.Value) 94 | .Append(context.Request.PathBase.Value) 95 | .Append(context.Request.Path.Value) 96 | .Append(context.Request.QueryString.Value) 97 | .ToStringRecycle(); 98 | 99 | var routeData = context.GetRouteData(); 100 | if (routeData != null) 101 | { 102 | settingsBuilder = settingsBuilder.CustomData(PathCustomDataKey, routeData.Values["controller"] + "/" + routeData.Values["action"]); 103 | } 104 | else 105 | { 106 | if (url.Length > 50) 107 | url = url.Remove(50); 108 | settingsBuilder = settingsBuilder.CustomData(PathCustomDataKey, url); 109 | } 110 | } 111 | 112 | return settingsBuilder; 113 | } 114 | 115 | #endregion 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/DiagnosticSource/PerformanceDiagnosticObserver.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Mvc.Abstractions; 3 | using Microsoft.AspNetCore.Mvc.Controllers; 4 | using Microsoft.Extensions.DiagnosticAdapter; 5 | using System; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.Extensions.Options; 8 | using Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Attributes; 9 | using Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Common; 10 | using Unchase.FluentPerformanceMeter.Builders; 11 | 12 | namespace Unchase.FluentPerformanceMeter.AspNetCore.Mvc.DiagnosticSource 13 | { 14 | /// 15 | /// The class for watching performance with diagnostic source. 16 | /// 17 | /// Class with public methods. 18 | public sealed class PerformanceDiagnosticObserver : PerformanceDiagnosticObserverBase where TClass : ControllerBase 19 | { 20 | #region Fields 21 | 22 | private readonly IOptions> _options; 23 | 24 | internal PerformanceMeterMvcOptions Options => _options.Value; 25 | 26 | #endregion 27 | 28 | #region Constructors 29 | 30 | /// 31 | /// Constructor for . 32 | /// 33 | /// The options, containing the rules to apply. 34 | public PerformanceDiagnosticObserver(IOptions> options) 35 | { 36 | _options = options ?? throw new ArgumentNullException(nameof(options)); 37 | } 38 | 39 | #endregion 40 | 41 | #region Methods 42 | 43 | /// 44 | /// Check matching the name. 45 | /// 46 | /// Name. 47 | /// 48 | /// Returns true if name is match. 49 | /// 50 | protected override bool IsMatch(string name) 51 | { 52 | return name == "Microsoft.AspNetCore"; 53 | } 54 | 55 | /// 56 | /// On Microsoft.AspNetCore.Hosting.HttpRequestIn. 57 | /// 58 | [DiagnosticName("Microsoft.AspNetCore.Hosting.HttpRequestIn")] 59 | public void OnHttpRequestIn() { } 60 | 61 | /// 62 | /// On Microsoft.AspNetCore.Mvc.BeforeAction. 63 | /// 64 | /// . 65 | /// . 66 | [DiagnosticName("Microsoft.AspNetCore.Mvc.BeforeAction")] 67 | public void OnBeforeAction(HttpContext httpContext, ActionDescriptor actionDescriptor) 68 | { 69 | if (PerformanceMeterCommonMethods.ShouldWatching(httpContext.Request, Options)) 70 | { 71 | if (actionDescriptor is ControllerActionDescriptor controllerActionDescriptor) 72 | { 73 | if (PerformanceMeterCommonMethods.CheckExcludedMethods(controllerActionDescriptor, Options)) 74 | { 75 | return; 76 | } 77 | 78 | if (PerformanceMeterCommonMethods.CheckAnnotatedAttribute(controllerActionDescriptor, Options, 79 | typeof(WatchingWithDiagnosticSourceAttribute))) 80 | { 81 | return; 82 | } 83 | 84 | if (PerformanceMeterCommonMethods.CheckIgnoreMethodPerformanceAttribute(controllerActionDescriptor, 85 | Options)) 86 | { 87 | return; 88 | } 89 | 90 | var performanceMeterBuilder = PerformanceMeter 91 | .WatchingMethod(controllerActionDescriptor.ActionName) 92 | .WithSettingData; 93 | 94 | // add custom data from custom attributes 95 | performanceMeterBuilder = 96 | performanceMeterBuilder.AddCustomDataFromCustomAttributes(httpContext, controllerActionDescriptor, 97 | Options); 98 | 99 | httpContext.Items["PerformanceMeter"] = performanceMeterBuilder.Start(); 100 | } 101 | } 102 | } 103 | 104 | /// 105 | /// On Microsoft.AspNetCore.Hosting.HttpRequestIn.Start. 106 | /// 107 | /// . 108 | [DiagnosticName("Microsoft.AspNetCore.Hosting.HttpRequestIn.Start")] 109 | public void OnHttpRequestInStart(HttpContext httpContext) { } 110 | 111 | /// 112 | /// On Microsoft.AspNetCore.Mvc.AfterAction. 113 | /// 114 | /// . 115 | /// . 116 | [DiagnosticName("Microsoft.AspNetCore.Mvc.AfterAction")] 117 | public void OnAfterAction(HttpContext httpContext, ActionDescriptor actionDescriptor) { } 118 | 119 | /// 120 | /// On Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop. 121 | /// 122 | /// . 123 | [DiagnosticName("Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop")] 124 | public void OnHttpRequestInStop(HttpContext httpContext) 125 | { 126 | if (!httpContext.Items.TryGetValue("PerformanceMeter", out var performanceMeter)) 127 | return; 128 | 129 | ((PerformanceMeter)performanceMeter).Dispose(); 130 | } 131 | 132 | /// 133 | /// Handle the Exception. 134 | /// 135 | /// Exception. 136 | protected override void HandleException(Exception error) 137 | { 138 | var exceptionHandler = PerformanceMeter.GetDefaultExceptionHandler(); 139 | exceptionHandler(error); 140 | } 141 | 142 | #endregion 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/DiagnosticSource/PerformanceDiagnosticObserverBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | 5 | namespace Unchase.FluentPerformanceMeter.AspNetCore.Mvc.DiagnosticSource 6 | { 7 | /// 8 | /// The base class for performance diagnostic source. 9 | /// 10 | public abstract class PerformanceDiagnosticObserverBase : IObserver 11 | { 12 | #region Fields 13 | 14 | private readonly List _subscriptions = new List(); 15 | 16 | #endregion 17 | 18 | #region Methods 19 | 20 | /// 21 | /// Check matching the name. 22 | /// 23 | /// Name. 24 | /// 25 | /// Returns true if name is match. 26 | /// 27 | protected abstract bool IsMatch(string name); 28 | 29 | void IObserver.OnNext(DiagnosticListener diagnosticListener) 30 | { 31 | if (IsMatch(diagnosticListener.Name)) 32 | { 33 | var subscription = diagnosticListener.SubscribeWithAdapter(this); 34 | _subscriptions.Add(subscription); 35 | } 36 | } 37 | 38 | void IObserver.OnError(Exception error) 39 | { 40 | HandleException(error); 41 | } 42 | 43 | void IObserver.OnCompleted() 44 | { 45 | _subscriptions.ForEach(x => x.Dispose()); 46 | _subscriptions.Clear(); 47 | } 48 | 49 | /// 50 | /// Handle the exception. 51 | /// 52 | /// Exception. 53 | protected abstract void HandleException(Exception error); 54 | 55 | #endregion 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/Extensions/PerformanceDiagnosticSourceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.DependencyInjection.Extensions; 5 | using System.Diagnostics; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Unchase.FluentPerformanceMeter.AspNetCore.Mvc.DiagnosticSource; 9 | 10 | namespace Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Extensions 11 | { 12 | /// 13 | /// Extension methods for Diagnostic Source. 14 | /// 15 | public static class PerformanceDiagnosticSourceExtensions 16 | { 17 | #region Extension methods 18 | 19 | #region DiagnosticObserver 20 | 21 | /// 22 | /// Register services. 23 | /// 24 | /// . 25 | /// An to configure options for Unchase.FluentPerformanceMeter. 26 | public static IServiceCollection AddPerformanceDiagnosticObserver(this IServiceCollection services, Action> configureOptions = null) where TClass : ControllerBase 27 | { 28 | if (configureOptions != null) 29 | { 30 | services.Configure(configureOptions); 31 | } 32 | services.Configure>(o => PerformanceMeter.Configure(o)); 33 | 34 | services.TryAddEnumerable( 35 | ServiceDescriptor.Transient>()); 36 | 37 | return services; 38 | } 39 | 40 | /// 41 | /// Use diagnostic source subscriptions. 42 | /// 43 | /// . 44 | public static IApplicationBuilder UsePerformanceDiagnosticObserver(this IApplicationBuilder app) 45 | { 46 | var diagnosticObservers = app.ApplicationServices.GetServices(); 47 | foreach (var diagnosticObserver in diagnosticObservers) 48 | { 49 | DiagnosticListener.AllListeners.Subscribe(diagnosticObserver); 50 | } 51 | 52 | return app; 53 | } 54 | 55 | #endregion 56 | 57 | #region PerformanceMeter 58 | 59 | /// 60 | /// Register Unchase.FluentPerformanceMeter services. 61 | /// 62 | /// . 63 | /// An to configure options for Unchase.FluentPerformanceMeter. 64 | public static IServiceCollection AddPerformanceMeter(this IServiceCollection services, Action> configureOptions = null) where TClass : ControllerBase 65 | { 66 | // ensure that IHttpContextAccessor was added 67 | services.AddHttpContextAccessor(); 68 | 69 | if (configureOptions != null) 70 | { 71 | services.Configure(configureOptions); 72 | } 73 | 74 | services.Configure>(o => 75 | { 76 | PerformanceMeter.Configure(o); 77 | }); 78 | 79 | services.AddScoped(typeof(PerformanceMeter), serviceProvider => 80 | { 81 | var httpContext = serviceProvider.GetRequiredService()?.HttpContext; 82 | return httpContext?.Items[$"PerformanceMeter{httpContext.TraceIdentifier}"]; 83 | }); 84 | 85 | return services; 86 | } 87 | 88 | /// 89 | /// Use middleware for watching methods performance. 90 | /// 91 | /// . 92 | public static IApplicationBuilder UsePerformanceMeterFor(this IApplicationBuilder app) where TClass : ControllerBase 93 | { 94 | return app.UseMiddleware>(); 95 | } 96 | 97 | #endregion 98 | 99 | #endregion 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/Extensions/PerformanceMeterMvcOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Extensions 5 | { 6 | /// 7 | /// Handy extensions for . 8 | /// 9 | public static class PerformanceMeterMvcOptionsExtensions 10 | { 11 | #region Extension methods 12 | 13 | /// 14 | /// Excludes a path from being watched, convenience method for chaining, basically .Add(assembly) 15 | /// 16 | /// Type of the controller. 17 | /// The options to exclude the type on. 18 | /// The path to exclude from profiled. 19 | /// 20 | /// Returns . 21 | /// 22 | public static PerformanceMeterMvcOptions IgnorePath(this PerformanceMeterMvcOptions options, string path) where TClass : ControllerBase 23 | { 24 | options.IgnoredPaths.Add(path); 25 | return options; 26 | } 27 | 28 | /// 29 | /// Add common custom data of the controller. 30 | /// 31 | /// Type of the controller. 32 | /// The options. 33 | /// Key. 34 | /// Value. 35 | /// 36 | /// Returns . 37 | /// 38 | public static PerformanceMeterMvcOptions AddCustomData(this PerformanceMeterMvcOptions options, string key, object value) where TClass : ControllerBase 39 | { 40 | PerformanceMeter.AddCustomData(key, value); 41 | return options; 42 | } 43 | 44 | /// 45 | /// Set the time in minutes to clear collection of the method calls. 46 | /// 47 | /// Type of the controller. 48 | /// The options. 49 | /// Time in minutes to clear list of the method calls. 50 | /// 51 | /// Returns . 52 | /// 53 | public static PerformanceMeterMvcOptions SetMethodCallsCacheTime(this PerformanceMeterMvcOptions options, int minutes) where TClass : ControllerBase 54 | { 55 | PerformanceMeter.SetMethodCallsCacheTime(minutes); 56 | return options; 57 | } 58 | 59 | /// 60 | /// Set Action to handle exceptions that occur by default. 61 | /// 62 | /// Type of the controller. 63 | /// The options. 64 | /// Action to handle exceptions that occur. 65 | /// 66 | /// Returns . 67 | /// 68 | public static PerformanceMeterMvcOptions SetDefaultExceptionHandler(this PerformanceMeterMvcOptions options, Action exceptionHandler) where TClass : ControllerBase 69 | { 70 | PerformanceMeter.SetDefaultExceptionHandler(exceptionHandler); 71 | return options; 72 | } 73 | 74 | #endregion 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/PerformanceMeterMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Http.Features; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Controllers; 7 | using Microsoft.Extensions.Options; 8 | using Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Attributes; 9 | using Unchase.FluentPerformanceMeter.Builders; 10 | using Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Common; 11 | 12 | namespace Unchase.FluentPerformanceMeter.AspNetCore.Mvc 13 | { 14 | /// 15 | /// Represents a middleware that starts and stops an Unchase.FluentPerformanceMeter. 16 | /// 17 | public class PerformanceMeterMiddleware where TClass : ControllerBase 18 | { 19 | #region Fields 20 | 21 | private readonly RequestDelegate _next; 22 | 23 | private readonly IOptions> _options; 24 | 25 | internal PerformanceMeterMvcOptions Options => _options.Value; 26 | 27 | #endregion 28 | 29 | #region Constructors 30 | 31 | /// 32 | /// Creates a new instance of 33 | /// 34 | /// The delegate representing the next middleware in the request pipeline. 35 | /// The middleware options, containing the rules to apply. 36 | /// Throws when , or is null. 37 | public PerformanceMeterMiddleware( 38 | RequestDelegate next, 39 | IOptions> options) 40 | { 41 | _next = next ?? throw new ArgumentNullException(nameof(next)); 42 | _options = options ?? throw new ArgumentNullException(nameof(options)); 43 | } 44 | 45 | #endregion 46 | 47 | #region Methods 48 | 49 | /// 50 | /// Executes the Unchase.FluentPerformanceMeter-wrapped middleware. 51 | /// 52 | /// The for the current request. 53 | /// A task that represents the execution of the MiniProfiler-wrapped middleware. 54 | /// Throws when is null. 55 | public async Task Invoke(HttpContext context) 56 | { 57 | _ = context ?? throw new ArgumentNullException(nameof(context)); 58 | 59 | if (PerformanceMeterCommonMethods.ShouldWatching(context.Request, Options)) 60 | { 61 | var controllerActionDescriptor = (context.Features[typeof(IEndpointFeature)] as IEndpointFeature)?.Endpoint?.Metadata?.GetMetadata(); 62 | if (controllerActionDescriptor != null) 63 | { 64 | if (PerformanceMeterCommonMethods.CheckExcludedMethods(controllerActionDescriptor, Options)) 65 | { 66 | await _next(context); // don't watching, only relay 67 | } 68 | 69 | if (PerformanceMeterCommonMethods.CheckAnnotatedAttribute(controllerActionDescriptor, Options, 70 | typeof(WatchingPerformanceAttribute))) 71 | { 72 | await _next(context); // don't watching, only relay 73 | } 74 | 75 | if (PerformanceMeterCommonMethods.CheckIgnoreMethodPerformanceAttribute(controllerActionDescriptor, Options)) 76 | { 77 | await _next(context); // don't watching, only relay 78 | } 79 | 80 | if (controllerActionDescriptor.ControllerTypeInfo.UnderlyingSystemType != typeof(TClass)) 81 | { 82 | await _next(context); // don't watching, only relay 83 | } 84 | 85 | var performanceMeterBuilder = PerformanceMeter 86 | .WatchingMethod(controllerActionDescriptor.ActionName) 87 | .WithSettingData; 88 | 89 | // add custom data from custom attributes 90 | performanceMeterBuilder = 91 | performanceMeterBuilder.AddCustomDataFromCustomAttributes(context, controllerActionDescriptor, 92 | Options); 93 | 94 | using (performanceMeterBuilder.Start()) 95 | { 96 | // execute the pipe 97 | await _next(context); 98 | } 99 | } 100 | else 101 | { 102 | // don't watching, only relay 103 | await _next(context); 104 | } 105 | } 106 | else 107 | { 108 | // don't watching, only relay 109 | await _next(context); 110 | } 111 | } 112 | 113 | #endregion 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/PerformanceMeterMvcOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | namespace Unchase.FluentPerformanceMeter.AspNetCore.Mvc 7 | { 8 | /// 9 | /// Options for configuring Unchase.FluentPerformanceMeter. 10 | /// 11 | public class PerformanceMeterMvcOptions : PerformanceMeterBaseOptions where TClass : ControllerBase 12 | { 13 | #region Properties 14 | 15 | /// 16 | /// When is called, if the current request URL contains any items in this property, 17 | /// no performance watching will be instantiated and no results will be displayed. 18 | /// Default value is { "/content/", "/scripts/", "/favicon.ico" }. 19 | /// 20 | public HashSet IgnoredPaths { get; } = new HashSet { 21 | "/content/", 22 | "/scripts/", 23 | "/favicon.ico" 24 | }; 25 | 26 | /// 27 | /// Set a function to control whether a given request should be watching at all. 28 | /// 29 | public Func ShouldWatching { get; set; } = _ => true; 30 | 31 | /// 32 | /// Watch actions performance annotated with special attribute. 33 | /// Default value is true. 34 | /// 35 | public bool WatchForAnnotatedWithAttributeOnly { get; set; } = true; 36 | 37 | /// 38 | /// Add route path to custom data. 39 | /// Default value is true. 40 | /// 41 | public bool AddRoutePathToCustomData { get; set; } = true; 42 | 43 | #endregion 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/Unchase.FluentPerformanceMeter.AspNetCore.Mvc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | 2.1.3 7 | Unchase 8 | Unchase 9 | Unchase Fluent Performance Meter is an open-source and cross-platform .Net Standart 2.0 library is designed for the method’s performance measurement. 10 | Copyrigth © Unchase 2019 11 | Apache-2.0 12 | https://github.com/unchase/Unchase.PerformanceMeter 13 | icon.png 14 | 15 | https://github.com/unchase/Unchase.PerformanceMeter 16 | Github 17 | performance benchmark benchmarking csharp dot-net 18 | en 19 | 2.1.3.0 20 | 2.1.3.0 21 | Unchase.FluentPerformanceMeter.AspNetCore.Mvc.xml 22 | 23 | 24 | 25 | Unchase.FluentPerformanceMeter.AspNetCore.Mvc.xml 26 | 27 | 28 | 29 | Unchase.FluentPerformanceMeter.AspNetCore.Mvc.xml 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | True 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.AspNetCore.Mvc/Unchase.FluentPerformanceMeter.AspNetCore.Mvc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Unchase.FluentPerformanceMeter.AspNetCore.Mvc 5 | 6 | 7 | 8 | 9 | Attribute to adding action arguments to the measurement custom data with . 10 | 11 | 12 | 13 | 14 | Constructor for . 15 | 16 | Key for arguments in custom data storage. 17 | 18 | 19 | 20 | Call before the action executes, after model binding is complete. 21 | 22 | . 23 | 24 | 25 | 26 | Attribute to indicate that performance watching will be performed with diagnostic source. 27 | 28 | 29 | 30 | 31 | Attribute to indicate that performance watching will be performed with diagnostic source. 32 | 33 | 34 | 35 | 36 | The class for watching performance with diagnostic source. 37 | 38 | Class with public methods. 39 | 40 | 41 | 42 | Constructor for . 43 | 44 | The options, containing the rules to apply. 45 | 46 | 47 | 48 | Check matching the name. 49 | 50 | Name. 51 | 52 | Returns true if name is match. 53 | 54 | 55 | 56 | 57 | On Microsoft.AspNetCore.Hosting.HttpRequestIn. 58 | 59 | 60 | 61 | 62 | On Microsoft.AspNetCore.Mvc.BeforeAction. 63 | 64 | . 65 | . 66 | 67 | 68 | 69 | On Microsoft.AspNetCore.Hosting.HttpRequestIn.Start. 70 | 71 | . 72 | 73 | 74 | 75 | On Microsoft.AspNetCore.Mvc.AfterAction. 76 | 77 | . 78 | . 79 | 80 | 81 | 82 | On Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop. 83 | 84 | . 85 | 86 | 87 | 88 | Handle the Exception. 89 | 90 | Exception. 91 | 92 | 93 | 94 | The base class for performance diagnostic source. 95 | 96 | 97 | 98 | 99 | Check matching the name. 100 | 101 | Name. 102 | 103 | Returns true if name is match. 104 | 105 | 106 | 107 | 108 | Handle the exception. 109 | 110 | Exception. 111 | 112 | 113 | 114 | Extension methods for Diagnostic Source. 115 | 116 | 117 | 118 | 119 | Register services. 120 | 121 | . 122 | An to configure options for Unchase.FluentPerformanceMeter. 123 | 124 | 125 | 126 | Use diagnostic source subscriptions. 127 | 128 | . 129 | 130 | 131 | 132 | Register Unchase.FluentPerformanceMeter services. 133 | 134 | . 135 | An to configure options for Unchase.FluentPerformanceMeter. 136 | 137 | 138 | 139 | Use middleware for watching methods performance. 140 | 141 | . 142 | 143 | 144 | 145 | Handy extensions for . 146 | 147 | 148 | 149 | 150 | Excludes a path from being watched, convenience method for chaining, basically .Add(assembly) 151 | 152 | Type of the controller. 153 | The options to exclude the type on. 154 | The path to exclude from profiled. 155 | 156 | Returns . 157 | 158 | 159 | 160 | 161 | Add common custom data of the controller. 162 | 163 | Type of the controller. 164 | The options. 165 | Key. 166 | Value. 167 | 168 | Returns . 169 | 170 | 171 | 172 | 173 | Set the time in minutes to clear collection of the method calls. 174 | 175 | Type of the controller. 176 | The options. 177 | Time in minutes to clear list of the method calls. 178 | 179 | Returns . 180 | 181 | 182 | 183 | 184 | Set Action to handle exceptions that occur by default. 185 | 186 | Type of the controller. 187 | The options. 188 | Action to handle exceptions that occur. 189 | 190 | Returns . 191 | 192 | 193 | 194 | 195 | Represents a middleware that starts and stops an Unchase.FluentPerformanceMeter. 196 | 197 | 198 | 199 | 200 | Creates a new instance of 201 | 202 | The delegate representing the next middleware in the request pipeline. 203 | The middleware options, containing the rules to apply. 204 | Throws when , or is null. 205 | 206 | 207 | 208 | Executes the Unchase.FluentPerformanceMeter-wrapped middleware. 209 | 210 | The for the current request. 211 | A task that represents the execution of the MiniProfiler-wrapped middleware. 212 | Throws when is null. 213 | 214 | 215 | 216 | Options for configuring Unchase.FluentPerformanceMeter. 217 | 218 | 219 | 220 | 221 | When is called, if the current request URL contains any items in this property, 222 | no performance watching will be instantiated and no results will be displayed. 223 | Default value is { "/content/", "/scripts/", "/favicon.ico" }. 224 | 225 | 226 | 227 | 228 | Set a function to control whether a given request should be watching at all. 229 | 230 | 231 | 232 | 233 | Watch actions performance annotated with special attribute. 234 | Default value is true. 235 | 236 | 237 | 238 | 239 | Add route path to custom data. 240 | Default value is true. 241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.Test.Common/Commands/CustomDataCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Unchase.FluentPerformanceMeter.Models; 3 | 4 | namespace Unchase.FluentPerformanceMeter.Test.Common.Commands 5 | { 6 | /// 7 | /// Custom executed command which work with method calls custom data. 8 | /// 9 | public class CustomDataCommand : IPerformanceCommand 10 | { 11 | /// 12 | /// Executed command name. 13 | /// 14 | public string CommandName => this.GetType().Name; 15 | 16 | /// 17 | /// Execute command. 18 | /// 19 | /// . 20 | public void Execute(IPerformanceInfo performanceInfo) 21 | { 22 | foreach (var methodCall in performanceInfo.MethodCalls) 23 | { 24 | foreach (var customData in methodCall.CustomData) 25 | { 26 | Debug.WriteLine($"{customData.Key} : {customData.Value}"); 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.Test.Common/Commands/ExecutedCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Unchase.FluentPerformanceMeter.Models; 3 | 4 | namespace Unchase.FluentPerformanceMeter.Test.Common.Commands 5 | { 6 | /// 7 | /// Custom executed command. 8 | /// 9 | public class ExecutedCommand : IPerformanceCommand 10 | { 11 | /// 12 | /// Executed commad name. 13 | /// 14 | public string CommandName => this.GetType().Name; 15 | 16 | private string _customString { get; } 17 | 18 | internal bool IsCommandExecuted { get; private set; } 19 | 20 | /// 21 | /// Constructor. 22 | /// 23 | /// 24 | /// You can pass any data through the command constructor. 25 | /// 26 | /// 27 | public ExecutedCommand(string customString) 28 | { 29 | this._customString = customString; 30 | } 31 | 32 | /// 33 | /// Execute command. 34 | /// 35 | /// . 36 | public void Execute(IPerformanceInfo performanceInfo) 37 | { 38 | // for example, write to the debug console some information 39 | Debug.WriteLine(this.CommandName); 40 | Debug.WriteLine(this._customString); 41 | Debug.WriteLine($"Method names count: {performanceInfo.MethodNames.Count}"); 42 | this.IsCommandExecuted = true; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.Test.Common/CustomException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Unchase.FluentPerformanceMeter.Test.Common 4 | { 5 | /// 6 | /// Custom exception. 7 | /// 8 | public class CustomException : Exception 9 | { 10 | public CustomException(string message) : base(message) { } 11 | 12 | public CustomException(string message, Exception innerException) : base(message, innerException) { } 13 | 14 | public CustomException() { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.Test.Common/FakeService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | 3 | namespace Unchase.FluentPerformanceMeter.Test.Common 4 | { 5 | /// 6 | /// Fake service class. 7 | /// 8 | public class FakeService 9 | { 10 | /// 11 | /// Fake method 1. 12 | /// 13 | public static void FakeMethod1() 14 | { 15 | for (int i = 0; i < 1000000; i++) 16 | { 17 | var t = i.ToString() + (i + 1).ToString(); 18 | } 19 | } 20 | 21 | /// 22 | /// Fake method 2. 23 | /// 24 | /// 25 | /// Return string. 26 | /// 27 | public static string FakeMethod2(string parameter) 28 | { 29 | Thread.Sleep(1000); 30 | return $"Fake method result with parameter value = {parameter}"; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.Test.Common/Unchase.FluentPerformanceMeter.Test.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Hosting; 3 | 4 | namespace Unchase.FluentPerformanceMeter.TestWebAPI 5 | { 6 | /// 7 | /// Startup program. 8 | /// 9 | public class Program 10 | { 11 | /// 12 | /// Main method. 13 | /// 14 | /// 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | /// 21 | /// Create WebHost. 22 | /// 23 | /// Arguments. 24 | /// . 25 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 26 | WebHost.CreateDefaultBuilder(args) 27 | .UseStartup(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:62927", 7 | "sslPort": 44388 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Unchase.FluentPerformanceMeter.TestWebAPI": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Swashbuckle.AspNetCore.Filters; 7 | using Swashbuckle.AspNetCore.Swagger; 8 | using System; 9 | using System.IO; 10 | using Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Extensions; 11 | using Unchase.FluentPerformanceMeter.TestWebAPI.Controllers; 12 | using Unchase.FluentPerformanceMeter.TestWebAPI.SwaggerExamples; 13 | 14 | namespace Unchase.FluentPerformanceMeter.TestWebAPI 15 | { 16 | /// 17 | /// Startup. 18 | /// 19 | public class Startup 20 | { 21 | /// 22 | /// Constructor. 23 | /// 24 | /// . 25 | public Startup(IConfiguration configuration) 26 | { 27 | Configuration = configuration; 28 | } 29 | 30 | /// 31 | /// Configuration. 32 | /// 33 | public IConfiguration Configuration { get; } 34 | 35 | /// 36 | /// This method gets called by the runtime. 37 | /// Use this method to add services to the container. 38 | /// 39 | /// . 40 | public void ConfigureServices(IServiceCollection services) 41 | { 42 | services.AddHttpContextAccessor(); 43 | 44 | services.AddPerformanceDiagnosticObserver(); 45 | 46 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); 47 | 48 | services.AddSwaggerGen(c => 49 | { 50 | c.SwaggerDoc("v1", new Info 51 | { 52 | Title = "Unchase.PerformanceMeter Test WebAPI", 53 | Version = "v1", 54 | Description = "Unchase.PerformanceMeter Test WebAPI", 55 | License = new License 56 | { 57 | Name = "Apache-2.0", 58 | Url = "https://github.com/unchase/Unchase.PerformanceMeter/blob/master/LICENSE.md" 59 | } 60 | }); 61 | 62 | // add OpenApi filters with examples 63 | c.ExampleFilters(); 64 | 65 | // add documentation from xml-file comments from assemblies 66 | foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 67 | { 68 | if (!assembly.IsDynamic) 69 | { 70 | var xmlFile = $"{assembly.GetName().Name}.xml"; 71 | var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); 72 | if (File.Exists(xmlPath)) 73 | c.IncludeXmlComments(xmlPath); 74 | } 75 | } 76 | 77 | // add OpenApi Annotations 78 | c.EnableAnnotations(); 79 | 80 | // sort actions by routeValue and relativePath 81 | c.OrderActionsBy((apiDesc) => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.RelativePath}"); 82 | }); 83 | 84 | services.AddSwaggerExamplesFromAssemblyOf(); 85 | } 86 | 87 | /// 88 | /// This method gets called by the runtime. 89 | /// Use this method to configure the HTTP request pipeline. 90 | /// 91 | /// . 92 | /// . 93 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 94 | { 95 | if (env.IsDevelopment()) 96 | { 97 | app.UseDeveloperExceptionPage(); 98 | } 99 | else 100 | { 101 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 102 | app.UseHsts(); 103 | } 104 | 105 | app.UseHttpsRedirection(); 106 | 107 | app.UseSwagger(); 108 | app.UseSwaggerUI(c => 109 | { 110 | c.DocumentTitle = "Unchase.PerformanceMeter Test WebAPI"; 111 | c.SwaggerEndpoint($"/swagger/v1/swagger.json", $"Unchase.PerformanceMeter Test WebAPI v1"); 112 | c.ConfigObject.DisplayRequestDuration = true; 113 | }); 114 | 115 | app.UsePerformanceDiagnosticObserver(); 116 | 117 | app.UseMvc(); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI/SwaggerExamples/ResponseExamples.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Reflection; 5 | using Swashbuckle.AspNetCore.Filters; 6 | using Unchase.FluentPerformanceMeter.Models; 7 | 8 | namespace Unchase.FluentPerformanceMeter.TestWebAPI.SwaggerExamples 9 | { 10 | internal class ResponseExamples 11 | { 12 | private class PerformanceInfoExample : IPerformanceInfo 13 | { 14 | public List> MethodCalls { get => new List>(); set { } } 15 | 16 | public List> TotalActivity { get => new List>(); set { } } 17 | 18 | public List> CurrentActivity { get => new List>(); set { } } 19 | 20 | public DateTime UptimeSince { get => DateTime.Now.AddHours(-1); set { } } 21 | 22 | public string ClassName { get => "SomeClassName"; set { } } 23 | 24 | public List MethodNames { get => new List(); set { } } 25 | 26 | public long TimerFrequency => Stopwatch.Frequency; 27 | 28 | IDictionary IPerformanceInfo.CustomData { get => new Dictionary(); } 29 | } 30 | 31 | internal class GetPerformanceInfoResponse200Example : IExamplesProvider 32 | { 33 | /// 34 | /// Get examples. 35 | /// 36 | object IExamplesProvider.GetExamples() 37 | { 38 | return new PerformanceInfoExample(); 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI/Unchase.FluentPerformanceMeter.TestWebAPI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | InProcess 6 | Unchase.FluentPerformanceMeter.TestWebAPI 7 | 8 | 9 | 10 | Unchase.FluentPerformanceMeter.TestWebAPI.xml 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI/Unchase.FluentPerformanceMeter.TestWebAPI.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Unchase.FluentPerformanceMeter.TestWebAPI 5 | 6 | 7 | 8 | 9 | Unchase.PerformanceMeter Test WebAPI Controller. 10 | 11 | 12 | 13 | 14 | Static constructor. 15 | 16 | 17 | 18 | 19 | Constructor. 20 | 21 | . 22 | 23 | 24 | 25 | Get methods performance info for this controller. 26 | 27 | Returns methods performance info. 28 | Returns methods performance info. 29 | 30 | 31 | 32 | Get methods performance info for fake service. 33 | 34 | Returns fake service methods performance info. 35 | Returns fake service methods performance info. 36 | 37 | 38 | 39 | Get methods performance info for Thread class. 40 | 41 | Returns Thread methods performance info. 42 | Returns Thread methods performance info. 43 | 44 | 45 | 46 | Test GET method with simple performance watching. 47 | 48 | 49 | 50 | 51 | Test GET method with simple performance watching. 52 | 53 | 54 | 55 | 56 | Test GET method from Func with simple performance watching. 57 | 58 | 59 | 60 | 61 | Test GET method with simple performance watching (with steps). 62 | 63 | 64 | 65 | 66 | Test GET method with simple performance watching (with ignored blocks). 67 | 68 | 69 | 70 | 71 | Test GET method with simple performance watching (with executing some code (Action) throws the exception) with exception handler. 72 | 73 | 74 | 75 | 76 | Test GET method with simple performance watching (with executing some code (Action and Func{string}) without performance watching). 77 | 78 | 79 | 80 | 81 | Test GET method with simple performance watching (with executing some code (Action) throws the custom exception). 82 | 83 | 84 | 85 | 86 | Test GET method with simple performance watching (with throws the exceptions) with exception handlers. 87 | 88 | 89 | 90 | 91 | Test GET method for public method of the public class . 92 | 93 | 94 | Returns external method call elapsed time. 95 | 96 | 97 | 98 | 99 | Test GET method from Action for public method of the public class . 100 | 101 | 102 | Returns external method call elapsed time. 103 | 104 | 105 | 106 | 107 | Call method with calling fake service methods in steps. 108 | 109 | 110 | Return elapsed total milliseconds for all steps. 111 | 112 | 113 | 114 | 115 | Test GET method with using HttpContextAccessor and adding custom data. 116 | 117 | 118 | With executed command after performance watching. 119 | 120 | Some value. 121 | 122 | Returns input value. 123 | 124 | 125 | 126 | 127 | Test GET method with adding custom data. 128 | 129 | 130 | With executed command after performance watching. 131 | 132 | 133 | Returns input value. 134 | 135 | 136 | 137 | 138 | Test POST method with caller name and custom data (from attribute). 139 | 140 | Some value from body. 141 | 142 | Returns input value. 143 | 144 | 145 | 146 | 147 | Test POST method with caller name (from attribute). 148 | 149 | Some value from body. 150 | 151 | Returns input value. 152 | 153 | 154 | 155 | 156 | Test GET method watching with diagnostic source. 157 | 158 | Input string. 159 | 160 | 161 | 162 | Test DTO for POST method. 163 | 164 | 165 | 166 | 167 | Test Data. 168 | 169 | 170 | 171 | 172 | Test POST method watching with diagnostic source (add arguments to custom data). 173 | 174 | Input DTO. 175 | 176 | 177 | 178 | Startup program. 179 | 180 | 181 | 182 | 183 | Main method. 184 | 185 | 186 | 187 | 188 | 189 | Create WebHost. 190 | 191 | Arguments. 192 | . 193 | 194 | 195 | 196 | Startup. 197 | 198 | 199 | 200 | 201 | Constructor. 202 | 203 | . 204 | 205 | 206 | 207 | Configuration. 208 | 209 | 210 | 211 | 212 | This method gets called by the runtime. 213 | Use this method to add services to the container. 214 | 215 | . 216 | 217 | 218 | 219 | This method gets called by the runtime. 220 | Use this method to configure the HTTP request pipeline. 221 | 222 | . 223 | . 224 | 225 | 226 | 227 | Get examples. 228 | 229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI31/OpenApiExamples/ResponseExamples.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Reflection; 5 | using Swashbuckle.AspNetCore.Filters; 6 | using Unchase.FluentPerformanceMeter.Models; 7 | 8 | namespace Unchase.FluentPerformanceMeter.TestWebAPI31.OpenApiExamples 9 | { 10 | internal class ResponseExamples 11 | { 12 | private class PerformanceInfoExample : IPerformanceInfo 13 | { 14 | public List> MethodCalls { get => new List>(); set { } } 15 | 16 | public List> TotalActivity { get => new List>(); set { } } 17 | 18 | public List> CurrentActivity { get => new List>(); set { } } 19 | 20 | public DateTime UptimeSince { get => DateTime.Now.AddHours(-1); set { } } 21 | 22 | public string ClassName { get => "SomeClassName"; set { } } 23 | 24 | public List MethodNames { get => new List(); set { } } 25 | 26 | public long TimerFrequency => Stopwatch.Frequency; 27 | 28 | IDictionary IPerformanceInfo.CustomData { get => new Dictionary(); } 29 | } 30 | 31 | internal class GetPerformanceInfoResponse200Example : IExamplesProvider 32 | { 33 | /// 34 | /// Get examples. 35 | /// 36 | public object GetExamples() 37 | { 38 | return new PerformanceInfoExample(); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI31/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace Unchase.FluentPerformanceMeter.TestWebAPI31 5 | { 6 | #pragma warning disable CS1591 7 | public class Program 8 | { 9 | public static void Main(string[] args) 10 | { 11 | CreateHostBuilder(args).Build().Run(); 12 | } 13 | 14 | public static IHostBuilder CreateHostBuilder(string[] args) => 15 | Host.CreateDefaultBuilder(args) 16 | .ConfigureWebHostDefaults(webBuilder => 17 | { 18 | webBuilder.UseStartup(); 19 | }); 20 | } 21 | #pragma warning restore CS1591 22 | } 23 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI31/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:64724", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "launchUrl": "swagger/index.html", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "Unchase.FluentPerformanceMeter.TestWebAPI31": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "launchUrl": "swagger/index.html", 23 | "applicationUrl": "http://localhost:5000", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI31/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.OpenApi.Models; 9 | using Newtonsoft.Json; 10 | using Swashbuckle.AspNetCore.Filters; 11 | using Unchase.FluentPerformanceMeter.AspNetCore.Mvc.Extensions; 12 | using Unchase.FluentPerformanceMeter.TestWebAPI31.Controllers; 13 | using Unchase.FluentPerformanceMeter.TestWebAPI31.OpenApiExamples; 14 | 15 | namespace Unchase.FluentPerformanceMeter.TestWebAPI31 16 | { 17 | #pragma warning disable CS1591 18 | public class Startup 19 | { 20 | public void ConfigureServices(IServiceCollection services) 21 | { 22 | services.AddHttpContextAccessor(); 23 | 24 | // adds a singleton service to the specified IPerformanceInfo with DI 25 | services.AddSingleton(s => PerformanceMeter.PerformanceInfo); 26 | // ... the same for another classes (controllers) 27 | 28 | services.AddPerformanceMeter(options => 29 | { 30 | // ALL of this is optional. You can simply call .AddPerformanceMeter() for all defaults 31 | // Defaults: In-Memory for 5 minutes, everything watched, every user can see 32 | 33 | // excludes a method from performance watching 34 | //options.ExcludeMethod(nameof(PerformanceMeterController.SimpleWatchingMethodStartWatchingPerformanceAttribute)); 35 | 36 | // to control which requests are watched, use the Func option: 37 | //options.ShouldWatching = request => request.HttpContext.User.IsInRole("Dev"); 38 | 39 | // allows to add custom data from custom attributes ("MethodCustomDataAttribute", "MethodCallerAttribute") to performance watching 40 | //options.AddCustomDataFromCustomAttributes = false; 41 | 42 | // allows to use "IgnoreMethodPerformanceAttribute" for excluding from performance watching 43 | //options.UseIgnoreMethodPerformanceAttribute = false; 44 | 45 | // allows to watch actions performance annotated with special attribute ("WatchingPerformanceAttribute") 46 | //options.WatchForAnnotatedWithAttributeOnly = false; 47 | 48 | // excludes a path from being watched 49 | //options.IgnorePath("/some_path"); 50 | 51 | // allows to add route path to custom data (with "pm_path" key) 52 | //options.AddRoutePathToCustomData = false; 53 | 54 | // set cache time for the watched performance results for the controller class 55 | //options.SetMethodCallsCacheTime(5); 56 | 57 | // adds common custom data (anonymous class) to class performance information 58 | //options.AddCustomData("Custom anonymous class", new { Name = "Custom Name", Value = 1 }); 59 | 60 | // set default exception handler for the controller class 61 | //options.SetDefaultExceptionHandler((ex) => Debug.WriteLine(ex.Message)); 62 | 63 | // adds a scope service of the PerformanceMeter of concrete class for DI 64 | // use it with ".WithSettingData.CallerFrom(IHttpContextAccessor)" 65 | //options.RegisterPerformanceMeterScope = false; 66 | }); 67 | 68 | //services.AddPerformanceDiagnosticObserver(options => 69 | //{ 70 | // // the same options like in "AddPerformanceMeter(options => {...})" 71 | //}); 72 | 73 | services.AddMvc().AddNewtonsoftJson(options => 74 | { 75 | options.SerializerSettings.Formatting = Formatting.Indented; 76 | options.SerializerSettings.StringEscapeHandling = StringEscapeHandling.Default; 77 | }).SetCompatibilityVersion(CompatibilityVersion.Latest); 78 | 79 | services.AddSwaggerGen(c => 80 | { 81 | c.SwaggerDoc("v1", new OpenApiInfo 82 | { 83 | Title = "Unchase.PerformanceMeter Test WebAPI (.NET Core 3.1)", 84 | Version = "v1", 85 | Description = "Unchase.PerformanceMeter Test WebAPI (.NET Core 3.1)", 86 | License = new OpenApiLicense 87 | { 88 | Name = "Apache-2.0", 89 | Url = new Uri("https://github.com/unchase/Unchase.PerformanceMeter/blob/master/LICENSE.md") 90 | } 91 | }); 92 | 93 | // add OpenApi filters with examples 94 | c.ExampleFilters(); 95 | 96 | // add documentation from xml-file comments from assemblies 97 | foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 98 | { 99 | if (!assembly.IsDynamic) 100 | { 101 | var xmlFile = $"{assembly.GetName().Name}.xml"; 102 | var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); 103 | if (File.Exists(xmlPath)) 104 | c.IncludeXmlComments(xmlPath); 105 | } 106 | } 107 | 108 | // add OpenApi Annotations 109 | c.EnableAnnotations(); 110 | 111 | // sort actions by routeValue and relativePath 112 | c.OrderActionsBy((apiDesc) => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.RelativePath}"); 113 | }); 114 | 115 | services.AddSwaggerGenNewtonsoftSupport(); 116 | 117 | services.AddSwaggerExamplesFromAssemblyOf(); 118 | } 119 | 120 | public void Configure( 121 | IApplicationBuilder app, 122 | IWebHostEnvironment env) 123 | { 124 | if (env.IsDevelopment()) 125 | { 126 | app.UseDeveloperExceptionPage(); 127 | } 128 | 129 | app.UseSwagger(); 130 | app.UseSwaggerUI(c => 131 | { 132 | c.DocumentTitle = "Unchase.PerformanceMeter Test WebAPI (.NET Core 3.1)"; 133 | c.SwaggerEndpoint($"/swagger/v1/swagger.json", "Unchase.PerformanceMeter Test WebAPI v1 (.NET Core 3.1)"); 134 | c.ConfigObject.DisplayRequestDuration = true; 135 | }); 136 | 137 | //app.UsePerformanceDiagnosticObserver(); 138 | 139 | app.UseRouting(); 140 | 141 | app.UseEndpoints(c => 142 | { 143 | c.MapControllers(); 144 | 145 | // use performance watching for concrete controller 146 | app.UsePerformanceMeterFor(); 147 | }); 148 | } 149 | } 150 | #pragma warning restore CS1591 151 | } 152 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI31/Unchase.FluentPerformanceMeter.TestWebAPI31.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | Unchase.FluentPerformanceMeter.TestWebAPI31.xml 9 | 10 | 11 | 12 | Unchase.FluentPerformanceMeter.TestWebAPI31.xml 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI31/Unchase.FluentPerformanceMeter.TestWebAPI31.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Unchase.FluentPerformanceMeter.TestWebAPI31 5 | 6 | 7 | 8 | 9 | Unchase.PerformanceMeter Test WebAPI Controller. 10 | 11 | 12 | 13 | 14 | Static constructor. 15 | 16 | 17 | 18 | 19 | Constructor. 20 | 21 | . 22 | . 23 | 24 | 25 | 26 | Get methods performance info for this controller. 27 | 28 | Returns methods performance info. 29 | Returns methods performance info. 30 | 31 | 32 | 33 | Get methods performance info for this controller. 34 | 35 | Returns methods performance info. 36 | Returns methods performance info. 37 | 38 | 39 | 40 | Get methods performance info for fake service. 41 | 42 | Returns fake service methods performance info. 43 | Returns fake service methods performance info. 44 | 45 | 46 | 47 | Get methods performance info for Thread class. 48 | 49 | Returns Thread methods performance info. 50 | Returns Thread methods performance info. 51 | 52 | 53 | 54 | Test GET method with simple performance watching. 55 | 56 | 57 | 58 | 59 | Test GET method with simple performance watching. 60 | 61 | 62 | 63 | 64 | Test GET method from Func with simple performance watching. 65 | 66 | 67 | 68 | 69 | Test GET method with simple performance watching (with steps). 70 | 71 | 72 | 73 | 74 | Test GET method with simple performance watching (with ignored blocks). 75 | 76 | 77 | 78 | 79 | Test GET method with simple performance watching (with executing some code (Action) throws the exception) with exception handler. 80 | 81 | 82 | 83 | 84 | Test GET method with simple performance watching (with executing some iterated code (Action)). 85 | 86 | 87 | 88 | 89 | Test GET method with simple performance watching (with executing some code (Action and Func{string}) without performance watching). 90 | 91 | 92 | 93 | 94 | Test GET method with simple performance watching (with executing some code (Action) throws the custom exception). 95 | 96 | 97 | 98 | 99 | Test GET method with simple performance watching (with throws the exceptions) with exception handlers. 100 | 101 | 102 | 103 | 104 | Test GET method for public method of the public class . 105 | 106 | 107 | Returns external method call elapsed time. 108 | 109 | 110 | 111 | 112 | Test GET method from Action for public method of the public class . 113 | 114 | 115 | Returns external method call elapsed time. 116 | 117 | 118 | 119 | 120 | Call method with calling fake service methods in steps. 121 | 122 | 123 | Return elapsed total milliseconds for all steps. 124 | 125 | 126 | 127 | 128 | Test GET method with using HttpContextAccessor and adding step with DI. 129 | 130 | Some value. 131 | 132 | Returns input value. 133 | 134 | 135 | 136 | 137 | Test GET method with using HttpContextAccessor and adding custom data. 138 | 139 | 140 | With executed command after performance watching. 141 | 142 | Some value. 143 | 144 | Returns input value. 145 | 146 | 147 | 148 | 149 | Test GET method with adding custom data. 150 | 151 | 152 | With executed command after performance watching. 153 | 154 | 155 | Returns input value. 156 | 157 | 158 | 159 | 160 | Test POST method with caller name and custom data (from attribute). 161 | 162 | Some value from body. 163 | 164 | Returns input value. 165 | 166 | 167 | 168 | 169 | Test POST method with caller name (from attribute). 170 | 171 | Some value from body. 172 | 173 | Returns input value. 174 | 175 | 176 | 177 | 178 | Test GET method watching with diagnostic source. 179 | 180 | Input string. 181 | 182 | 183 | 184 | Test DTO for POST method. 185 | 186 | 187 | 188 | 189 | Test Data. 190 | 191 | 192 | 193 | 194 | Test POST method watching with diagnostic source (add arguments to custom data). 195 | 196 | Input DTO. 197 | 198 | 199 | 200 | Test GET method with WatchingPerformance attribute. 201 | 202 | 203 | 204 | 205 | Get examples. 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI31/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.TestWebAPI31/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.Tests/PerformanceMeterTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.AspNetCore.Http; 3 | using System; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Unchase.FluentPerformanceMeter.Attributes; 7 | using Unchase.FluentPerformanceMeter.Builders; 8 | using Xunit; 9 | 10 | namespace Unchase.FluentPerformanceMeter.Tests 11 | { 12 | public class PerformanceMeterTests 13 | { 14 | private readonly IHttpContextAccessor _httpContextAccessor; 15 | 16 | public PerformanceMeterTests() 17 | { 18 | _httpContextAccessor = TestMoq.MockHttpContextAccessor.Object; 19 | } 20 | 21 | public class PublicClass 22 | { 23 | public void PublicVoidMethod() { } 24 | 25 | public static void PublicStaticVoidMethod() { } 26 | 27 | private void PrivateVoidMethod() { } 28 | 29 | private static void PrivateStaticVoidMethod() { } 30 | 31 | internal void InternalVoidMethod() { } 32 | 33 | internal static void InternalStaticVoidMethod() { } 34 | } 35 | 36 | public static class PublicStaticClass 37 | { 38 | public static void PublicStaticVoidMethod() { } 39 | 40 | private static void PrivateStaticVoidMethod() { } 41 | 42 | internal static void InternalStaticVoidMethod() { } 43 | } 44 | 45 | [Fact] 46 | public void PerformanceMeterStartWaychingPublicVoidMethodWithHttpContectAccessorReturnsSuccess() 47 | { 48 | // Arrange 49 | PerformanceMeter performanceMeter = null; 50 | 51 | // Act 52 | using (performanceMeter = PerformanceMeter 53 | .WatchingMethod(nameof(PublicClass.PublicVoidMethod)) 54 | .WithSettingData 55 | .CallerFrom(_httpContextAccessor) 56 | .Start()) 57 | { 58 | // Arrange 59 | var performanceInfo = PerformanceMeter.PerformanceInfo; 60 | var methodCurrentActivity = performanceInfo.CurrentActivity.Find(ca => ca.Method == performanceMeter.MethodInfo); 61 | var methodTotalActivity = performanceInfo.TotalActivity.Find(ca => ca.Method == performanceMeter.MethodInfo); 62 | var methodCalls = performanceInfo.MethodCalls.Find(ca => ca.Method == performanceMeter.MethodInfo); 63 | 64 | // Assert 65 | performanceInfo.ClassName.Should().EndWith(typeof(PublicClass).Name, "class name should be known"); 66 | 67 | performanceInfo.MethodNames.Count.Should().Be(typeof(PublicClass) 68 | .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) 69 | .Where(mi => !mi.IsSpecialName && mi.GetCustomAttribute() == null) 70 | .Count()); 71 | 72 | methodCurrentActivity.Should().NotBeNull(); 73 | methodCurrentActivity.CallsCount.Should().Be(1); 74 | 75 | methodTotalActivity.Should().NotBeNull(); 76 | methodTotalActivity.CallsCount.Should().Be(0); 77 | 78 | methodCalls.Should().BeNull(); 79 | } 80 | 81 | // Arrange 82 | var performanceInfoAfterDispose = PerformanceMeter.PerformanceInfo; 83 | var methodCurrentActivityAfterDispose = performanceInfoAfterDispose.CurrentActivity.Find(ca => ca.Method == performanceMeter.MethodInfo); 84 | var methodTotalActivityAfterDispose = performanceInfoAfterDispose.TotalActivity.Find(ca => ca.Method == performanceMeter.MethodInfo); 85 | var methodCallsAfterDispose = performanceInfoAfterDispose.MethodCalls.Find(ca => ca.Method == performanceMeter.MethodInfo); 86 | 87 | // Assert 88 | methodCurrentActivityAfterDispose.Should().NotBeNull(); 89 | methodCurrentActivityAfterDispose.CallsCount.Should().Be(0); 90 | 91 | methodTotalActivityAfterDispose.Should().NotBeNull(); 92 | methodTotalActivityAfterDispose.CallsCount.Should().Be(1); 93 | 94 | methodCallsAfterDispose.Should().NotBeNull(); 95 | methodCallsAfterDispose.Method.Should().BeSameAs(performanceMeter.MethodInfo); 96 | methodCallsAfterDispose.MethodName.Should().Be(nameof(PublicClass.PublicVoidMethod)); 97 | methodCallsAfterDispose.Elapsed.Should().BeGreaterThan(new TimeSpan()); 98 | methodCallsAfterDispose.Caller.Should().Be(_httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString()); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.Tests/TestMoq.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Moq; 3 | using System.Net; 4 | 5 | namespace Unchase.FluentPerformanceMeter.Tests 6 | { 7 | /// 8 | /// Test Moq. 9 | /// 10 | public static class TestMoq 11 | { 12 | private static Mock _mockHttpContextAccessor; 13 | public static Mock MockHttpContextAccessor 14 | { 15 | get 16 | { 17 | if (_mockHttpContextAccessor == null) 18 | { 19 | var mockConnectionInfo = new Mock(); 20 | mockConnectionInfo.Setup(_ => _.RemoteIpAddress).Returns(IPAddress.Parse("127.0.0.1")); 21 | _mockHttpContextAccessor = new Mock(); 22 | _mockHttpContextAccessor.Setup(_ => _.HttpContext.Connection).Returns(mockConnectionInfo.Object); 23 | } 24 | return _mockHttpContextAccessor; 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.Tests/Unchase.FluentPerformanceMeter.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29509.3 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unchase.FluentPerformanceMeter", "Unchase.FluentPerformanceMeter\Unchase.FluentPerformanceMeter.csproj", "{A7B78715-3FB5-4BFE-8F50-91D0CBFA3F2D}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unchase.FluentPerformanceMeter.Tests", "Unchase.FluentPerformanceMeter.Tests\Unchase.FluentPerformanceMeter.Tests.csproj", "{A744F33B-FCF3-4E12-A47D-1F3554087F85}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unchase.FluentPerformanceMeter.TestWebAPI", "Unchase.FluentPerformanceMeter.TestWebAPI\Unchase.FluentPerformanceMeter.TestWebAPI.csproj", "{F673B9A7-B949-49BA-AB2E-4DB8586D56F0}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unchase.FluentPerformanceMeter.AspNetCore.Mvc", "Unchase.FluentPerformanceMeter.AspNetCore.Mvc\Unchase.FluentPerformanceMeter.AspNetCore.Mvc.csproj", "{4DBF1648-BD99-4E82-91B2-0C4B5CF38FCC}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unchase.FluentPerformanceMeter.TestWebAPI31", "Unchase.FluentPerformanceMeter.TestWebAPI31\Unchase.FluentPerformanceMeter.TestWebAPI31.csproj", "{BF7E52E9-0FBB-4E97-97D7-44847B0D278E}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unchase.FluentPerformanceMeter.Test.Common", "Unchase.FluentPerformanceMeter.Test.Common\Unchase.FluentPerformanceMeter.Test.Common.csproj", "{1387C0E2-CFB2-42D0-9754-02C891D882AA}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{BC5C0197-83D0-48F7-A8E9-388B634F2671}" 19 | ProjectSection(SolutionItems) = preProject 20 | appveyor.yml = appveyor.yml 21 | CHANGELOG.md = CHANGELOG.md 22 | LICENSE.md = LICENSE.md 23 | README.md = README.md 24 | README_RU.md = README_RU.md 25 | EndProjectSection 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | Release|Any CPU = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {A7B78715-3FB5-4BFE-8F50-91D0CBFA3F2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {A7B78715-3FB5-4BFE-8F50-91D0CBFA3F2D}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {A7B78715-3FB5-4BFE-8F50-91D0CBFA3F2D}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {A7B78715-3FB5-4BFE-8F50-91D0CBFA3F2D}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {A744F33B-FCF3-4E12-A47D-1F3554087F85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {A744F33B-FCF3-4E12-A47D-1F3554087F85}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {A744F33B-FCF3-4E12-A47D-1F3554087F85}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {A744F33B-FCF3-4E12-A47D-1F3554087F85}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {F673B9A7-B949-49BA-AB2E-4DB8586D56F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {F673B9A7-B949-49BA-AB2E-4DB8586D56F0}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {F673B9A7-B949-49BA-AB2E-4DB8586D56F0}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {F673B9A7-B949-49BA-AB2E-4DB8586D56F0}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {4DBF1648-BD99-4E82-91B2-0C4B5CF38FCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {4DBF1648-BD99-4E82-91B2-0C4B5CF38FCC}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {4DBF1648-BD99-4E82-91B2-0C4B5CF38FCC}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {4DBF1648-BD99-4E82-91B2-0C4B5CF38FCC}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {BF7E52E9-0FBB-4E97-97D7-44847B0D278E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {BF7E52E9-0FBB-4E97-97D7-44847B0D278E}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {BF7E52E9-0FBB-4E97-97D7-44847B0D278E}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {BF7E52E9-0FBB-4E97-97D7-44847B0D278E}.Release|Any CPU.Build.0 = Release|Any CPU 53 | {1387C0E2-CFB2-42D0-9754-02C891D882AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {1387C0E2-CFB2-42D0-9754-02C891D882AA}.Debug|Any CPU.Build.0 = Debug|Any CPU 55 | {1387C0E2-CFB2-42D0-9754-02C891D882AA}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {1387C0E2-CFB2-42D0-9754-02C891D882AA}.Release|Any CPU.Build.0 = Release|Any CPU 57 | EndGlobalSection 58 | GlobalSection(SolutionProperties) = preSolution 59 | HideSolutionNode = FALSE 60 | EndGlobalSection 61 | GlobalSection(ExtensibilityGlobals) = postSolution 62 | SolutionGuid = {950C54C8-272E-46BB-ACD2-6AFAA8FF9358} 63 | EndGlobalSection 64 | EndGlobal 65 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Attributes/IgnoreMethodPerformanceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Unchase.FluentPerformanceMeter.Attributes 4 | { 5 | /// 6 | /// Attribute to indicate that no performance watching will be performed for this method. 7 | /// 8 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 9 | public class IgnoreMethodPerformanceAttribute : Attribute { } 10 | } 11 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Attributes/MethodCallerAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Unchase.FluentPerformanceMeter.Attributes 4 | { 5 | /// 6 | /// Attribute to add caller to method performance watching. 7 | /// 8 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 9 | public class MethodCallerAttribute : Attribute 10 | { 11 | #region Properties 12 | 13 | /// 14 | /// Caller. 15 | /// 16 | public string Caller { get; } 17 | 18 | #endregion 19 | 20 | #region Constructors 21 | 22 | /// 23 | /// Constructor for . 24 | /// 25 | /// . 26 | public MethodCallerAttribute(string caller) 27 | { 28 | this.Caller = caller; 29 | } 30 | 31 | #endregion 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Attributes/MethodCustomDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Unchase.FluentPerformanceMeter.Attributes 4 | { 5 | /// 6 | /// Attribute to add custom data to method performance watching. 7 | /// 8 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 9 | public class MethodCustomDataAttribute : Attribute 10 | { 11 | #region Properties 12 | 13 | /// 14 | /// Key. 15 | /// 16 | public string Key { get; } 17 | 18 | /// 19 | /// Value. 20 | /// 21 | public object Value { get; } 22 | 23 | #endregion 24 | 25 | #region Constructors 26 | 27 | /// 28 | /// Constructor for . 29 | /// 30 | /// . 31 | /// . 32 | public MethodCustomDataAttribute(string key, object value) 33 | { 34 | this.Key = key; 35 | this.Value = value; 36 | } 37 | 38 | #endregion 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Builders/CodeExecutorBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Unchase.FluentPerformanceMeter.Builders 5 | { 6 | /// 7 | /// Class for configure and execute the code. 8 | /// 9 | /// Class with methods. 10 | /// Type of exception of the exception handler. 11 | public class CodeExecutorBuilder where TClass : class where TException : Exception 12 | { 13 | #region Properties and fields 14 | 15 | internal PerformanceMeter PerformanceMeter; 16 | 17 | internal Action _exceptionHandler; 18 | 19 | private bool _useExceptionHandler; 20 | 21 | private bool _stopWatching; 22 | 23 | #endregion 24 | 25 | #region Constructors 26 | 27 | /// 28 | /// Default constructor for . 29 | /// 30 | internal CodeExecutorBuilder() { } 31 | 32 | /// 33 | /// Constructor for . 34 | /// 35 | /// . 36 | public CodeExecutorBuilder(PerformanceMeter performanceMeter) 37 | { 38 | this.PerformanceMeter = performanceMeter; 39 | } 40 | 41 | #endregion 42 | 43 | #region Main 44 | 45 | /// 46 | /// Stop watching when executing the code. 47 | /// 48 | /// 49 | /// Returns . 50 | /// 51 | internal CodeExecutorBuilder WithoutWatching() 52 | { 53 | this._stopWatching = true; 54 | return this; 55 | } 56 | 57 | /// 58 | /// Set exception handler for executed code. 59 | /// 60 | /// Exception handler for executed code. 61 | /// 62 | /// Returns . 63 | /// 64 | internal CodeExecutorBuilder WithExceptionHandler(Action exceptionHandler) 65 | { 66 | this._useExceptionHandler = true; 67 | this._exceptionHandler = exceptionHandler; 68 | return this; 69 | } 70 | 71 | /// 72 | /// Execute the Func. 73 | /// 74 | /// Type of result. 75 | /// Executed Func. 76 | /// Default result if exception will occured. 77 | /// 78 | /// Returns result. 79 | /// 80 | internal TResult Execute(Func func, TResult defaultResult = default) 81 | { 82 | if (this._useExceptionHandler) 83 | { 84 | TResult result = defaultResult; 85 | if (this._stopWatching) 86 | this.PerformanceMeter.InnerStopwatch.Stop(); 87 | try 88 | { 89 | result = func(); 90 | } 91 | catch (TException ex) 92 | { 93 | if (this._exceptionHandler != null) 94 | this._exceptionHandler(ex); 95 | else 96 | throw; 97 | } 98 | catch (Exception ex) 99 | { 100 | if (this.PerformanceMeter.ExceptionHandler != null) 101 | this.PerformanceMeter.ExceptionHandler(ex); 102 | else 103 | throw; 104 | } 105 | this.PerformanceMeter.InnerStopwatch.Start(); 106 | return result; 107 | } 108 | else 109 | { 110 | if (this._stopWatching) 111 | this.PerformanceMeter.InnerStopwatch.Stop(); 112 | var result = func(); 113 | this.PerformanceMeter.InnerStopwatch.Start(); 114 | return result; 115 | } 116 | } 117 | 118 | /// 119 | /// Execute the Task. 120 | /// 121 | /// Type of result. 122 | /// Executed Task. 123 | /// Default result if exception will occured. 124 | /// 125 | /// Returns result. 126 | /// 127 | internal async ValueTask ExecuteAsync(ValueTask task, TResult defaultResult = default) 128 | { 129 | if (this._useExceptionHandler) 130 | { 131 | TResult result = defaultResult; 132 | if (this._stopWatching) 133 | this.PerformanceMeter.InnerStopwatch.Stop(); 134 | try 135 | { 136 | result = await task; 137 | } 138 | catch (TException ex) 139 | { 140 | if (this._exceptionHandler != null) 141 | this._exceptionHandler(ex); 142 | else 143 | throw; 144 | } 145 | catch (Exception ex) 146 | { 147 | if (this.PerformanceMeter.ExceptionHandler != null) 148 | this.PerformanceMeter.ExceptionHandler(ex); 149 | else 150 | throw; 151 | } 152 | this.PerformanceMeter.InnerStopwatch.Start(); 153 | return result; 154 | } 155 | else 156 | { 157 | if (this._stopWatching) 158 | this.PerformanceMeter.InnerStopwatch.Stop(); 159 | var result = await task; 160 | this.PerformanceMeter.InnerStopwatch.Start(); 161 | return result; 162 | } 163 | } 164 | 165 | /// 166 | /// Execute the Action. 167 | /// 168 | /// Executed Action. 169 | /// Number of executing Action iterations. 170 | internal void Execute(Action action, uint iterations = 1) 171 | { 172 | if (this._useExceptionHandler) 173 | { 174 | if (this._stopWatching) 175 | this.PerformanceMeter.InnerStopwatch.Stop(); 176 | try 177 | { 178 | for (var i = 0; i < iterations; i++) 179 | { 180 | action(); 181 | } 182 | } 183 | catch (TException ex) 184 | { 185 | if (this._exceptionHandler != null) 186 | this._exceptionHandler(ex); 187 | else 188 | throw; 189 | } 190 | catch (Exception ex) 191 | { 192 | if (this.PerformanceMeter.ExceptionHandler != null) 193 | this.PerformanceMeter.ExceptionHandler(ex); 194 | else 195 | throw; 196 | } 197 | this.PerformanceMeter.InnerStopwatch.Start(); 198 | } 199 | else 200 | { 201 | if (this._stopWatching) 202 | this.PerformanceMeter.InnerStopwatch.Stop(); 203 | for (var i = 0; i < iterations; i++) 204 | { 205 | action(); 206 | } 207 | this.PerformanceMeter.InnerStopwatch.Start(); 208 | } 209 | } 210 | 211 | /// 212 | /// Execute the Task. 213 | /// 214 | /// Executed Task. 215 | /// Number of executing Task iterations. 216 | internal async ValueTask ExecuteAsync(ValueTask task, uint iterations = 1) 217 | { 218 | if (this._useExceptionHandler) 219 | { 220 | if (this._stopWatching) 221 | this.PerformanceMeter.InnerStopwatch.Stop(); 222 | try 223 | { 224 | for (var i = 0; i < iterations; i++) 225 | { 226 | await task; 227 | } 228 | } 229 | catch (TException ex) 230 | { 231 | if (this._exceptionHandler != null) 232 | this._exceptionHandler(ex); 233 | else 234 | throw; 235 | } 236 | catch (Exception ex) 237 | { 238 | if (this.PerformanceMeter.ExceptionHandler != null) 239 | this.PerformanceMeter.ExceptionHandler(ex); 240 | else 241 | throw; 242 | } 243 | this.PerformanceMeter.InnerStopwatch.Start(); 244 | } 245 | else 246 | { 247 | if (this._stopWatching) 248 | this.PerformanceMeter.InnerStopwatch.Stop(); 249 | for (var i = 0; i < iterations; i++) 250 | { 251 | await task; 252 | } 253 | this.PerformanceMeter.InnerStopwatch.Start(); 254 | } 255 | } 256 | 257 | #endregion 258 | } 259 | 260 | /// 261 | /// Extension methods for . 262 | /// 263 | public static class CodeExecutorBuilderExtensions 264 | { 265 | #region Extension methods 266 | 267 | /// 268 | /// Stop watching when executing the code. 269 | /// 270 | /// Class with methods. 271 | /// Type of exception of the exception handler. 272 | /// . 273 | /// 274 | /// Returns . 275 | /// 276 | public static CodeExecutorBuilder WithoutWatching(this CodeExecutorBuilder codeExecutorBuilder) where TClass : class where TException : Exception 277 | { 278 | return codeExecutorBuilder.WithoutWatching(); 279 | } 280 | 281 | /// 282 | /// Set exception handler for executed code. 283 | /// 284 | /// Class with methods. 285 | /// Type of exception of the exception handler. 286 | /// . 287 | /// Exception handler for executed code. 288 | /// 289 | /// Returns . 290 | /// 291 | public static CodeExecutorBuilder WithExceptionHandler(this CodeExecutorBuilder codeExecutorBuilder, Action exceptionHandler) where TClass : class where TException : Exception 292 | { 293 | return codeExecutorBuilder.WithExceptionHandler(exceptionHandler); 294 | } 295 | 296 | /// 297 | /// Execute the Action. 298 | /// 299 | /// Class with methods. 300 | /// Type of exception of the exception handler. 301 | /// . 302 | /// Executed Action. 303 | /// Number of executing Action iterations. 304 | public static void Start(this CodeExecutorBuilder codeExecutorBuilder, Action action, uint iterations = 1) where TClass : class where TException : Exception 305 | { 306 | codeExecutorBuilder.Execute(action, iterations); 307 | } 308 | 309 | /// 310 | /// Execute the Task. 311 | /// 312 | /// Class with methods. 313 | /// Type of exception of the exception handler. 314 | /// . 315 | /// Executed Task. 316 | /// Number of executing Task iterations. 317 | public static async ValueTask StartAsync(this CodeExecutorBuilder codeExecutorBuilder, ValueTask task, uint iterations = 1) where TClass : class where TException : Exception 318 | { 319 | await codeExecutorBuilder.ExecuteAsync(task, iterations).ConfigureAwait(false); 320 | } 321 | 322 | /// 323 | /// Execute the Func. 324 | /// 325 | /// Class with methods. 326 | /// Type of exception of the exception handler. 327 | /// Type of result. 328 | /// . 329 | /// Executed Func. 330 | /// Default result if exception will occured. 331 | /// 332 | /// Returns result. 333 | /// 334 | public static TResult Start(this CodeExecutorBuilder codeExecutorBuilder, Func func, TResult defaultResult = default) where TClass : class where TException : Exception 335 | { 336 | return codeExecutorBuilder.Execute(func, defaultResult); 337 | } 338 | 339 | /// 340 | /// Execute the Task. 341 | /// 342 | /// Class with methods. 343 | /// Type of exception of the exception handler. 344 | /// Type of result. 345 | /// . 346 | /// Executed Task. 347 | /// Default result if exception will occured. 348 | /// 349 | /// Returns result. 350 | /// 351 | public static async ValueTask StartAsync(this CodeExecutorBuilder codeExecutorBuilder, ValueTask task, TResult defaultResult = default) where TClass : class where TException : Exception 352 | { 353 | return await codeExecutorBuilder.ExecuteAsync(task, defaultResult).ConfigureAwait(false); 354 | } 355 | 356 | #endregion 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Builders/ExecutedCommandsBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Unchase.FluentPerformanceMeter.Models; 3 | 4 | namespace Unchase.FluentPerformanceMeter.Builders 5 | { 6 | /// 7 | /// Class for setting custom data for . 8 | /// 9 | /// Class with methods. 10 | public sealed class ExecutedCommandsBuilder : PerformanceMeterBuilder where TClass : class 11 | { 12 | #region Constructors 13 | 14 | /// 15 | /// Constructor for . 16 | /// 17 | /// . 18 | public ExecutedCommandsBuilder(PerformanceMeter performanceMeter) 19 | { 20 | this.PerformanceMeter = performanceMeter; 21 | } 22 | 23 | #endregion 24 | 25 | #region Methods 26 | 27 | /// 28 | /// Register commands which will be executed after the performance watching is completed. 29 | /// 30 | /// Collection of the executed commands. 31 | /// 32 | /// Returns . 33 | /// 34 | internal ExecutedCommandsBuilder AddCommands(params IPerformanceCommand[] performanceCommands) 35 | { 36 | foreach (var performanceCommand in performanceCommands) 37 | { 38 | if (!this.PerformanceMeter.RegisteredCommands.Contains(performanceCommand)) 39 | this.PerformanceMeter.RegisteredCommands.Add(performanceCommand); 40 | } 41 | return this; 42 | } 43 | 44 | /// 45 | /// Register actions which will be executed after the performance watching is completed. 46 | /// 47 | /// Collection of the executed actions. 48 | /// 49 | /// Returns . 50 | /// 51 | internal ExecutedCommandsBuilder AddActions(params Action[] performanceActions) 52 | { 53 | foreach (var performanceAction in performanceActions) 54 | { 55 | if (!this.PerformanceMeter.RegisteredActions.Contains(performanceAction)) 56 | this.PerformanceMeter.RegisteredActions.Add(performanceAction); 57 | } 58 | return this; 59 | } 60 | 61 | #endregion 62 | } 63 | 64 | /// 65 | /// Extension methods for the 66 | /// 67 | public static class ExecutedCommandsBuilderExtensions 68 | { 69 | #region Extension methods 70 | 71 | /// 72 | /// Register commands which will be executed after the performance watching is completed. 73 | /// 74 | /// Class with methods. 75 | /// . 76 | /// Collection of the executed commands. 77 | /// 78 | /// Returns . 79 | /// 80 | public static ExecutedCommandsBuilder Commands(this ExecutedCommandsBuilder executedCommandsBuilder, params IPerformanceCommand[] performanceCommands) where TClass : class 81 | { 82 | return executedCommandsBuilder.AddCommands(performanceCommands); 83 | } 84 | 85 | /// 86 | /// Register command which will be executed after the performance watching is completed. 87 | /// 88 | /// Class with methods. 89 | /// . 90 | /// Executed command. 91 | /// 92 | /// Returns . 93 | /// 94 | public static ExecutedCommandsBuilder Command(this ExecutedCommandsBuilder executedCommandsBuilder, IPerformanceCommand performanceCommand) where TClass : class 95 | { 96 | return executedCommandsBuilder.AddCommands(performanceCommand); 97 | } 98 | 99 | /// 100 | /// Register actions which will be executed after the performance watching is completed. 101 | /// 102 | /// Class with methods. 103 | /// . 104 | /// Collection of the executed actions. 105 | /// 106 | /// Returns . 107 | /// 108 | public static ExecutedCommandsBuilder Action(this ExecutedCommandsBuilder executedCommandsBuilder, params Action[] performanceActions) where TClass : class 109 | { 110 | return executedCommandsBuilder.AddActions(performanceActions); 111 | } 112 | 113 | /// 114 | /// Register action which will be executed after the performance watching is completed. 115 | /// 116 | /// Class with methods. 117 | /// . 118 | /// Executed action. 119 | /// 120 | /// Returns . 121 | /// 122 | public static ExecutedCommandsBuilder Action(this ExecutedCommandsBuilder executedCommandsBuilder, Action performanceAction) where TClass : class 123 | { 124 | return executedCommandsBuilder.AddActions(performanceAction); 125 | } 126 | 127 | #endregion 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Builders/PerformanceMeterBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Unchase.FluentPerformanceMeter.Builders 5 | { 6 | /// 7 | /// Class for create and configure . 8 | /// 9 | /// Class with methods. 10 | public class PerformanceMeterBuilder where TClass : class 11 | { 12 | #region Properties and fields 13 | 14 | internal PerformanceMeter PerformanceMeter; 15 | 16 | /// 17 | /// Allows to register commands which will be executed after the performance watching is completed. 18 | /// 19 | public ExecutedCommandsBuilder WithExecutingOnComplete => new ExecutedCommandsBuilder(this.PerformanceMeter); 20 | 21 | /// 22 | /// Allows to configure method performance watching settings. 23 | /// 24 | public SettingsBuilder WithSettingData => new SettingsBuilder(this.PerformanceMeter); 25 | 26 | #endregion 27 | 28 | #region Constructors 29 | 30 | /// 31 | /// Default constructor for . 32 | /// 33 | internal PerformanceMeterBuilder() { } 34 | 35 | /// 36 | /// Constructor for . 37 | /// 38 | /// . 39 | public PerformanceMeterBuilder(PerformanceMeter performanceMeter) 40 | { 41 | this.PerformanceMeter = performanceMeter; 42 | } 43 | 44 | #endregion 45 | 46 | #region Main 47 | 48 | /// 49 | /// Start watching method performance. 50 | /// 51 | /// 52 | /// Returns . 53 | /// 54 | internal PerformanceMeter Start() 55 | { 56 | try 57 | { 58 | if (PerformanceMeter.DefaultOptions?.ExcludedMethods?.Contains(this.PerformanceMeter.MethodInfo 59 | .Name) != true) 60 | { 61 | Performance.Input(this.PerformanceMeter.MethodInfo); 62 | this.PerformanceMeter.InnerStopwatch = Stopwatch.StartNew(); 63 | this.PerformanceMeter.DateStart = DateTime.UtcNow - this.PerformanceMeter.InnerStopwatch.Elapsed; 64 | } 65 | } 66 | catch (Exception ex) 67 | { 68 | if (this.PerformanceMeter.ExceptionHandler != null) 69 | this.PerformanceMeter.ExceptionHandler(ex); 70 | else 71 | throw; 72 | } 73 | 74 | if (PerformanceMeter.DefaultOptions?.RegisterPerformanceMeterScope == true) 75 | { 76 | var httpContext = this.PerformanceMeter.HttpContextAccessor?.HttpContext; 77 | if (httpContext != null) 78 | httpContext.Items[$"PerformanceMeter{httpContext.TraceIdentifier}"] = this.PerformanceMeter; 79 | } 80 | 81 | return this.PerformanceMeter; 82 | } 83 | 84 | #endregion 85 | } 86 | 87 | /// 88 | /// Extension methods for the 89 | /// 90 | public static class PerformanceMeterBuilderExtensions 91 | { 92 | #region Extension methods 93 | 94 | /// 95 | /// Start watching method performance. 96 | /// 97 | /// Class with methods. 98 | /// . 99 | /// 100 | /// Returns . 101 | /// 102 | public static PerformanceMeter Start(this PerformanceMeterBuilder performanceMeterBuilder) where TClass : class 103 | { 104 | return performanceMeterBuilder.Start(); 105 | } 106 | 107 | #endregion 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Builders/SettingsBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using Unchase.FluentPerformanceMeter.Attributes; 5 | 6 | namespace Unchase.FluentPerformanceMeter.Builders 7 | { 8 | /// 9 | /// Class for setting custom data for . 10 | /// 11 | /// Class with methods. 12 | public sealed class SettingsBuilder : PerformanceMeterBuilder where TClass : class 13 | { 14 | #region Constructors 15 | 16 | /// 17 | /// Constructor for . 18 | /// 19 | /// . 20 | public SettingsBuilder(PerformanceMeter performanceMeter) 21 | { 22 | this.PerformanceMeter = performanceMeter; 23 | if (PerformanceMeter.DefaultOptions?.AddCustomDataFromCustomAttributes ?? true) 24 | { 25 | foreach (var performanceCustomDataAttribute in performanceMeter.MethodInfo.GetCustomAttributes( 26 | typeof(MethodCustomDataAttribute), false)) 27 | { 28 | if (performanceCustomDataAttribute is MethodCustomDataAttribute customDataAttribute) 29 | AddCustomData(customDataAttribute.Key, customDataAttribute.Value); 30 | } 31 | 32 | foreach (var methodCallerAttribute in performanceMeter.MethodInfo.GetCustomAttributes( 33 | typeof(MethodCallerAttribute), false)) 34 | { 35 | if (methodCallerAttribute is MethodCallerAttribute caller) 36 | WithCaller(caller.Caller); 37 | } 38 | } 39 | } 40 | 41 | #endregion 42 | 43 | #region Methods 44 | 45 | /// 46 | /// Set to get the ip address of the caller. 47 | /// 48 | /// . 49 | /// 50 | /// Returns . 51 | /// 52 | internal SettingsBuilder WithHttpContextAccessor(IHttpContextAccessor httpContextAccessor) 53 | { 54 | this.PerformanceMeter.HttpContextAccessor = httpContextAccessor; 55 | return this; 56 | } 57 | 58 | /// 59 | /// Set Action to handle exceptions that occur. 60 | /// 61 | /// Action to handle exceptions that occur. 62 | /// 63 | /// Returns . 64 | /// 65 | internal SettingsBuilder WithExceptionHandler(Action exceptionHandler = null) 66 | { 67 | this.PerformanceMeter.ExceptionHandler = exceptionHandler; 68 | return this; 69 | } 70 | 71 | /// 72 | /// Set caller name. 73 | /// 74 | /// Caller name. 75 | /// 76 | /// Returns . 77 | /// 78 | internal SettingsBuilder WithCaller(string caller) 79 | { 80 | this.PerformanceMeter.Caller = caller; 81 | return this; 82 | } 83 | 84 | /// 85 | /// Add custom data. 86 | /// 87 | /// Key. 88 | /// Value. 89 | /// 90 | /// Returns . 91 | /// 92 | internal SettingsBuilder AddCustomData(string key, object value) 93 | { 94 | this.PerformanceMeter.CustomData.TryAdd(key, value); 95 | return this; 96 | } 97 | 98 | /// 99 | /// Add caller data. 100 | /// 101 | /// Caller source. 102 | /// Caller source line number. 103 | /// 104 | /// Returns . 105 | /// 106 | internal SettingsBuilder AddCallerSourceData(string callerSource = "", int callerSourceLineNumber = 0) 107 | { 108 | if (!string.IsNullOrWhiteSpace(callerSource)) 109 | this.AddCustomData(nameof(callerSource), callerSource); 110 | 111 | if (callerSourceLineNumber > 0) 112 | this.AddCustomData(nameof(callerSourceLineNumber), callerSourceLineNumber); 113 | 114 | return this; 115 | } 116 | 117 | #endregion 118 | } 119 | 120 | /// 121 | /// Extension methods for the 122 | /// 123 | public static class SettingsBuilderExtensions 124 | { 125 | #region Extension methods 126 | 127 | /// 128 | /// Set to get the ip address of the caller. 129 | /// 130 | /// Class with methods. 131 | /// . 132 | /// . 133 | /// 134 | /// Returns . 135 | /// 136 | public static SettingsBuilder CallerFrom(this SettingsBuilder settingsBuilder, IHttpContextAccessor httpContextAccessor) where TClass : class 137 | { 138 | return settingsBuilder.WithHttpContextAccessor(httpContextAccessor); 139 | } 140 | 141 | /// 142 | /// Set Action to handle exceptions that occur. 143 | /// 144 | /// Class with methods. 145 | /// . 146 | /// Action to handle exceptions that occur. 147 | /// 148 | /// Returns . 149 | /// 150 | public static SettingsBuilder ExceptionHandler(this SettingsBuilder settingsBuilder, Action exceptionHandler = null) where TClass : class 151 | { 152 | return settingsBuilder.WithExceptionHandler(exceptionHandler); 153 | } 154 | 155 | /// 156 | /// Set caller name. 157 | /// 158 | /// Class with methods. 159 | /// . 160 | /// Caller name. 161 | /// 162 | /// Returns . 163 | /// 164 | public static SettingsBuilder CallerFrom(this SettingsBuilder settingsBuilder, string caller) where TClass : class 165 | { 166 | return settingsBuilder.WithCaller(caller); 167 | } 168 | 169 | /// 170 | /// Add custom data. 171 | /// 172 | /// Class with methods. 173 | /// . 174 | /// Key. 175 | /// Value. 176 | /// 177 | /// Returns . 178 | /// 179 | public static SettingsBuilder CustomData(this SettingsBuilder settingsBuilder, string key, object value) where TClass : class 180 | { 181 | return settingsBuilder.AddCustomData(key, value); 182 | } 183 | 184 | /// 185 | /// Add caller data. 186 | /// 187 | /// Class with methods. 188 | /// . 189 | /// Caller source. 190 | /// Caller source line number. 191 | /// 192 | /// Returns . 193 | /// 194 | public static SettingsBuilder CallerSourceData(this SettingsBuilder settingsBuilder, 195 | [CallerFilePath] string callerSource = "", 196 | [CallerLineNumber] int callerSourceLineNumber = 0) where TClass : class 197 | { 198 | return settingsBuilder.AddCallerSourceData(callerSource, callerSourceLineNumber); 199 | } 200 | 201 | #endregion 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Extensions/CommonExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Unchase.FluentPerformanceMeter.Extensions 4 | { 5 | /// 6 | /// Common extension methods to use only in this project 7 | /// 8 | public static class CommonExtensions 9 | { 10 | #region Extension methods 11 | 12 | /// 13 | /// Checks if a string contains another one. 14 | /// 15 | /// The string to check for presence in. 16 | /// The value to check presence of. 17 | /// The to use when comparing. 18 | /// Whether is contained in . 19 | public static bool Contains(this string s, string value, StringComparison comparison) => 20 | s.IndexOf(value, comparison) >= 0; 21 | 22 | #endregion 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Extensions/PerformanceInfoStepExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Unchase.FluentPerformanceMeter.Extensions 2 | { 3 | /// 4 | /// Extension methods for . 5 | /// 6 | public static class PerformanceInfoStepExtensions 7 | { 8 | #region Extension methods 9 | 10 | /// 11 | /// Add custom data to performance meter step information. 12 | /// 13 | /// Class with methods. 14 | /// . 15 | /// Key. 16 | /// Value. 17 | /// 18 | /// Returns . 19 | /// 20 | public static PerformanceInfoStep AddCustomData(this PerformanceInfoStep performanceInfoStep, string key, object value) where TClass : class 21 | { 22 | return performanceInfoStep.AddCustomData(key, value); 23 | } 24 | 25 | /// 26 | /// Silences a performance meter for the duration, use in a using to silence for the duration. 27 | /// 28 | /// Class with methods. 29 | /// . 30 | /// 31 | /// Returns . 32 | /// 33 | public static PerformanceInfoStep WithoutWatching(this PerformanceInfoStep performanceInfoStep) where TClass : class 34 | { 35 | return performanceInfoStep.WithoutWatching(); 36 | } 37 | 38 | #endregion 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Extensions/PerformanceMeterBaseOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Unchase.FluentPerformanceMeter.Extensions 2 | { 3 | /// 4 | /// Handy extensions for . 5 | /// 6 | public static class PerformanceMeterBaseOptionsExtensions 7 | { 8 | #region Extension methods 9 | 10 | /// 11 | /// Excludes a method from performance watching, convenience method for chaining, basically .Add(name) 12 | /// 13 | /// The subtype of to use (inferred for common usage). 14 | /// The options to exclude the method on. 15 | /// The method name to exclude from performance watching. 16 | public static T ExcludeMethod(this T options, string method) where T : PerformanceMeterBaseOptions 17 | { 18 | options.ExcludedMethods.Add(method); 19 | return options; 20 | } 21 | 22 | #endregion 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Extensions/PerformanceMeterExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Unchase.FluentPerformanceMeter.Extensions 2 | { 3 | /// 4 | /// Extension methods for . 5 | /// 6 | public static class PerformanceMeterExtensions 7 | { 8 | #region Extension methods 9 | 10 | /// 11 | /// Add performance meter step. 12 | /// 13 | /// Class with methods. 14 | /// . 15 | /// Step name. 16 | /// 17 | /// Returns . 18 | /// 19 | public static PerformanceInfoStep Step(this PerformanceMeter performanceMeter, string stepName) where TClass : class 20 | { 21 | return PerformanceInfoStep.WatchingStep(performanceMeter, stepName); 22 | } 23 | 24 | /// 25 | /// Add performance meter step, but only saves when over . 26 | /// 27 | /// Class with methods. 28 | /// . 29 | /// Step name. 30 | /// The minimum time to take before this step is saved (e.g. if it’s fast, leave it out). 31 | /// 32 | /// Returns . 33 | /// 34 | public static PerformanceInfoStep StepIf(this PerformanceMeter performanceMeter, string stepName, double minSaveMs) where TClass : class 35 | { 36 | return PerformanceInfoStep.WatchingStepIf(performanceMeter, stepName, minSaveMs); 37 | } 38 | 39 | /// 40 | /// Silences a performance meter for the duration, use in a using to silence for the duration. 41 | /// 42 | /// Class with methods. 43 | /// 44 | /// Returns . 45 | /// 46 | public static PerformanceInfoStep Ignore(this PerformanceMeter performanceMeter) where TClass : class 47 | { 48 | return PerformanceInfoStep.WatchingStepIf(performanceMeter, string.Empty, double.MaxValue).WithoutWatching(); 49 | } 50 | 51 | #endregion 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Models/IPerformanceCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Unchase.FluentPerformanceMeter.Models 2 | { 3 | /// 4 | /// Custom executed command interface. 5 | /// 6 | public interface IPerformanceCommand 7 | { 8 | /// 9 | /// Executed command name. 10 | /// 11 | string CommandName { get; } 12 | 13 | /// 14 | /// Execute command. 15 | /// 16 | /// . 17 | void Execute(IPerformanceInfo performanceInfo); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Models/IPerformanceInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace Unchase.FluentPerformanceMeter.Models 6 | { 7 | /// 8 | /// Method performance information for specific class. 9 | /// 10 | /// Class with public methods. 11 | public interface IPerformanceInfo : IPerformanceInfo where TClass : class { } 12 | 13 | /// 14 | /// Method performance information. 15 | /// 16 | public interface IPerformanceInfo 17 | { 18 | /// 19 | /// List of method calls information. 20 | /// 21 | /// 22 | /// . 23 | /// 24 | List> MethodCalls { get; } 25 | 26 | /// 27 | /// List of total method calls count information. 28 | /// 29 | /// 30 | /// . 31 | /// 32 | List> TotalActivity { get; } 33 | 34 | /// 35 | /// List of current method calls count information. 36 | /// 37 | /// 38 | /// . 39 | /// 40 | List> CurrentActivity { get; } 41 | 42 | /// 43 | /// Uptime. 44 | /// 45 | DateTime UptimeSince { get; } 46 | 47 | /// 48 | /// Frequency of the timer as the number of ticks per second. 49 | /// 50 | long TimerFrequency { get; } 51 | 52 | /// 53 | /// Class name. 54 | /// 55 | string ClassName { get; } 56 | 57 | /// 58 | /// List of method names. 59 | /// 60 | List MethodNames { get; } 61 | 62 | /// 63 | /// Custom data. 64 | /// 65 | IDictionary CustomData { get; } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Models/IPerformanceInfoStepData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Unchase.FluentPerformanceMeter.Models 5 | { 6 | /// 7 | /// Performace meter step information. 8 | /// 9 | public interface IPerformanceInfoStepData 10 | { 11 | /// 12 | /// Step name. 13 | /// 14 | string StepName { get; } 15 | 16 | /// 17 | /// Performance meter step call duration. 18 | /// 19 | TimeSpan Elapsed { get; } 20 | 21 | /// 22 | /// Step call start date. 23 | /// 24 | DateTime StartTime { get; } 25 | 26 | /// 27 | /// Step call end date. 28 | /// 29 | DateTime EndTime { get; } 30 | 31 | /// 32 | /// Custom data. 33 | /// 34 | IDictionary CustomData { get; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Models/MethodCallInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Runtime.Serialization; 5 | 6 | namespace Unchase.FluentPerformanceMeter.Models 7 | { 8 | /// 9 | /// Method calls information. 10 | /// 11 | /// Method with type . 12 | [DataContract] 13 | public class MethodCallInfo where T : MethodInfo 14 | { 15 | #region Properties 16 | 17 | /// 18 | /// Method information. 19 | /// 20 | /// 21 | /// . 22 | /// 23 | public T Method { get; } 24 | 25 | /// 26 | /// Method name. 27 | /// 28 | [DataMember] 29 | public string MethodName { get; } 30 | 31 | /// 32 | /// Method call duration. 33 | /// 34 | [DataMember] 35 | public TimeSpan Elapsed { get; } 36 | 37 | /// 38 | /// Caller name. 39 | /// 40 | [DataMember] 41 | public string Caller { get; } 42 | 43 | /// 44 | /// Method call start date. 45 | /// 46 | [DataMember] 47 | public DateTime StartTime { get; } 48 | 49 | /// 50 | /// Method call end date. 51 | /// 52 | [DataMember] 53 | public DateTime EndTime { get; } 54 | 55 | /// 56 | /// Custom data for a specific method call. 57 | /// 58 | [DataMember] 59 | public IDictionary CustomData { get; } 60 | 61 | /// 62 | /// Collection of performance meter steps. 63 | /// 64 | [DataMember] 65 | public IEnumerable Steps { get; } 66 | 67 | #endregion 68 | 69 | #region Constructors 70 | 71 | /// 72 | /// Default constructor for . 73 | /// 74 | internal MethodCallInfo() { } 75 | 76 | /// 77 | /// Constructor for . 78 | /// 79 | /// . 80 | /// Elapsed time. 81 | /// . 82 | /// . 83 | /// . 84 | /// . 85 | internal MethodCallInfo(T m, TimeSpan elapsed, string caller, DateTime ds, IDictionary customData, IEnumerable steps) 86 | { 87 | Method = m; 88 | if (m != null) 89 | MethodName = m.Name; 90 | Elapsed = elapsed; 91 | Caller = caller; 92 | StartTime = ds; 93 | EndTime = ds + elapsed; 94 | CustomData = customData; 95 | Steps = steps; 96 | } 97 | 98 | #endregion 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Models/MethodCallsCount.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.Serialization; 3 | 4 | namespace Unchase.FluentPerformanceMeter.Models 5 | { 6 | /// 7 | /// Method calls count information. 8 | /// 9 | /// Method with type . 10 | [DataContract] 11 | public class MethodCallsCount where T : MethodInfo 12 | { 13 | #region Properties 14 | 15 | /// 16 | /// Method information. 17 | /// 18 | /// 19 | /// . 20 | /// 21 | public T Method { get; } 22 | 23 | /// 24 | /// Method name. 25 | /// 26 | [DataMember] 27 | public string MethodName => Method?.Name; 28 | 29 | internal long _callsCount; 30 | 31 | /// 32 | /// Method calls count. 33 | /// 34 | [DataMember] 35 | public long CallsCount 36 | { 37 | get => _callsCount; 38 | private set => _callsCount = value; 39 | } 40 | 41 | #endregion 42 | 43 | #region Constructors 44 | 45 | /// 46 | /// Default constructor for . 47 | /// 48 | internal MethodCallsCount() { } 49 | 50 | /// 51 | /// Constructor for . 52 | /// 53 | /// . 54 | internal MethodCallsCount(T m) 55 | { 56 | Method = m; 57 | } 58 | 59 | #endregion 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Models/PerformanceInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.Serialization; 7 | using Unchase.FluentPerformanceMeter.Attributes; 8 | 9 | namespace Unchase.FluentPerformanceMeter.Models 10 | { 11 | /// 12 | /// Method performance information. 13 | /// 14 | /// Class with public methods. 15 | [DataContract] 16 | public class PerformanceInfo : IPerformanceInfo where TClass : class 17 | { 18 | #region Properties 19 | 20 | /// 21 | /// List of method calls information. 22 | /// 23 | /// 24 | /// . 25 | /// 26 | [DataMember] 27 | public List> MethodCalls { get; } 28 | 29 | /// 30 | /// List of total method calls count information. 31 | /// 32 | /// 33 | /// . 34 | /// 35 | [DataMember] 36 | public List> TotalActivity { get; } 37 | 38 | /// 39 | /// List of current method calls count information. 40 | /// 41 | /// 42 | /// . 43 | /// 44 | [DataMember] 45 | public List> CurrentActivity { get; } 46 | 47 | /// 48 | /// Uptime. 49 | /// 50 | [DataMember] 51 | public DateTime UptimeSince { get; } 52 | 53 | /// 54 | /// Class name. 55 | /// 56 | [DataMember] 57 | public string ClassName { get; } 58 | 59 | /// 60 | /// List of method names. 61 | /// 62 | [DataMember] 63 | public List MethodNames { get; } 64 | 65 | /// 66 | /// Custom data. 67 | /// 68 | [DataMember] 69 | public IDictionary CustomData { get; } 70 | 71 | /// 72 | /// Frequency of the timer as the number of ticks per second. 73 | /// 74 | [DataMember] 75 | public long TimerFrequency => Stopwatch.Frequency; 76 | 77 | #endregion 78 | 79 | #region Constructors 80 | 81 | /// 82 | /// Constructor for . 83 | /// 84 | public PerformanceInfo() 85 | { 86 | UptimeSince = DateTime.UtcNow; 87 | MethodCalls = new List>(); 88 | TotalActivity = new List>(); 89 | CurrentActivity = new List>(); 90 | var methodInfos = typeof(TClass) 91 | .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) 92 | .Where(mi => 93 | !mi.IsSpecialName && 94 | (PerformanceMeter.DefaultOptions?.UseIgnoreMethodPerformanceAttribute != true || 95 | mi.GetCustomAttribute() == null)) 96 | .ToArray(); 97 | MethodNames = methodInfos.Select(mi => mi.Name).Distinct().ToList(); 98 | foreach (var method in methodInfos) 99 | { 100 | TotalActivity.Add(new MethodCallsCount(method)); 101 | CurrentActivity.Add(new MethodCallsCount(method)); 102 | } 103 | ClassName = typeof(TClass).FullName; 104 | CustomData = new Dictionary(); 105 | } 106 | 107 | #endregion 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Models/PerformanceInfoStepData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Unchase.FluentPerformanceMeter.Models 5 | { 6 | /// 7 | /// Performace meter step information. 8 | /// 9 | internal class PerformanceInfoStepData : IPerformanceInfoStepData 10 | { 11 | #region Properties 12 | 13 | /// 14 | /// Step name. 15 | /// 16 | public string StepName { get; } 17 | 18 | /// 19 | /// Performance meter step call duration. 20 | /// 21 | public TimeSpan Elapsed { get; } 22 | 23 | /// 24 | /// Step call start date. 25 | /// 26 | public DateTime StartTime { get; } 27 | 28 | /// 29 | /// Step call end date. 30 | /// 31 | public DateTime EndTime { get; } 32 | 33 | /// 34 | /// Custom data. 35 | /// 36 | public IDictionary CustomData { get; } 37 | 38 | #endregion 39 | 40 | #region Constructors 41 | 42 | /// 43 | /// Constructor for . 44 | /// 45 | /// Step name. 46 | /// Performance meter step call duration. 47 | /// Step call start date. 48 | /// Custom data. 49 | private PerformanceInfoStepData(string stepName, TimeSpan elapsed, DateTime dateStart, IDictionary customData) 50 | { 51 | this.StepName = stepName; 52 | this.Elapsed = elapsed; 53 | this.StartTime = dateStart; 54 | this.EndTime = dateStart + elapsed; 55 | this.CustomData = customData; 56 | } 57 | 58 | #endregion 59 | 60 | #region Methods 61 | 62 | /// 63 | /// Create performance meter step data class instance. 64 | /// 65 | /// Step name. 66 | /// Performance meter step call duration. 67 | /// Step call start date. 68 | /// Custom data. 69 | /// 70 | /// Returns . 71 | /// 72 | internal static PerformanceInfoStepData Create(string stepName, TimeSpan elapsed, DateTime dateStart, IDictionary customData) 73 | { 74 | return new PerformanceInfoStepData(stepName, elapsed, dateStart, customData); 75 | } 76 | 77 | #endregion 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Performance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Threading; 5 | using Unchase.FluentPerformanceMeter.Attributes; 6 | using Unchase.FluentPerformanceMeter.Models; 7 | 8 | namespace Unchase.FluentPerformanceMeter 9 | { 10 | /// 11 | /// Additional class for performance information. 12 | /// 13 | /// Class with public methods. 14 | internal static class Performance where TClass : class 15 | { 16 | #region Fields 17 | 18 | private static readonly object PerformanceLock = new object(); 19 | 20 | private static DateTime _lastRemoveDate; 21 | 22 | private static int _defaultMethodCallsCacheTime = 5; 23 | 24 | #endregion 25 | 26 | #region Properties 27 | 28 | private static IPerformanceInfo _performanceInfo; 29 | /// 30 | /// Methods performance information. 31 | /// 32 | internal static IPerformanceInfo PerformanceInfo 33 | { 34 | get 35 | { 36 | lock (PerformanceLock) 37 | { 38 | if (_performanceInfo == null) 39 | { 40 | _performanceInfo = new PerformanceInfo(); 41 | _lastRemoveDate = DateTime.Now; 42 | } 43 | else 44 | { 45 | if (_lastRemoveDate.AddMinutes(MethodCallsCacheTime) < DateTime.Now) 46 | { 47 | _performanceInfo.MethodCalls?.RemoveAll(x => x?.StartTime.CompareTo(DateTime.Now.AddMinutes(-MethodCallsCacheTime)) < 0); 48 | _lastRemoveDate = DateTime.Now; 49 | } 50 | } 51 | return _performanceInfo; 52 | } 53 | } 54 | } 55 | 56 | /// 57 | /// Time in minutes to clear list of the method calls. 58 | /// 59 | /// 60 | /// . 61 | /// 62 | internal static int MethodCallsCacheTime { get; set; } = _defaultMethodCallsCacheTime; 63 | 64 | #endregion 65 | 66 | #region Public methods 67 | 68 | /// 69 | /// Start watching method performance. 70 | /// 71 | /// Method with type . 72 | internal static void Input(MethodInfo method) 73 | { 74 | var currentActivity = PerformanceInfo.CurrentActivity.Find(x => x.Method == method); 75 | if (currentActivity != null) 76 | Interlocked.CompareExchange(ref currentActivity._callsCount, currentActivity._callsCount + 1, currentActivity._callsCount); 77 | } 78 | 79 | /// 80 | /// Stop watching method performance. 81 | /// 82 | /// Caller name. 83 | /// Method with type . 84 | /// Elapsed time from the running of a method. 85 | /// Method start date. 86 | /// Custom data for a specific method call. 87 | /// Performance meter steps. 88 | internal static IPerformanceInfo Output(string caller, MethodInfo method, TimeSpan elapsed, DateTime dateStart, IDictionary customData = null, IEnumerable steps = null) 89 | { 90 | var currentActivity = PerformanceInfo.CurrentActivity.Find(x => x.Method == method); 91 | if (currentActivity != null) 92 | Interlocked.CompareExchange(ref currentActivity._callsCount, currentActivity._callsCount - 1, currentActivity._callsCount); 93 | if (PerformanceMeter.DefaultOptions?.UseIgnoreMethodPerformanceAttribute != true || method.GetCustomAttribute() == null) 94 | PerformanceInfo.MethodCalls.Add(new MethodCallInfo(method, elapsed, caller, dateStart, customData, steps)); 95 | var totalActivity = PerformanceInfo.TotalActivity.Find(x => x.Method == method); 96 | if (totalActivity != null) 97 | Interlocked.CompareExchange(ref totalActivity._callsCount, totalActivity._callsCount + 1, totalActivity._callsCount); 98 | return PerformanceInfo; 99 | } 100 | 101 | /// 102 | /// Clear all performance watching information. 103 | /// 104 | /// 105 | /// Returns . 106 | /// 107 | internal static IPerformanceInfo Reset() 108 | { 109 | _performanceInfo = null; 110 | MethodCallsCacheTime = _defaultMethodCallsCacheTime; 111 | return PerformanceInfo; 112 | } 113 | 114 | #endregion 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/PerformanceInfoStep.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Diagnostics; 4 | using Unchase.FluentPerformanceMeter.Models; 5 | 6 | namespace Unchase.FluentPerformanceMeter 7 | { 8 | /// 9 | /// Class for starting and stopping method steps performance watching. 10 | /// 11 | /// Class with methods. 12 | public class PerformanceInfoStep : IDisposable where TClass : class 13 | { 14 | #region Peroperties and fields 15 | 16 | private bool _disposed; 17 | 18 | private Stopwatch _innerStopwatch { get; set; } = new Stopwatch(); 19 | 20 | private DateTime _dateStart { get; } = DateTime.UtcNow; 21 | 22 | private PerformanceMeter _performanceMeter { get; } 23 | 24 | private string _stepName { get; } 25 | 26 | private double _minSaveMs { get; set; } 27 | 28 | private bool _wasStopped { get; set; } 29 | 30 | private ConcurrentDictionary _customData { get; } = new ConcurrentDictionary(); 31 | 32 | #endregion 33 | 34 | #region Constructors and destructor 35 | 36 | /// 37 | /// Private constructor for . 38 | /// 39 | private PerformanceInfoStep(PerformanceMeter performanceMeter, string stepName, double minSaveMs = -1) 40 | { 41 | this._performanceMeter = performanceMeter; 42 | this._stepName = stepName; 43 | this._minSaveMs = minSaveMs; 44 | this._innerStopwatch.Start(); 45 | this._dateStart = DateTime.UtcNow - this._innerStopwatch.Elapsed; 46 | 47 | } 48 | 49 | /// 50 | /// Static constructor for . 51 | /// 52 | static PerformanceInfoStep() { } 53 | 54 | // Use C# destructor syntax for finalization code. 55 | // This destructor will run only if the Dispose method 56 | // does not get called. 57 | // It gives your base class the opportunity to finalize. 58 | // Do not provide destructors in types derived from this class. 59 | /// 60 | /// Destructor for . 61 | /// 62 | ~PerformanceInfoStep() 63 | { 64 | // Do not re-create Dispose clean-up code here. 65 | // Calling Dispose(false) is optimal in terms of 66 | // readability and maintainability. 67 | Dispose(false); 68 | } 69 | 70 | #endregion 71 | 72 | #region Main methods 73 | 74 | /// 75 | /// Create an instance of the class to watching method step performance. 76 | /// 77 | /// . 78 | /// Step name. 79 | /// 80 | /// Returns . 81 | /// 82 | internal static PerformanceInfoStep WatchingStep(PerformanceMeter performanceMeter, string stepName) 83 | { 84 | return new PerformanceInfoStep(performanceMeter, stepName); 85 | } 86 | 87 | /// 88 | /// Create an instance of the class to watching method step performance. 89 | /// 90 | /// . 91 | /// Step name. 92 | /// The minimum time to take before this step is saved (e.g. if it’s fast, leave it out). 93 | /// 94 | /// Returns . 95 | /// 96 | internal static PerformanceInfoStep WatchingStepIf(PerformanceMeter performanceMeter, string stepName, double minSaveMs) 97 | { 98 | return new PerformanceInfoStep(performanceMeter, stepName, minSaveMs); 99 | } 100 | 101 | /// 102 | /// Add custom data to performance meter step information. 103 | /// 104 | /// Key. 105 | /// Value. 106 | /// 107 | /// Returns . 108 | /// 109 | public PerformanceInfoStep AddCustomData(string key, object value) 110 | { 111 | this._customData.TryAdd(key, value); 112 | return this; 113 | } 114 | 115 | /// 116 | /// Get and remove custom data from performance meter step information. 117 | /// 118 | /// Type of custom data result. 119 | /// Key. 120 | /// 121 | /// Returns typed result. 122 | /// 123 | public TResult GetAndRemoveCustomData(string key) 124 | { 125 | if (this._customData.ContainsKey(key)) 126 | { 127 | if (this._customData.TryRemove(key, out var result) && result is TResult typedResult) 128 | { 129 | return typedResult; 130 | } 131 | } 132 | return default; 133 | } 134 | 135 | /// 136 | /// Get custom data from performance meter step information. 137 | /// 138 | /// Type of custom data result. 139 | /// Key. 140 | /// 141 | /// Returns typed result. 142 | /// 143 | public TResult GetCustomData(string key) 144 | { 145 | if (this._customData.ContainsKey(key)) 146 | { 147 | if (this._customData.TryRemove(key, out var result) && result is TResult typedResult) 148 | { 149 | this._customData.TryAdd(key, typedResult); 150 | return typedResult; 151 | } 152 | } 153 | return default; 154 | } 155 | 156 | /// 157 | /// Clear custom data to performance meter step information. 158 | /// 159 | public void ClearCustomData() 160 | { 161 | this._customData.Clear(); 162 | } 163 | 164 | /// 165 | /// Silences a performance meter for the duration, use in a using to silence for the duration. 166 | /// 167 | /// 168 | /// Returns . 169 | /// 170 | internal PerformanceInfoStep WithoutWatching() 171 | { 172 | this._minSaveMs = double.MaxValue; 173 | if (this._performanceMeter.InnerStopwatch.IsRunning) 174 | { 175 | this._wasStopped = true; 176 | this._performanceMeter.InnerStopwatch.Stop(); 177 | } 178 | return this; 179 | } 180 | 181 | /// 182 | /// Implement IDisposable. 183 | /// 184 | /// 185 | /// Stop watching method step performance. 186 | /// 187 | public void Dispose() 188 | { 189 | Dispose(true); 190 | // This object will be cleaned up by the Dispose method. 191 | // Therefore, you should call GC.SupressFinalize to 192 | // take this object off the finalization queue 193 | // and prevent finalization code for this object 194 | // from executing a second time. 195 | GC.SuppressFinalize(this); 196 | } 197 | 198 | // Dispose(bool disposing) executes in two distinct scenarios. 199 | // If disposing equals true, the method has been called directly 200 | // or indirectly by a user code. Managed and unmanaged resources 201 | // can be disposed. 202 | // If disposing equals false, the method has been called by the 203 | // runtime from inside the finalizer and you should not reference 204 | // other objects. Only unmanaged resources can be disposed. 205 | private void Dispose(bool disposing) 206 | { 207 | // Check to see if Dispose has already been called. 208 | if (!this._disposed) 209 | { 210 | if (disposing) 211 | { 212 | if (this._innerStopwatch?.IsRunning == true) 213 | { 214 | this._innerStopwatch.Stop(); 215 | if (this._minSaveMs <= 0 || this._innerStopwatch.Elapsed.TotalMilliseconds >= this._minSaveMs) 216 | this._performanceMeter.Steps.Add(PerformanceInfoStepData.Create(this._stepName, this._innerStopwatch.Elapsed, this._dateStart, this._customData)); 217 | this._innerStopwatch = null; 218 | if (this._wasStopped) 219 | this._performanceMeter.InnerStopwatch.Start(); 220 | } 221 | } 222 | } 223 | this._disposed = true; 224 | } 225 | 226 | #endregion 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/PerformanceMeterBaseOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Unchase.FluentPerformanceMeter.Attributes; 5 | 6 | namespace Unchase.FluentPerformanceMeter 7 | { 8 | /// 9 | /// Various configuration properties for Unchase.FluentPerformanceMeter. 10 | /// 11 | public class PerformanceMeterBaseOptions 12 | { 13 | #region Properties 14 | 15 | /// 16 | /// Assembly version of the Unchase.FluentPerformanceMeter. 17 | /// 18 | public static Version Version { get; } = typeof(PerformanceMeterBaseOptions).GetTypeInfo().Assembly.GetName().Version; 19 | 20 | /// 21 | /// The hash to use for file cache breaking, this is automatically calculated. 22 | /// 23 | public virtual string VersionHash { get; } = typeof(PerformanceMeterBaseOptions).GetTypeInfo().Assembly.GetCustomAttribute()?.InformationalVersion ?? Version.ToString(); 24 | 25 | /// 26 | /// Methods to exclude from performance watching. 27 | /// 28 | public HashSet ExcludedMethods { get; } = new HashSet 29 | { 30 | "lambda_method", 31 | ".ctor" 32 | }; 33 | 34 | /// 35 | /// Allow to use for excluding from performance watching. 36 | /// Default value is true. 37 | /// 38 | public bool UseIgnoreMethodPerformanceAttribute { get; set; } = true; 39 | 40 | /// 41 | /// Add custom data from custom attributes. 42 | /// Default value is true. 43 | /// 44 | public bool AddCustomDataFromCustomAttributes { get; set; } = true; 45 | 46 | /// 47 | /// Adds a scope service of the PerformanceMeter of Class. 48 | /// Default value is true. 49 | /// 50 | public bool RegisterPerformanceMeterScope { get; set; } = true; 51 | 52 | #endregion 53 | 54 | #region Methods 55 | 56 | /// 57 | /// Called when passed to . 58 | /// 59 | protected virtual void OnConfigure() { } 60 | 61 | internal void Configure() => OnConfigure(); 62 | 63 | #endregion 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/StringBuilderCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading; 4 | 5 | namespace Unchase.FluentPerformanceMeter 6 | { 7 | /// 8 | /// Provides optimized access to StringBuilder instances 9 | /// Credit: Marc Gravell (@marcgravell), Stack Exchange Inc. 10 | /// 11 | public static class StringBuilderCache 12 | { 13 | #region Fields 14 | 15 | // one per thread 16 | [ThreadStatic] 17 | private static StringBuilder _perThread; 18 | 19 | // and one secondary that is shared between threads 20 | private static StringBuilder _shared; 21 | 22 | private const int DefaultCapacity = 0x10; 23 | 24 | #endregion 25 | 26 | #region Methods 27 | 28 | /// 29 | /// Obtain a StringBuilder instance; this could be a recycled instance, or could be new 30 | /// 31 | /// The capacity to start the fetched at. 32 | public static StringBuilder Get(int capacity = DefaultCapacity) 33 | { 34 | var tmp = _perThread; 35 | if (tmp != null) 36 | { 37 | _perThread = null; 38 | tmp.Length = 0; 39 | return tmp; 40 | } 41 | 42 | tmp = Interlocked.Exchange(ref _shared, null); 43 | if (tmp == null) return new StringBuilder(capacity); 44 | tmp.Length = 0; 45 | return tmp; 46 | } 47 | 48 | /// 49 | /// Get the string contents of a StringBuilder and recycle the instance at the same time 50 | /// 51 | /// The to recycle. 52 | public static string ToStringRecycle(this StringBuilder builder) 53 | { 54 | var s = builder.ToString(); 55 | Recycle(builder); 56 | return s; 57 | } 58 | 59 | /// 60 | /// Get the string contents of a StringBuilder and recycle the instance at the same time 61 | /// 62 | /// The to recycle. 63 | /// The index to start at. 64 | /// The amount of characters to get. 65 | public static string ToStringRecycle(this StringBuilder builder, int startIndex, int length) 66 | { 67 | var s = builder.ToString(startIndex, length); 68 | Recycle(builder); 69 | return s; 70 | } 71 | 72 | /// 73 | /// Recycles a StringBuilder instance if possible 74 | /// 75 | /// The to recycle. 76 | public static void Recycle(StringBuilder builder) 77 | { 78 | if (builder == null) return; 79 | if (_perThread == null) 80 | { 81 | _perThread = builder; 82 | } 83 | else 84 | { 85 | Interlocked.CompareExchange(ref _shared, builder, null); 86 | } 87 | } 88 | 89 | #endregion 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Unchase.FluentPerformanceMeter/Unchase.FluentPerformanceMeter.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | true 6 | Unchase 7 | Copyrigth © Unchase 2019 8 | https://github.com/unchase/Unchase.PerformanceMeter 9 | https://github.com/unchase/Unchase.PerformanceMeter 10 | Github 11 | performance benchmark benchmarking csharp dot-net 12 | en 13 | Unchase Fluent Performance Meter is an open-source and cross-platform .Net Standart 2.0 library is designed for the method’s performance measurement. 14 | icon.png 15 | 16 | Apache-2.0 17 | 2.1.3.0 18 | 2.1.3.0 19 | 2.1.3 20 | Unchase.FluentPerformanceMeter.xml 21 | 22 | 23 | 24 | Unchase.FluentPerformanceMeter.xml 25 | 26 | 27 | 28 | Unchase.FluentPerformanceMeter.xml 29 | 30 | 31 | 32 | 33 | True 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 2.1.{build} 2 | pull_requests: 3 | do_not_increment_build_number: true 4 | skip_tags: true 5 | image: Previous Visual Studio 2019 6 | platform: Any CPU 7 | dotnet_csproj: 8 | patch: true 9 | file: '**\*.csproj' 10 | version: '{version}' 11 | package_version: '{version}' 12 | assembly_version: '{version}' 13 | file_version: '{version}' 14 | informational_version: '{version}' 15 | build: 16 | verbosity: minimal 17 | configuration: Release 18 | before_build: 19 | - ps: nuget restore Unchase.FluentPerformanceMeter.sln 20 | build_script: 21 | - msbuild Unchase.FluentPerformanceMeter.sln /p:configuration=Release /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal /v:m 22 | artifacts: 23 | - path: artifacts/Unchase.*.nupkg 24 | deploy: 25 | - provider: GitHub 26 | tag: v$(appveyor_build_version) 27 | release: Unchase.FluentPerformanceMeter-v$(appveyor_build_version) 28 | auth_token: 29 | secure: 5YjB5tKbw0Z/mnSTKxo3WLD9TWuyGpGPhaNlSTA+cFA1oORUk46i6tPuyvekHaS9 30 | repository: unchase/Unchase.FluentPerformanceMeter 31 | artifact: /Unchase.*\.nupkg/ 32 | force_update: true 33 | # on: 34 | #branch: master # release from master branch only 35 | #appveyor_repo_tag: false # deploy on tag push only 36 | - provider: NuGet 37 | api_key: 38 | secure: jsvXBsuTRifpLtKgXYBysrAESPumHnOy3bXJXzUvHetfR4i35gsBi+xtOItUopA0 39 | skip_symbols: true 40 | artifact: /Unchase.*\.nupkg/ 41 | notifications: 42 | - provider: Email 43 | to: 44 | - spiritkola@hotmail.com 45 | subject: 'Unchase.FluentPerformanceMeter - Build {{status}}' 46 | message: "Building complete, commitId = {{commitId}}" 47 | on_build_status_changed: true -------------------------------------------------------------------------------- /img/buymeacoffe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unchase/Unchase.FluentPerformanceMeter/dee35b4d8fe17282cbbfde77354317f99b0856ec/img/buymeacoffe.png -------------------------------------------------------------------------------- /img/charts1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unchase/Unchase.FluentPerformanceMeter/dee35b4d8fe17282cbbfde77354317f99b0856ec/img/charts1.png -------------------------------------------------------------------------------- /img/charts2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unchase/Unchase.FluentPerformanceMeter/dee35b4d8fe17282cbbfde77354317f99b0856ec/img/charts2.png -------------------------------------------------------------------------------- /img/charts3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unchase/Unchase.FluentPerformanceMeter/dee35b4d8fe17282cbbfde77354317f99b0856ec/img/charts3.png -------------------------------------------------------------------------------- /img/charts4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unchase/Unchase.FluentPerformanceMeter/dee35b4d8fe17282cbbfde77354317f99b0856ec/img/charts4.png -------------------------------------------------------------------------------- /img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unchase/Unchase.FluentPerformanceMeter/dee35b4d8fe17282cbbfde77354317f99b0856ec/img/icon.png -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unchase/Unchase.FluentPerformanceMeter/dee35b4d8fe17282cbbfde77354317f99b0856ec/img/logo.png --------------------------------------------------------------------------------