├── .github └── workflows │ ├── publish-nuget-packages.yaml │ └── run-tests.yaml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Directory.Build.props ├── LICENSE ├── README.md ├── prometheus-net.Contrib.Tests ├── CountersTests.cs └── prometheus-net.Contrib.Tests.csproj ├── prometheus-net.Contrib.sln ├── samples └── WebApp │ ├── Controllers │ └── TestController.cs │ ├── Data │ └── TestContext.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Startup.cs │ ├── WebApp.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── docker-compose.yml └── src ├── prometheus-net.Contrib ├── AssemblyInfo.cs ├── Core │ ├── DiagnosticListenerHandler.cs │ ├── DiagnosticSourceListener.cs │ ├── DiagnosticSourceSubscriber.cs │ ├── ICounterAdapter.cs │ └── PropertyFetcher.cs ├── DependencyInjection │ └── DiagnosticServiceCollectionExtensions.cs ├── Diagnostics │ ├── AspNetCoreListenerHandler.cs │ ├── GrpcClientListenerHandler.cs │ ├── HttpClientListenerHandler.cs │ └── SqlClientListenerHandler.cs ├── EventListeners │ ├── Adapters │ │ ├── BaseAdapter.cs │ │ ├── PrometheusAspNetCoreCounterAdapter.cs │ │ ├── PrometheusEfCoreCounterAdapter.cs │ │ ├── PrometheusGrpcClientCounterAdapter.cs │ │ ├── PrometheusGrpcServerCounterAdapter.cs │ │ ├── PrometheusHttpClientCounterAdapter.cs │ │ ├── PrometheusKestrelCounterAdapter.cs │ │ ├── PrometheusNetNameResolutionCounterAdapter.cs │ │ ├── PrometheusNetSecurityCounterAdapter.cs │ │ ├── PrometheusNetSocketsCounterAdapter.cs │ │ ├── PrometheusRuntimeCounterAdapter.cs │ │ ├── PrometheusSignalRCounterAdapter.cs │ │ └── PrometheusSqlClientCounterAdapter.cs │ ├── Counters │ │ ├── BaseCounter.cs │ │ ├── CounterUtils.cs │ │ ├── IncrementCounter.cs │ │ └── MeanCounter.cs │ └── CountersEventListener.cs ├── Healthchecks │ └── PrometheusHealthcheckPublisher.cs ├── Options │ └── SqlMetricsOptions.cs └── prometheus-net.Contrib.csproj ├── prometheus-net.EasyCaching ├── DiagnosticServiceCollectionExtensions.cs ├── Diagnostics │ └── EasyCachingListenerHandler.cs └── prometheus-net.EasyCaching.csproj ├── prometheus-net.EntityFramework ├── DiagnosticServiceCollectionExtensions.cs ├── Diagnostics │ └── EntityFrameworkListenerHandler.cs └── prometheus-net.EntityFramework.csproj ├── prometheus-net.Esquio ├── DiagnosticServiceCollectionExtensions.cs ├── Diagnostics │ └── EsquioListenerHandler.cs └── prometheus-net.Esquio.csproj └── prometheus-net.IdentityServer ├── DiagnosticServiceCollectionExtensions.cs ├── Sinks └── PrometheusEventsSink.cs └── prometheus-net.IdentityServer.csproj /.github/workflows/publish-nuget-packages.yaml: -------------------------------------------------------------------------------- 1 | name: publish-nuget-packages 2 | 3 | on: 4 | release: 5 | types: [published, prereleased] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - uses: actions/setup-dotnet@v1 13 | with: 14 | dotnet-version: '5.0.101' 15 | - run: dotnet pack prometheus-net.Contrib.sln --include-symbols -c "Release" --output "build/" 16 | - run: dotnet nuget push "build/*.symbols.nupkg" -k ${{ secrets.NUGET_API_KEY }} -s "https://api.nuget.org/v3/index.json" -n true 17 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yaml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | push: 5 | branches: master 6 | pull_request: 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: actions/setup-dotnet@v1 16 | with: 17 | dotnet-version: '5.0.101' 18 | - name: dotnet test 19 | run: dotnet test -c "Release" 20 | -------------------------------------------------------------------------------- /.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 262 | App_Data 263 | coverage 264 | db/content_*.sql 265 | **/keys/*.xml 266 | 267 | .DS_Store 268 | ui/.DS_Store 269 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at alex.valuiskyi@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MIT 5 | https://github.com/alexvaluyskiy/prometheus-net-contrib 6 | https://github.com/alexvaluyskiy/prometheus-net-contrib 7 | alexvaluyskiy 8 | alexvaluyskiy 9 | true 10 | 0.9.5 11 | prometheus metrics 12 | Exposes .NET core diagnostic listeners and counters 13 | 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alex Valuyskiy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # prometheus-net-contrib 2 | 3 | ![Build status](https://github.com/alexvaluyskiy/prometheus-net-contrib/workflows/run-tests/badge.svg) [![Nuget](https://img.shields.io/nuget/v/prometheus-net.Contrib.svg)](https://www.nuget.org/packages/prometheus-net.Contrib/) 4 | 5 | A plugin for the [prometheus-net](https://github.com/prometheus-net/prometheus-net) package, exposing event counters and diagnostic listeners for .NET Core Runtime, ASP.NET Core, SignalR, GRPC, etc. 6 | 7 | ## Installation 8 | Supports .NET core v3.0+ only. 9 | 10 | Add the package from [nuget](https://www.nuget.org/packages/prometheus-net.Contrib): 11 | ```powershell 12 | dotnet add package prometheus-net.Contrib 13 | ``` 14 | 15 | And then start the collectors: 16 | ```csharp 17 | public class Startup 18 | { 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | ... 22 | services.AddPrometheusCounters(); 23 | services.AddPrometheusAspNetCoreMetrics(); 24 | services.AddPrometheusHttpClientMetrics(); 25 | services.AddPrometheusSqlClientMetrics(); 26 | } 27 | } 28 | ``` 29 | 30 | ## .NET Core 3.0 Event Counters 31 | 32 | ### Runtime 33 | | Name | Type | Description | 34 | |------------------------------------------|-------|--------------------------------------| 35 | | runtime_cpu_usage_ratio | Gauge | CPU usage in percents | 36 | | runtime_memory_working_set_megabytes | Gauge | Working Set in megabytes | 37 | | runtime_gc_heap_size_megabytes | Gauge | GC Heap Size in megabytes | 38 | | runtime_gc_count | Gauge | GC Count | 39 | | runtime_exceptions_total | Gauge | Exception Count | 40 | | runtime_threadpool_threads_total | Gauge | ThreadPool Thread Count | 41 | | runtime_lock_contention_total | Gauge | Monitor Lock Contention Count | 42 | | runtime_threadpool_queue_length | Gauge | ThreadPool Queue Length | 43 | | runtime_threadpool_completed_items_total | Gauge | ThreadPool Completed Work Item Count | 44 | | runtime_time_in_gc_ratio | Gauge | % Time in GC since last GC | 45 | | runtime_gc_size_bytes | Gauge | GC size in bytes | 46 | | runtime_allocation_rate_bytes | Gauge | Allocation Rate in bytes | 47 | | runtime_assemblies_total | Gauge | Number of Assemblies Loaded | 48 | | runtime_active_timers_total | Gauge | Number of Active Timers | 49 | 50 | ### ASP .NET Core 51 | 52 | | Name | Type | Description | 53 | |-----------------------------------|-------|------------------| 54 | | aspnetcore_requests_per_second | Gauge | Request Rate | 55 | | aspnetcore_requests_total | Gauge | Total Requests | 56 | | aspnetcore_requests_current_total | Gauge | Current Requests | 57 | | aspnetcore_requests_failed_total | Gauge | Failed Requests | 58 | 59 | ### ASP .NET Core SignalR 60 | 61 | | Name | Type | Description | 62 | |--------------------------------------|-------|-----------------------------| 63 | | signalr_connections_started_total | Gauge | Total Connections Started | 64 | | signalr_connections_stopped_total | Gauge | Total Connections Stopped | 65 | | signalr_connections_timed_out_total | Gauge | Total Connections Timed Out | 66 | | signalr_connections_current_total | Gauge | Current Connections | 67 | | signalr_connections_duration_seconds | Gauge | Average Connection Duration | 68 | 69 | ### ASP .NET Core GRPC Server 70 | 71 | | Name | Type | Description | 72 | |---------------------------------------|-------|-------------------------------| 73 | | grpc_server_calls_total | Gauge | Total Calls | 74 | | grpc_server_calls_current_total | Gauge | Current Calls | 75 | | grpc_server_calls_failed_total | Gauge | Total Calls Failed | 76 | | grpc_server_deadline_exceeded_total | Gauge | Total Calls Deadline Exceeded | 77 | | grpc_server_messages_sent_total | Gauge | Total Messages Sent | 78 | | grpc_server_messages_received_total | Gauge | Total Messages Received | 79 | | grpc_server_calls_unimplemented_total | Gauge | Total Calls Unimplemented | 80 | 81 | ### ASP .NET Core GRPC Client 82 | 83 | | Name | Type | Description | 84 | |-------------------------------------------|-------|-------------------------------| 85 | | grpc_client_calls_total | Gauge | Total Calls | 86 | | grpc_client_calls_current_total | Gauge | Current Calls | 87 | | grpc_client_calls_failed_total | Gauge | Total Calls Failed | 88 | | grpc_client_calls_deadline_exceeded_total | Gauge | Total Calls Deadline Exceeded | 89 | | grpc_client_messages_sent_total | Gauge | Total Messages Sent | 90 | | grpc_client_messages_received_total | Gauge | Total Messages Received | 91 | 92 | ## .NET Core Diagnostic Listeners 93 | ### ASP .NET Core 94 | 95 | | Name | Type | Description | 96 | |--------------------------------------|-----------|------------------------------------------------------------------------| 97 | | aspnetcore_requests_duration_seconds | Histogram | The duration of HTTP requests processed by an ASP.NET Core application | 98 | | aspnetcore_requests_errors_total | Counter | Total HTTP requests received errors | 99 | 100 | ### HTTP Client 101 | 102 | | Name | Type | Description | 103 | |---------------------------------------|-----------|-----------------------------------------------------------------------------------| 104 | | http_client_requests_duration_seconds | Histogram | Time between first byte of request headers sent to last byte of response received | 105 | | http_client_requests_errors_total | Counter | Total HTTP requests sent errors | 106 | 107 | ### SQL Client 108 | 109 | | Name | Type | Description | 110 | |----------------------------------------|-----------|---------------------------------------------------------| 111 | | sqlclient_commands_duration_seconds | Histogram | The duration of DB requests processed by an application | 112 | | sqlclient_commands_errors_total | Counter | Total DB command errors | 113 | | sqlclient_connections_opened_total | Counter | Total opened DB connections | 114 | | sqlclient_connections_closed_total | Counter | Total closed DB connections | 115 | | sqlclient_connections_errors_total | Counter | Total DB connections errors | 116 | | sqlclient_transactions_committed_total | Counter | Total committed transactions | 117 | | sqlclient_transactions_rollback_total | Counter | Total rollback transactions | 118 | | sqlclient_transactions_errors_total | Counter | Total DB transaction errors | 119 | 120 | ### SQL Client (Microsoft.Data.SqlClient 3.x) 121 | | Name | Type | Description | 122 | |-------------------------------------------------|-------|---------------------------------------------------------| 123 | | sqlclient_active_hard_connections_total | Gauge | Actual active connections are made to servers | 124 | | sqlclient_hard_connects_per_second | Gauge | Actual connections are made to servers | 125 | | sqlclient_hard_disconnects_per_second | Gauge | Actual disconnections are made to servers | 126 | | sqlclient_active_soft_connections_total | Gauge | Active connections got from connection pool | 127 | | sqlclient_soft_connects_per_second | Gauge | Connections got from connection pool | 128 | | sqlclient_soft_disconnects_per_second | Gauge | Connections returned to the connection pool | 129 | | sqlclient_non_pooled_connections_total | Gauge | Number of connections are not using connection pooling | 130 | | sqlclient_pooled_connections_total | Gauge | Number of connections are managed by connection pooler | 131 | | sqlclient_active_connection_pool_groups_total | Gauge | Number of active unique connection strings | 132 | | sqlclient_inactive_connection_pool_groups_total | Gauge | Number of unique connection strings waiting for pruning | 133 | | sqlclient_active_connection_pools_total | Gauge | Number of active connection pools | 134 | | sqlclient_inactive_connection_pools_total | Gauge | Actual connections are made to servers | 135 | | sqlclient_active_connections_total | Gauge | Number of active connections | 136 | | sqlclient_free_connections_total | Gauge | Number of free-ready connections | 137 | | sqlclient_stasis_connections_total | Gauge | Number of connections currently waiting to be ready | 138 | | sqlclient_reclaimed_connections_total | Gauge | Number of reclaimed connections from GC | 139 | 140 | ### GRPC Client 141 | | Name | Type | Description | 142 | |---------------------------------------|-----------|-----------------------------------------------------------------------------------| 143 | | grpc_client_requests_duration_seconds | Histogram | Time between first byte of request headers sent to last byte of response received | 144 | | grpc_client_requests_errors_total | Counter | Total GRPC requests sent errors | 145 | 146 | ### Identity Server 147 | 148 | ```powershell 149 | dotnet add package prometheus-net.IdentityServer 150 | ``` 151 | 152 | And then start the collectors: 153 | ```csharp 154 | public class Startup 155 | { 156 | public void ConfigureServices(IServiceCollection services) 157 | { 158 | services.AddIdentityServer(options => 159 | { 160 | options.Events.RaiseErrorEvents = true; 161 | options.Events.RaiseFailureEvents = true; 162 | options.Events.RaiseInformationEvents = true; 163 | options.Events.RaiseSuccessEvents = true; 164 | }); 165 | 166 | services.AddPrometheusIdentityServerMetrics(); 167 | } 168 | } 169 | ``` 170 | 171 | | Name | Type | Description | 172 | |-------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------| 173 | | idsrv_api_authentication_failure_total | Counter | Gets raised for successful API authentication at the introspection endpoint | 174 | | idsrv_api_authentication_failure_total | Counter | Gets raised for failed API authentication at the introspection endpoint | 175 | | idsrv_client_authentication_success_total | Counter | Gets raised for successful client authentication at the token endpoint | 176 | | idsrv_client_authentication_failure_total | Counter | Gets raised for failed client authentication at the token endpoint | 177 | | idsrv_token_issued_success_total | Counter | Gets raised for successful attempts to request access tokens | 178 | | idsrv_token_issued_failure_total | Counter | Gets raised for failed attempts to request access tokens | 179 | | idsrv_token_introspection_success_total | Counter | Gets raised for successful attempts to request identity tokens, access tokens, refresh tokens and authorization codes | 180 | | idsrv_token_introspection_failure_total | Counter | Gets raised for failed attempts to request identity tokens, access tokens, refresh tokens and authorization codes | 181 | | idsrv_token_revoked_success_total | Counter | Gets raised for successful token revocation requests. | 182 | | idsrv_user_login_success_total | Counter | Gets raised by the UI for successful user logins | 183 | | idsrv_user_login_failure_total | Counter | Gets raised by the UI for failed user logins | 184 | | idsrv_user_logout_success_total | Counter | Gets raised for successful logout requests | 185 | | idsrv_consent_granted_total | Counter | Gets raised in the consent UI | 186 | | idsrv_consent_denied_total | Counter | Gets raised in the consent UI | 187 | | idsrv_unhandled_exceptions_total | Counter | Gets raised for unhandled exceptions | 188 | | idsrv_device_authorization_success_total | Counter | Gets raised for successful device authorization requests | 189 | | idsrv_device_authorization_success_total | Counter | Gets raised for failed device authorization requests | 190 | 191 | ## Prometheus healthchecks 192 | It is possible to publish all healthchecks results to a prometheus 193 | ```csharp 194 | public virtual void ConfigureServices(IServiceCollection services) 195 | { 196 | services.AddHealthChecks() 197 | .AddSqlServer("", name: "sqlserver") 198 | 199 | services.AddSingleton(); 200 | 201 | return services; 202 | } 203 | ``` 204 | -------------------------------------------------------------------------------- /prometheus-net.Contrib.Tests/CountersTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Prometheus.Contrib.EventListeners.Counters; 4 | using Xunit; 5 | 6 | namespace prometheus_net.Contrib.Tests 7 | { 8 | public class CountersTests 9 | { 10 | [Fact] 11 | public void MeanCounter_WhenMeanIsGreaterThanZero_ThenUseMeanForValue() 12 | { 13 | var meanCounter = new MeanCounter("test1", "test2", "test3"); 14 | 15 | var eventData = new Dictionary 16 | { 17 | ["Name"] = "cpu-usage", 18 | ["DisplayName"] = "CPU Usage", 19 | ["Mean"] = 15d, 20 | ["StandardDeviation"] = 0, 21 | ["Count"] = 1, 22 | ["Min"] = 0, 23 | ["Max"] = 0, 24 | ["InvervalSec"] = 9.9996233, 25 | ["Series"] = "Interval=10000", 26 | ["CounterType"] = "Mean", 27 | ["Metadata"] = "", 28 | ["DisplayUnits"] = "%", 29 | }; 30 | 31 | meanCounter.TryReadEventCounterData(eventData); 32 | 33 | Assert.Equal(15, meanCounter.Metric.Value); 34 | } 35 | 36 | [Fact] 37 | public void MeanCounter_WhenMeanIsZero_ThenUseCountForValue() 38 | { 39 | var meanCounter = new MeanCounter("test1", "test2", "test3"); 40 | 41 | var eventData = new Dictionary 42 | { 43 | ["Name"] = "active-db-contexts", 44 | ["DisplayName"] = "Active DbContexts", 45 | ["Mean"] = 0d, 46 | ["StandardDeviation"] = 0, 47 | ["Count"] = 5, 48 | ["Min"] = 0, 49 | ["Max"] = 0, 50 | ["InvervalSec"] = 9.9996233, 51 | ["Series"] = "Interval=10000", 52 | ["CounterType"] = "Mean", 53 | ["Metadata"] = "", 54 | ["DisplayUnits"] = "", 55 | }; 56 | 57 | meanCounter.TryReadEventCounterData(eventData); 58 | 59 | Assert.Equal(5, meanCounter.Metric.Value); 60 | } 61 | 62 | [Fact] 63 | public void MeanCounter_WhenMeanIsNaN_ThenUseZeroForValue() 64 | { 65 | var meanCounter = new MeanCounter("test1", "test2", "test3"); 66 | 67 | var eventData = new Dictionary 68 | { 69 | ["Name"] = "compiled-query-cache-hit-rate", 70 | ["DisplayName"] = "Query Cache Hit Rate", 71 | ["Mean"] = double.NaN, 72 | ["StandardDeviation"] = 0, 73 | ["Count"] = 1, 74 | ["Min"] = double.NaN, 75 | ["Max"] = double.NaN, 76 | ["InvervalSec"] = 9.9996233, 77 | ["Series"] = "Interval=10000", 78 | ["CounterType"] = "Mean", 79 | ["Metadata"] = "", 80 | ["DisplayUnits"] = "%" 81 | }; 82 | 83 | meanCounter.TryReadEventCounterData(eventData); 84 | 85 | Assert.Equal(0, meanCounter.Metric.Value); 86 | } 87 | 88 | [Fact] 89 | public void IncrementCounter_WhenIncrement_ThenUseIncrementForValue() 90 | { 91 | var incrementCounter = new IncrementCounter("test1", "test2", "test3"); 92 | 93 | var eventData = new Dictionary 94 | { 95 | ["Name"] = "optimistic-concurrency-failures-per-second", 96 | ["DisplayName"] = "Optimistic Concurrency Failures", 97 | ["DisplayRateTimeScale"] = "00:00:01", 98 | ["Increment"] = 1d, 99 | ["IntervalSec"] = 675.144775, 100 | ["Metadata"] = "", 101 | ["Series"] = "Interval=10000", 102 | ["CounterType"] = "Sum", 103 | ["DisplayUnits"] = "" 104 | }; 105 | 106 | incrementCounter.TryReadEventCounterData(eventData); 107 | 108 | Assert.Equal(1, incrementCounter.Metric.Value); 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /prometheus-net.Contrib.Tests/prometheus-net.Contrib.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | prometheus_net.Contrib.Tests 6 | 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | all 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /prometheus-net.Contrib.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29324.140 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "prometheus-net.Contrib", "src\prometheus-net.Contrib\prometheus-net.Contrib.csproj", "{D962C339-857C-49CA-AC62-BEF6D4ABCD4B}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3706E795-AB7E-4E74-BE2B-32443C7D8479}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{537F8DEC-BBA4-4AF6-BDF8-D517CADAFDE4}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApp", "samples\WebApp\WebApp.csproj", "{7EB9C835-6EC9-490C-8D18-901E913AE1E9}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "prometheus-net.EntityFramework", "src\prometheus-net.EntityFramework\prometheus-net.EntityFramework.csproj", "{98AA0634-5B0D-47BB-82E0-870C50D65315}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "prometheus-net.EasyCaching", "src\prometheus-net.EasyCaching\prometheus-net.EasyCaching.csproj", "{A19FEE93-6856-452D-84A1-0A934959D823}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "prometheus-net.IdentityServer", "src\prometheus-net.IdentityServer\prometheus-net.IdentityServer.csproj", "{4D84BD15-265B-4429-89BE-8A4015040B2F}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "prometheus-net.Esquio", "src\prometheus-net.Esquio\prometheus-net.Esquio.csproj", "{3A94C4BE-4CAB-43B4-8B9C-C1D9170514A8}" 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AC951721-6E15-4E0A-A15A-E268AA6DDAEA}" 23 | ProjectSection(SolutionItems) = preProject 24 | Directory.Build.props = Directory.Build.props 25 | .github\workflows\publish-nuget-packages.yaml = .github\workflows\publish-nuget-packages.yaml 26 | README.md = README.md 27 | .github\workflows\run-tests.yaml = .github\workflows\run-tests.yaml 28 | EndProjectSection 29 | EndProject 30 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F671BC85-327B-4D38-B79B-468E6E014F28}" 31 | EndProject 32 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "prometheus-net.Contrib.Tests", "prometheus-net.Contrib.Tests\prometheus-net.Contrib.Tests.csproj", "{6F8CB8F7-B50E-4523-AAA2-2AE23DD64DFA}" 33 | EndProject 34 | Global 35 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 36 | Debug|Any CPU = Debug|Any CPU 37 | Release|Any CPU = Release|Any CPU 38 | EndGlobalSection 39 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 40 | {D962C339-857C-49CA-AC62-BEF6D4ABCD4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {D962C339-857C-49CA-AC62-BEF6D4ABCD4B}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {D962C339-857C-49CA-AC62-BEF6D4ABCD4B}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {D962C339-857C-49CA-AC62-BEF6D4ABCD4B}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {7EB9C835-6EC9-490C-8D18-901E913AE1E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {7EB9C835-6EC9-490C-8D18-901E913AE1E9}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {7EB9C835-6EC9-490C-8D18-901E913AE1E9}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {7EB9C835-6EC9-490C-8D18-901E913AE1E9}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {98AA0634-5B0D-47BB-82E0-870C50D65315}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {98AA0634-5B0D-47BB-82E0-870C50D65315}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {98AA0634-5B0D-47BB-82E0-870C50D65315}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {98AA0634-5B0D-47BB-82E0-870C50D65315}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {A19FEE93-6856-452D-84A1-0A934959D823}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {A19FEE93-6856-452D-84A1-0A934959D823}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {A19FEE93-6856-452D-84A1-0A934959D823}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {A19FEE93-6856-452D-84A1-0A934959D823}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {4D84BD15-265B-4429-89BE-8A4015040B2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {4D84BD15-265B-4429-89BE-8A4015040B2F}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {4D84BD15-265B-4429-89BE-8A4015040B2F}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {4D84BD15-265B-4429-89BE-8A4015040B2F}.Release|Any CPU.Build.0 = Release|Any CPU 60 | {3A94C4BE-4CAB-43B4-8B9C-C1D9170514A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {3A94C4BE-4CAB-43B4-8B9C-C1D9170514A8}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {3A94C4BE-4CAB-43B4-8B9C-C1D9170514A8}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {3A94C4BE-4CAB-43B4-8B9C-C1D9170514A8}.Release|Any CPU.Build.0 = Release|Any CPU 64 | {6F8CB8F7-B50E-4523-AAA2-2AE23DD64DFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {6F8CB8F7-B50E-4523-AAA2-2AE23DD64DFA}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {6F8CB8F7-B50E-4523-AAA2-2AE23DD64DFA}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {6F8CB8F7-B50E-4523-AAA2-2AE23DD64DFA}.Release|Any CPU.Build.0 = Release|Any CPU 68 | EndGlobalSection 69 | GlobalSection(SolutionProperties) = preSolution 70 | HideSolutionNode = FALSE 71 | EndGlobalSection 72 | GlobalSection(NestedProjects) = preSolution 73 | {D962C339-857C-49CA-AC62-BEF6D4ABCD4B} = {3706E795-AB7E-4E74-BE2B-32443C7D8479} 74 | {7EB9C835-6EC9-490C-8D18-901E913AE1E9} = {537F8DEC-BBA4-4AF6-BDF8-D517CADAFDE4} 75 | {98AA0634-5B0D-47BB-82E0-870C50D65315} = {3706E795-AB7E-4E74-BE2B-32443C7D8479} 76 | {A19FEE93-6856-452D-84A1-0A934959D823} = {3706E795-AB7E-4E74-BE2B-32443C7D8479} 77 | {4D84BD15-265B-4429-89BE-8A4015040B2F} = {3706E795-AB7E-4E74-BE2B-32443C7D8479} 78 | {3A94C4BE-4CAB-43B4-8B9C-C1D9170514A8} = {3706E795-AB7E-4E74-BE2B-32443C7D8479} 79 | {6F8CB8F7-B50E-4523-AAA2-2AE23DD64DFA} = {F671BC85-327B-4D38-B79B-468E6E014F28} 80 | EndGlobalSection 81 | GlobalSection(ExtensibilityGlobals) = postSolution 82 | SolutionGuid = {A88CA7BA-1BAD-4ADA-A863-C8FC9768A0D0} 83 | EndGlobalSection 84 | EndGlobal 85 | -------------------------------------------------------------------------------- /samples/WebApp/Controllers/TestController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Threading.Tasks; 4 | using EasyCaching.Core; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Data.SqlClient; 7 | using WebApp.Data; 8 | 9 | namespace WebApp.Controllers 10 | { 11 | [ApiController] 12 | [Route("[controller]")] 13 | public class TestController : ControllerBase 14 | { 15 | private readonly SqlConnection sqlConnection; 16 | private readonly TestContext testContext; 17 | private readonly IEasyCachingProvider cachingProvider; 18 | 19 | public TestController(SqlConnection sqlConnection, TestContext testContext, IEasyCachingProviderFactory cachingFactory) 20 | { 21 | this.sqlConnection = sqlConnection; 22 | this.testContext = testContext; 23 | this.cachingProvider = cachingFactory.GetCachingProvider("default"); 24 | } 25 | 26 | [HttpGet("http-in")] 27 | public async Task HttpIn() => Ok("It's OK"); 28 | 29 | [HttpGet("http-out")] 30 | public async Task HttpOut() 31 | { 32 | using var client = new HttpClient(); 33 | await client.GetAsync("https://www.google.com"); 34 | return Ok("It's OK"); 35 | } 36 | 37 | [HttpGet("sql-query")] 38 | public async Task SqlQuery() 39 | { 40 | var command = this.sqlConnection.CreateCommand(); 41 | command.CommandText = "SELECT 1"; 42 | await command.ExecuteNonQueryAsync(); 43 | 44 | return Ok("It's OK"); 45 | } 46 | 47 | [HttpGet("efcore-query")] 48 | public async Task EfCore() 49 | { 50 | await this.testContext.Database.CanConnectAsync(); 51 | 52 | return Ok("It's OK"); 53 | } 54 | 55 | [HttpGet("efcore-save-changes")] 56 | public async Task EfCoreSaveChanges() 57 | { 58 | await this.testContext.SaveChangesAsync(); 59 | 60 | return Ok("It's OK"); 61 | } 62 | 63 | [HttpGet("easy-caching")] 64 | public async Task EasyCaching() 65 | { 66 | const string key = "test-sample"; 67 | 68 | var cachedData = await this.cachingProvider.GetAsync(key); 69 | if (!cachedData.HasValue) 70 | { 71 | var data = new TestCommand { Id = Guid.NewGuid() }; 72 | await this.cachingProvider.SetAsync(key, data, TimeSpan.FromSeconds(5)); 73 | } 74 | 75 | var isExists = await this.cachingProvider.ExistsAsync(key); 76 | if (isExists) 77 | { 78 | await this.cachingProvider.RemoveAsync(key); 79 | } 80 | 81 | return Ok("It's OK"); 82 | } 83 | } 84 | 85 | public class TestCommand 86 | { 87 | public Guid Id { get; set; } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /samples/WebApp/Data/TestContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace WebApp.Data 4 | { 5 | public class TestContext : DbContext 6 | { 7 | public TestContext(DbContextOptions options) : base(options) 8 | { 9 | } 10 | 11 | public DbSet TestEntities { get; set; } 12 | } 13 | 14 | public class TestEntity 15 | { 16 | public int Id { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/WebApp/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | using System; 4 | 5 | namespace WebApp 6 | { 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 | } 22 | -------------------------------------------------------------------------------- /samples/WebApp/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "WebApp": { 5 | "commandName": "Project", 6 | "launchBrowser": true, 7 | "launchUrl": "test/send", 8 | "applicationUrl": "http://localhost:5000", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development" 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /samples/WebApp/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Microsoft.Data.SqlClient; 7 | using Prometheus; 8 | using Microsoft.EntityFrameworkCore; 9 | using Newtonsoft.Json; 10 | using WebApp.Data; 11 | 12 | namespace WebApp 13 | { 14 | public class Startup 15 | { 16 | public Startup(IConfiguration configuration) 17 | { 18 | Configuration = configuration; 19 | } 20 | 21 | public IConfiguration Configuration { get; } 22 | 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | services.AddControllers(); 26 | 27 | services.AddDbContext(options => 28 | { 29 | options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")); 30 | }); 31 | 32 | services.AddSingleton(provider => 33 | { 34 | var sqlConnection = new SqlConnection(Configuration.GetConnectionString("DefaultConnection")); 35 | sqlConnection.StatisticsEnabled = true; 36 | sqlConnection.Open(); 37 | return sqlConnection; 38 | }); 39 | 40 | services.AddPrometheusCounters(); 41 | services.AddPrometheusSqlClientMetrics(); 42 | services.AddPrometheusAspNetCoreMetrics(); 43 | services.AddPrometheusEasyCachingMetrics(); 44 | 45 | AddCachingExtensions(services); 46 | } 47 | 48 | private void AddCachingExtensions(IServiceCollection services) 49 | { 50 | services.AddEasyCaching(options => 51 | { 52 | var connectionString = Configuration.GetConnectionString("RedisConnection"); 53 | 54 | if (!string.IsNullOrEmpty(connectionString)) 55 | { 56 | options.UseRedis( 57 | config => 58 | { 59 | config.DBConfig.Configuration = connectionString; 60 | config.SerializerName = "json"; 61 | config.EnableLogging = true; 62 | }, 63 | "default"); 64 | } 65 | else 66 | { 67 | options.UseInMemory("default"); 68 | } 69 | 70 | options.WithJson( 71 | jsonSerializerSettingsConfigure: json => json.ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 72 | "json"); 73 | }); 74 | } 75 | 76 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 77 | { 78 | Metrics.SuppressDefaultMetrics(); 79 | app.UseMetricServer(); 80 | 81 | if (env.IsDevelopment()) 82 | app.UseDeveloperExceptionPage(); 83 | 84 | app.UseRouting(); 85 | 86 | app.UseAuthorization(); 87 | 88 | app.UseEndpoints(endpoints => 89 | { 90 | endpoints.MapControllers(); 91 | endpoints.MapMetrics(); 92 | }); 93 | 94 | using var scope = app.ApplicationServices.GetRequiredService().CreateScope(); 95 | var context = scope.ServiceProvider.GetService(); 96 | context.Database.Migrate(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /samples/WebApp/WebApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | all 12 | runtime; build; native; contentfiles; analyzers; buildtransitive 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /samples/WebApp/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Data Source=localhost,11433;Initial Catalog=tracing;Persist Security Info=True;User ID=sa;Password=Max12345!", 4 | "RedisConnection": "" 5 | }, 6 | "Logging": { 7 | "LogLevel": { 8 | "Default": "Information", 9 | "Microsoft": "Information", 10 | "Microsoft.Hosting.Lifetime": "Information" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/WebApp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Information", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /samples/WebApp/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | sqlserver: 5 | image: microsoft/mssql-server-linux:2017-latest 6 | environment: 7 | - "ACCEPT_EULA=Y" 8 | - "SA_PASSWORD=Max12345!" 9 | ports: 10 | - 11433:1433 11 | 12 | rabbit: 13 | image: "rabbitmq:3.8.2-management" 14 | environment: 15 | RABBITMQ_ERLANG_COOKIE: "SWQOKODSQALRPCLNMEQG" 16 | RABBITMQ_DEFAULT_USER: "admin" 17 | RABBITMQ_DEFAULT_PASS: "admin" 18 | RABBITMQ_DEFAULT_VHOST: "/" 19 | ports: 20 | - "15672:15672" 21 | - "5672:5672" -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("prometheus-net.Contrib.Tests")] -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Core/DiagnosticListenerHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Prometheus.Contrib.Core 4 | { 5 | public abstract class DiagnosticListenerHandler 6 | { 7 | public DiagnosticListenerHandler(string sourceName) 8 | { 9 | SourceName = sourceName; 10 | } 11 | 12 | public string SourceName { get; } 13 | 14 | public virtual void OnStartActivity(Activity activity, object payload) 15 | { 16 | } 17 | 18 | public virtual void OnStopActivity(Activity activity, object payload) 19 | { 20 | } 21 | 22 | public virtual void OnException(Activity activity, object payload) 23 | { 24 | } 25 | 26 | public virtual void OnCustom(string name, Activity activity, object payload) 27 | { 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Core/DiagnosticSourceListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | 5 | namespace Prometheus.Contrib.Core 6 | { 7 | internal class DiagnosticSourceListener : IObserver> 8 | { 9 | private readonly DiagnosticListenerHandler handler; 10 | 11 | public DiagnosticSourceListener(DiagnosticListenerHandler handler) 12 | { 13 | this.handler = handler ?? throw new ArgumentNullException(nameof(handler)); 14 | } 15 | 16 | public void OnCompleted() 17 | { 18 | } 19 | 20 | public void OnError(Exception error) 21 | { 22 | } 23 | 24 | public void OnNext(KeyValuePair value) 25 | { 26 | try 27 | { 28 | if (value.Key.EndsWith("Start")) 29 | handler.OnStartActivity(Activity.Current, value.Value); 30 | else if (value.Key.EndsWith("Stop")) 31 | handler.OnStopActivity(Activity.Current, value.Value); 32 | else if (value.Key.EndsWith("Exception")) 33 | handler.OnException(Activity.Current, value.Value); 34 | else 35 | handler.OnCustom(value.Key, Activity.Current, value.Value); 36 | } 37 | catch 38 | { 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Core/DiagnosticSourceSubscriber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Threading; 5 | 6 | namespace Prometheus.Contrib.Core 7 | { 8 | public class DiagnosticSourceSubscriber : IDisposable, IObserver 9 | { 10 | private readonly Func handlerFactory; 11 | private readonly Func diagnosticSourceFilter; 12 | private long disposed; 13 | private IDisposable allSourcesSubscription; 14 | private readonly List listenerSubscriptions; 15 | 16 | public DiagnosticSourceSubscriber( 17 | Func handlerFactory, 18 | Func diagnosticSourceFilter) 19 | { 20 | listenerSubscriptions = new List(); 21 | this.handlerFactory = handlerFactory ?? throw new ArgumentNullException(nameof(handlerFactory)); 22 | this.diagnosticSourceFilter = diagnosticSourceFilter; 23 | } 24 | 25 | public void Subscribe() 26 | { 27 | if (allSourcesSubscription == null) 28 | allSourcesSubscription = DiagnosticListener.AllListeners.Subscribe(this); 29 | } 30 | 31 | public void OnNext(DiagnosticListener value) 32 | { 33 | if (Interlocked.Read(ref disposed) == 0 && 34 | diagnosticSourceFilter(value)) 35 | { 36 | var handler = handlerFactory(value.Name); 37 | var listener = new DiagnosticSourceListener(handler); 38 | var subscription = value.Subscribe(listener); 39 | 40 | lock (listenerSubscriptions) 41 | listenerSubscriptions.Add(subscription); 42 | } 43 | } 44 | 45 | public void OnCompleted() 46 | { 47 | } 48 | 49 | public void OnError(Exception error) 50 | { 51 | } 52 | 53 | public void Dispose() 54 | { 55 | if (Interlocked.CompareExchange(ref disposed, 1, 0) == 1) 56 | return; 57 | 58 | lock (listenerSubscriptions) 59 | { 60 | foreach (var listenerSubscription in listenerSubscriptions) 61 | listenerSubscription?.Dispose(); 62 | 63 | listenerSubscriptions.Clear(); 64 | } 65 | 66 | allSourcesSubscription?.Dispose(); 67 | allSourcesSubscription = null; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Core/ICounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Prometheus.Contrib.Core 4 | { 5 | public interface ICounterAdapter 6 | { 7 | void OnCounterEvent(IDictionary eventPayload); 8 | } 9 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Core/PropertyFetcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace Prometheus.Contrib.Core 6 | { 7 | /// 8 | /// PropertyFetcher fetches a property from an object. 9 | /// 10 | /// The type of the property being fetched. 11 | public class PropertyFetcher 12 | { 13 | private readonly string propertyName; 14 | private PropertyFetch innerFetcher; 15 | 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | /// Property name to fetch. 20 | public PropertyFetcher(string propertyName) 21 | { 22 | this.propertyName = propertyName; 23 | } 24 | 25 | /// 26 | /// Fetch the property from the object. 27 | /// 28 | /// Object to be fetched. 29 | /// Property fetched. 30 | public T Fetch(object obj) 31 | { 32 | if (!TryFetch(obj, out T value)) 33 | { 34 | throw new ArgumentException("Supplied object was null or did not match the expected type.", nameof(obj)); 35 | } 36 | 37 | return value; 38 | } 39 | 40 | /// 41 | /// Try to fetch the property from the object. 42 | /// 43 | /// Object to be fetched. 44 | /// Fetched value. 45 | /// if the property was fetched. 46 | public bool TryFetch(object obj, out T value) 47 | { 48 | if (obj == null) 49 | { 50 | value = default; 51 | return false; 52 | } 53 | 54 | if (innerFetcher == null) 55 | { 56 | var type = obj.GetType().GetTypeInfo(); 57 | var property = type.DeclaredProperties.FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.InvariantCultureIgnoreCase)); 58 | if (property == null) 59 | { 60 | property = type.GetProperty(propertyName); 61 | } 62 | 63 | innerFetcher = PropertyFetch.FetcherForProperty(property); 64 | } 65 | 66 | return innerFetcher.TryFetch(obj, out value); 67 | } 68 | 69 | // see https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs 70 | private class PropertyFetch 71 | { 72 | /// 73 | /// Create a property fetcher from a .NET Reflection PropertyInfo class that 74 | /// represents a property of a particular type. 75 | /// 76 | public static PropertyFetch FetcherForProperty(PropertyInfo propertyInfo) 77 | { 78 | if (propertyInfo == null || !typeof(T).IsAssignableFrom(propertyInfo.PropertyType)) 79 | { 80 | // returns null on any fetch. 81 | return new PropertyFetch(); 82 | } 83 | 84 | var typedPropertyFetcher = typeof(TypedPropertyFetch<,>); 85 | var instantiatedTypedPropertyFetcher = typedPropertyFetcher.MakeGenericType( 86 | typeof(T), propertyInfo.DeclaringType, propertyInfo.PropertyType); 87 | return (PropertyFetch)Activator.CreateInstance(instantiatedTypedPropertyFetcher, propertyInfo); 88 | } 89 | 90 | public virtual bool TryFetch(object obj, out T value) 91 | { 92 | value = default; 93 | return false; 94 | } 95 | 96 | private class TypedPropertyFetch : PropertyFetch 97 | where TDeclaredProperty : T 98 | { 99 | private readonly Func propertyFetch; 100 | 101 | public TypedPropertyFetch(PropertyInfo property) 102 | { 103 | propertyFetch = (Func)property.GetMethod.CreateDelegate(typeof(Func)); 104 | } 105 | 106 | public override bool TryFetch(object obj, out T value) 107 | { 108 | if (obj is TDeclaredObject o) 109 | { 110 | value = propertyFetch(o); 111 | return true; 112 | } 113 | 114 | value = default; 115 | return false; 116 | } 117 | } 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/DependencyInjection/DiagnosticServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Prometheus.Contrib.Core; 3 | using Prometheus.Contrib.Diagnostics; 4 | using Prometheus.Contrib.EventListeners; 5 | using Prometheus.Contrib.Options; 6 | 7 | namespace Microsoft.Extensions.DependencyInjection 8 | { 9 | public static class DiagnosticServiceCollectionExtensions 10 | { 11 | public static void AddPrometheusAspNetCoreMetrics(this IServiceCollection services) 12 | { 13 | var aspNetCoreListenerHandler = new DiagnosticSourceSubscriber( 14 | name => new AspNetCoreListenerHandler(name), 15 | listener => listener.Name.Equals("Microsoft.AspNetCore")); 16 | aspNetCoreListenerHandler.Subscribe(); 17 | 18 | services.AddSingleton(aspNetCoreListenerHandler); 19 | } 20 | 21 | public static void AddPrometheusHttpClientMetrics(this IServiceCollection services) 22 | { 23 | var httpClientListenerHandler = new DiagnosticSourceSubscriber( 24 | name => new HttpClientListenerHandler(name), 25 | listener => listener.Name.Equals("HttpHandlerDiagnosticListener")); 26 | httpClientListenerHandler.Subscribe(); 27 | 28 | services.AddSingleton(httpClientListenerHandler); 29 | } 30 | 31 | public static void AddPrometheusSqlClientMetrics(this IServiceCollection services, Action optionsInvoker = null) 32 | { 33 | var sqlMetricsOptions = new SqlMetricsOptions(); 34 | optionsInvoker?.Invoke(sqlMetricsOptions); 35 | 36 | var sqlClientListenerHandler = new DiagnosticSourceSubscriber( 37 | name => new SqlClientListenerHandler(name, sqlMetricsOptions), 38 | listener => listener.Name.Equals("SqlClientDiagnosticListener")); 39 | sqlClientListenerHandler.Subscribe(); 40 | 41 | services.AddSingleton(sqlClientListenerHandler); 42 | } 43 | 44 | public static void AddPrometheusGrpcClientMetrics(this IServiceCollection services) 45 | { 46 | var grpcClientListenerHandler = new DiagnosticSourceSubscriber( 47 | name => new GrpcClientListenerHandler(name), 48 | listener => listener.Name.Equals("Grpc.Net.Client")); 49 | grpcClientListenerHandler.Subscribe(); 50 | 51 | services.AddSingleton(grpcClientListenerHandler); 52 | } 53 | 54 | public static void AddPrometheusCounters(this IServiceCollection services, int refreshPeriodSeconds = 10) 55 | { 56 | services.AddSingleton(new CountersEventListener(refreshPeriodSeconds)); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Diagnostics/AspNetCoreListenerHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Http.Features; 3 | using Microsoft.AspNetCore.Routing; 4 | using Prometheus.Contrib.Core; 5 | using System.Diagnostics; 6 | 7 | namespace Prometheus.Contrib.Diagnostics 8 | { 9 | public class AspNetCoreListenerHandler : DiagnosticListenerHandler 10 | { 11 | private static class PrometheusCounters 12 | { 13 | public static readonly Histogram AspNetCoreRequestsDuration = Metrics.CreateHistogram( 14 | "aspnetcore_requests_duration_seconds", 15 | "The duration of HTTP requests processed by an ASP.NET Core application.", 16 | new HistogramConfiguration 17 | { 18 | LabelNames = new[] { "code", "method", "route" }, 19 | Buckets = Histogram.ExponentialBuckets(0.001, 2, 16) 20 | }); 21 | 22 | public static readonly Counter AspNetCoreRequestsErrors = Metrics.CreateCounter( 23 | "aspnetcore_requests_errors_total", 24 | "Total HTTP requests received errors."); 25 | } 26 | 27 | public AspNetCoreListenerHandler(string sourceName) : base(sourceName) 28 | { 29 | } 30 | 31 | public override void OnStopActivity(Activity activity, object payload) 32 | { 33 | if (payload is HttpContext httpContext) 34 | { 35 | var endpointFeature = httpContext.Features[typeof(IEndpointFeature)] as IEndpointFeature; 36 | string route = endpointFeature?.Endpoint is RouteEndpoint endpoint ? endpoint.RoutePattern.RawText : string.Empty; 37 | 38 | PrometheusCounters.AspNetCoreRequestsDuration 39 | .WithLabels(httpContext.Response.StatusCode.ToString(), httpContext.Request.Method, route) 40 | .Observe(activity.Duration.TotalSeconds); 41 | } 42 | } 43 | 44 | public override void OnException(Activity activity, object payload) 45 | { 46 | PrometheusCounters.AspNetCoreRequestsErrors.Inc(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Diagnostics/GrpcClientListenerHandler.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.Core; 2 | using System.Diagnostics; 3 | using System.Net.Http; 4 | 5 | namespace Prometheus.Contrib.Diagnostics 6 | { 7 | public class GrpcClientListenerHandler : DiagnosticListenerHandler 8 | { 9 | private static class PrometheusCounters 10 | { 11 | public static readonly Histogram GrpcClientRequestsDuration = Metrics.CreateHistogram( 12 | "grpc_client_requests_duration_seconds", 13 | "Time between first byte of request headers sent to last byte of response received.", 14 | new HistogramConfiguration 15 | { 16 | LabelNames = new[] { "code", "host" }, 17 | Buckets = Histogram.ExponentialBuckets(0.001, 2, 16) 18 | }); 19 | 20 | public static readonly Counter GrpcClientRequestsErrors = Metrics.CreateCounter( 21 | "grpc_client_requests_errors_total", 22 | "Total GRPC requests sent errors."); 23 | } 24 | 25 | private readonly PropertyFetcher stopResponseFetcher = new PropertyFetcher("Response"); 26 | 27 | public GrpcClientListenerHandler(string sourceName) : base(sourceName) 28 | { 29 | } 30 | 31 | public override void OnStopActivity(Activity activity, object payload) 32 | { 33 | var response = stopResponseFetcher.Fetch(payload); 34 | 35 | if (response is HttpResponseMessage httpResponse) 36 | PrometheusCounters.GrpcClientRequestsDuration 37 | .WithLabels(httpResponse.StatusCode.ToString("D"), httpResponse.RequestMessage.RequestUri.Host) 38 | .Observe(activity.Duration.TotalSeconds); 39 | } 40 | 41 | public override void OnException(Activity activity, object payload) 42 | { 43 | PrometheusCounters.GrpcClientRequestsErrors.Inc(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Diagnostics/HttpClientListenerHandler.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.Core; 2 | using System.Diagnostics; 3 | using System.Net.Http; 4 | 5 | namespace Prometheus.Contrib.Diagnostics 6 | { 7 | public class HttpClientListenerHandler : DiagnosticListenerHandler 8 | { 9 | private static class PrometheusCounters 10 | { 11 | public static readonly Histogram HttpClientRequestsDuration = Metrics.CreateHistogram( 12 | "http_client_requests_duration_seconds", 13 | "Time between first byte of request headers sent to last byte of response received.", 14 | new HistogramConfiguration 15 | { 16 | LabelNames = new[] { "code", "host" }, 17 | Buckets = Histogram.ExponentialBuckets(0.001, 2, 16) 18 | }); 19 | 20 | public static readonly Counter HttpClientRequestsErrors = Metrics.CreateCounter( 21 | "http_client_requests_errors_total", 22 | "Total HTTP requests sent errors."); 23 | } 24 | 25 | private readonly PropertyFetcher stopResponseFetcher = new PropertyFetcher("Response"); 26 | 27 | public HttpClientListenerHandler(string sourceName) : base(sourceName) 28 | { 29 | } 30 | 31 | public override void OnStopActivity(Activity activity, object payload) 32 | { 33 | var response = stopResponseFetcher.Fetch(payload); 34 | 35 | if (response is HttpResponseMessage httpResponse) 36 | PrometheusCounters.HttpClientRequestsDuration 37 | .WithLabels(httpResponse.StatusCode.ToString("D"), httpResponse.RequestMessage.RequestUri.Host) 38 | .Observe(activity.Duration.TotalSeconds); 39 | } 40 | 41 | public override void OnException(Activity activity, object payload) 42 | { 43 | PrometheusCounters.HttpClientRequestsErrors.Inc(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Diagnostics/SqlClientListenerHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Prometheus.Contrib.Core; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | using Prometheus.Contrib.Options; 8 | 9 | namespace Prometheus.Contrib.Diagnostics 10 | { 11 | public class SqlClientListenerHandler : DiagnosticListenerHandler 12 | { 13 | private readonly SqlMetricsOptions options; 14 | 15 | private static class PrometheusCounters 16 | { 17 | public static readonly Histogram SqlCommandsDuration = Metrics.CreateHistogram( 18 | "sqlclient_commands_duration_seconds", 19 | "The duration of DB requests processed by an application.", 20 | new HistogramConfiguration { Buckets = Histogram.ExponentialBuckets(0.001, 2, 16) }); 21 | public static readonly Counter SqlCommandsErrors = Metrics.CreateCounter( 22 | "sqlclient_commands_errors_total", 23 | "Total DB command errors", 24 | new CounterConfiguration { LabelNames = new[] { "code" } }); 25 | 26 | public static readonly Counter DbConnectionsOpenTotal = Metrics.CreateCounter("sqlclient_connections_opened_total", "Total opened DB connections"); 27 | public static readonly Counter DbConnectionsCloseTotal = Metrics.CreateCounter("sqlclient_connections_closed_total", "Total closed DB connections"); 28 | public static readonly Counter DbConnectionsErrors = Metrics.CreateCounter( 29 | "sqlclient_connections_errors_total", 30 | "Total DB connections errors", 31 | new CounterConfiguration { LabelNames = new[] { "code" } }); 32 | 33 | public static readonly Counter DbTransactionsCommitedCount = Metrics.CreateCounter("sqlclient_transactions_committed_total", "Total committed transactions."); 34 | public static readonly Counter DbTransactionsRollbackCount = Metrics.CreateCounter("sqlclient_transactions_rollback_total", "Total rollback transactions."); 35 | public static readonly Counter DbTransactionsErrors = Metrics.CreateCounter( 36 | "sqlclient_transactions_errors_total", 37 | "Total DB transaction errors", 38 | new CounterConfiguration { LabelNames = new[] { "code" } }); 39 | } 40 | 41 | private readonly PropertyFetcher commandException = new PropertyFetcher("Exception"); 42 | private readonly PropertyFetcher commandExceptionNumber = new PropertyFetcher("Number"); 43 | 44 | private readonly PropertyFetcher connectionException = new PropertyFetcher("Exception"); 45 | private readonly PropertyFetcher connectionExceptionNumber = new PropertyFetcher("Number"); 46 | 47 | private readonly PropertyFetcher transactionException = new PropertyFetcher("Exception"); 48 | private readonly PropertyFetcher transactionExceptionNumber = new PropertyFetcher("Number"); 49 | 50 | private readonly PropertyFetcher rollbackException = new PropertyFetcher("Exception"); 51 | private readonly PropertyFetcher rollbackExceptionNumber = new PropertyFetcher("Number"); 52 | 53 | private readonly AsyncLocal commandTimestampContext = new AsyncLocal(); 54 | 55 | public SqlClientListenerHandler(string sourceName, SqlMetricsOptions options) : base(sourceName) 56 | { 57 | this.options = options; 58 | } 59 | 60 | public override void OnCustom(string name, Activity activity, object payload) 61 | { 62 | if (name.Contains("WriteCommand")) 63 | OnWriteCommand(name, payload); 64 | else if (name.Contains("WriteConnectionOpen")) 65 | OnWriteConnectionOpen(name, payload); 66 | else if (name.Contains("WriteConnectionClose")) 67 | OnWriteConnectionClose(name, payload); 68 | else if (name.Contains("WriteTransactionCommit")) 69 | OnWriteTransactionCommit(name, payload); 70 | else if (name.Contains("WriteTransactionRollback")) 71 | OnWriteTransactionRollback(name, payload); 72 | } 73 | 74 | public void OnWriteCommand(string name, object payload) 75 | { 76 | switch (name) 77 | { 78 | case "Microsoft.Data.SqlClient.WriteCommandBefore": 79 | { 80 | commandTimestampContext.Value = Stopwatch.GetTimestamp(); 81 | } 82 | break; 83 | case "Microsoft.Data.SqlClient.WriteCommandAfter": 84 | { 85 | long ticks = Stopwatch.GetTimestamp() - commandTimestampContext.Value; 86 | var timeElapsed = TimeSpan.FromMilliseconds(((double)ticks / Stopwatch.Frequency) * 1000); 87 | PrometheusCounters.SqlCommandsDuration.Observe(timeElapsed.TotalSeconds); 88 | } 89 | break; 90 | case "Microsoft.Data.SqlClient.WriteCommandError": 91 | { 92 | if (commandException.TryFetch(payload, out var sqlException)) 93 | { 94 | if (commandExceptionNumber.TryFetch(sqlException, out var errorCode)) 95 | { 96 | PrometheusCounters.SqlCommandsErrors.WithLabels(errorCode.ToString()).Inc(); 97 | } 98 | } 99 | } 100 | break; 101 | } 102 | } 103 | 104 | public void OnWriteConnectionOpen(string name, object payload) 105 | { 106 | switch (name) 107 | { 108 | case "Microsoft.Data.SqlClient.WriteConnectionOpenBefore": 109 | { 110 | } 111 | break; 112 | case "Microsoft.Data.SqlClient.WriteConnectionOpenAfter": 113 | { 114 | PrometheusCounters.DbConnectionsOpenTotal.Inc(); 115 | } 116 | break; 117 | case "Microsoft.Data.SqlClient.WriteConnectionOpenError": 118 | { 119 | if (connectionException.TryFetch(payload, out var sqlException)) 120 | { 121 | if (connectionExceptionNumber.TryFetch(sqlException, out var errorCode)) 122 | { 123 | PrometheusCounters.DbConnectionsErrors.WithLabels(errorCode.ToString()).Inc(); 124 | } 125 | } 126 | } 127 | break; 128 | } 129 | } 130 | 131 | public void OnWriteConnectionClose(string name, object payload) 132 | { 133 | switch (name) 134 | { 135 | case "Microsoft.Data.SqlClient.WriteConnectionCloseBefore": 136 | { 137 | } 138 | break; 139 | case "Microsoft.Data.SqlClient.WriteConnectionCloseAfter": 140 | { 141 | PrometheusCounters.DbConnectionsCloseTotal.Inc(); 142 | } 143 | break; 144 | case "Microsoft.Data.SqlClient.WriteConnectionCloseError": 145 | { 146 | PrometheusCounters.DbConnectionsErrors.Inc(); 147 | } 148 | break; 149 | } 150 | } 151 | 152 | public void OnWriteTransactionCommit(string name, object payload) 153 | { 154 | switch (name) 155 | { 156 | case "Microsoft.Data.SqlClient.WriteTransactionCommitBefore": 157 | { 158 | } 159 | break; 160 | case "Microsoft.Data.SqlClient.WriteTransactionCommitAfter": 161 | { 162 | PrometheusCounters.DbTransactionsCommitedCount.Inc(); 163 | } 164 | break; 165 | case "Microsoft.Data.SqlClient.WriteTransactionCommitError": 166 | { 167 | if (transactionException.TryFetch(payload, out var sqlException)) 168 | { 169 | if (transactionExceptionNumber.TryFetch(sqlException, out var errorCode)) 170 | { 171 | PrometheusCounters.DbTransactionsErrors.WithLabels(errorCode.ToString()).Inc(); 172 | } 173 | } 174 | } 175 | break; 176 | } 177 | } 178 | 179 | public void OnWriteTransactionRollback(string name, object payload) 180 | { 181 | switch (name) 182 | { 183 | case "Microsoft.Data.SqlClient.WriteTransactionRollbackBefore": 184 | { 185 | } 186 | break; 187 | case "Microsoft.Data.SqlClient.WriteTransactionRollbackAfter": 188 | { 189 | PrometheusCounters.DbTransactionsRollbackCount.Inc(); 190 | } 191 | break; 192 | case "Microsoft.Data.SqlClient.WriteTransactionRollbackError": 193 | { 194 | if (rollbackException.TryFetch(payload, out var sqlException)) 195 | { 196 | if (rollbackExceptionNumber.TryFetch(sqlException, out var errorCode)) 197 | { 198 | PrometheusCounters.DbTransactionsErrors.WithLabels(errorCode.ToString()).Inc(); 199 | } 200 | } 201 | } 202 | break; 203 | } 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/BaseAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Prometheus.Contrib.Core; 3 | using Prometheus.Contrib.EventListeners.Counters; 4 | 5 | namespace Prometheus.Contrib.EventListeners.Adapters 6 | { 7 | internal abstract class BaseAdapter : ICounterAdapter 8 | { 9 | private readonly Dictionary _counters; 10 | 11 | protected BaseAdapter() 12 | { 13 | _counters = CounterUtils.GenerateDictionary(this); 14 | } 15 | 16 | public void OnCounterEvent(IDictionary eventPayload) 17 | { 18 | if (!eventPayload.TryGetValue("Name", out var counterName)) 19 | { 20 | return; 21 | } 22 | 23 | if (!_counters.TryGetValue((string) counterName, out var counter)) 24 | return; 25 | 26 | counter.TryReadEventCounterData(eventPayload); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusAspNetCoreCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusAspNetCoreCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "Microsoft.AspNetCore.Hosting"; 8 | 9 | internal readonly IncrementCounter RequestsPerSecond = new IncrementCounter("requests-per-second", "aspnetcore_requests_per_second", "Request Rate"); 10 | internal readonly MeanCounter TotalRequests = new MeanCounter("total-requests", "aspnetcore_requests_total", "Total Requests"); 11 | internal readonly MeanCounter CurrentRequests = new MeanCounter("current-requests", "aspnetcore_requests_current_total", "Current Requests"); 12 | internal readonly MeanCounter FailedRequests = new MeanCounter("failed-requests", "aspnetcore_requests_failed_total", "Failed Requests"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusEfCoreCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusEfCoreCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "Microsoft.EntityFrameworkCore"; 8 | 9 | internal readonly MeanCounter ActiveDbContexts = new MeanCounter("active-db-contexts", "efcore_active_dbcontexts_total", "Active DbContexts"); 10 | internal readonly MeanCounter TotalQueries = new MeanCounter("total-queries", "efcore_queries_total", "Queries (Total)"); 11 | internal readonly IncrementCounter QueriesPerSecond = new IncrementCounter("queries-per-second", "efcore_queries_per_second", "Queries"); 12 | internal readonly MeanCounter TotalSaveChanges = new MeanCounter("total-save-changes", "efcore_savechanges_total", "SaveChanges (Total)"); 13 | internal readonly IncrementCounter SaveChangesPerSecond = new IncrementCounter("save-changes-per-second", "efcore_savechanges_per_second", "SaveChanges"); 14 | internal readonly MeanCounter CompiledQueryCacheHitRate = new MeanCounter("compiled-query-cache-hit-rate", "efcore_compiled_query_cache_hit_ratio", "Query Cache Hit Rate"); 15 | internal readonly MeanCounter TotalExecutionStrategyOperationFailures = new MeanCounter("total-execution-strategy-operation-failures", "efcore_execution_strategy_operation_failures_total", "Execution Strategy Operation Failures (Total)"); 16 | internal readonly IncrementCounter ExecutionStrategyOperationFailuresPerSecond = new IncrementCounter("execution-strategy-operation-failures-per-second", "efcore_execution_strategy_operation_failures_per_second", "Execution Strategy Operation Failures"); 17 | internal readonly MeanCounter TotalOptimisticConcurrencyFailures = new MeanCounter("total-optimistic-concurrency-failures", "efcore_optimistic_concurrency_failures_total", "Optimistic Concurrency Failures (Total)"); 18 | internal readonly IncrementCounter OptimisticConcurrencyFailuresPerSecond = new IncrementCounter("optimistic-concurrency-failures-per-second", "efcore_optimistic_concurrency_failures_per_second", "Optimistic Concurrency Failures"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusGrpcClientCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusGrpcClientCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "Grpc.Net.Client"; 8 | 9 | internal readonly MeanCounter TotalCalls = new MeanCounter("total-calls", "grpc_client_calls_total", "Total Calls"); 10 | internal readonly MeanCounter CurrentCalls = new MeanCounter("current-calls", "grpc_client_calls_current_total", "Current Calls"); 11 | internal readonly MeanCounter CallsFailed = new MeanCounter("calls-failed", "grpc_client_calls_failed_total", "Total Calls Failed"); 12 | internal readonly MeanCounter CallsDeadlineExceeded = new MeanCounter("calls-deadline-exceeded", "grpc_client_calls_deadline_exceeded_total", "Total Calls Deadline Exceeded"); 13 | internal readonly MeanCounter MessagesSent = new MeanCounter("messages-sent", "grpc_client_messages_sent_total", "Total Messages Sent"); 14 | internal readonly MeanCounter MessagesReceived = new MeanCounter("messages-received", "grpc_client_messages_received_total", "Total Messages Received"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusGrpcServerCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusGrpcServerCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "Grpc.AspNetCore.Server"; 8 | 9 | internal readonly MeanCounter TotalCalls = new MeanCounter("total-calls", "grpc_server_calls_total", "Total Calls"); 10 | internal readonly MeanCounter CurrentCalls = new MeanCounter("current-calls", "grpc_server_calls_current_total", "Current Calls"); 11 | internal readonly MeanCounter CallsFailed = new MeanCounter("calls-failed", "grpc_server_calls_failed_total", "Total Calls Failed"); 12 | internal readonly MeanCounter CallsDeadlineExceeded = new MeanCounter("calls-deadline-exceeded", "grpc_server_deadline_exceeded_total", "Total Calls Deadline Exceeded"); 13 | internal readonly MeanCounter MessagesSent = new MeanCounter("messages-sent", "grpc_server_messages_sent_total", "Total Messages Sent"); 14 | internal readonly MeanCounter MessagesReceived = new MeanCounter("messages-received", "grpc_server_messages_received_total", "Total Messages Received"); 15 | internal readonly MeanCounter CallsUnimplemented = new MeanCounter("calls-unimplemented", "grpc_server_calls_unimplemented_total", "Total Calls Unimplemented"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusHttpClientCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusHttpClientCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "System.Net.Http"; 8 | 9 | internal readonly MeanCounter RequestsStarted = new MeanCounter("requests-started", "http_client_requests_started_total", "Requests Started"); 10 | internal readonly IncrementCounter RequestsStartedRate = new IncrementCounter("requests-started-rate", "http_client_requests_started_per_second", "Requests Started Rate"); 11 | internal readonly MeanCounter RequestsAborted = new MeanCounter("requests-aborted", "http_client_requests_aborted_total", "Total Requests Aborted"); 12 | internal readonly IncrementCounter RequestsAbortedRate = new IncrementCounter("requests-aborted-rate", "http_client_requests_aborted_per_second", "Requests Failed Rate"); 13 | internal readonly MeanCounter CurrentRequests = new MeanCounter("current-requests", "http_client_requests_current_total", "Current Requests"); 14 | internal readonly MeanCounter Http11ConnectionsCurrentTotal = new MeanCounter("http11-connections-current-total", "http_client_http11_connections_current_total", "Current Http 1.1 Connections"); 15 | internal readonly MeanCounter Http20ConnectionsCurrentTotal = new MeanCounter("http20-connections-current-total", "http_client_http20_connections_current_total", "Current Http 2.0 Connections"); 16 | internal readonly MeanCounter Http11RequestsQueueDuration = new MeanCounter("http11-requests-queue-duration", "http_client_http11_requests_queue_duration_milliseconds", "HTTP 1.1 Requests Queue Duration"); 17 | internal readonly MeanCounter Http20RequestsQueueDuration = new MeanCounter("http20-requests-queue-duration", "http_client_http20_requests_queue_duration_milliseconds", "HTTP 2.0 Requests Queue Duration"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusKestrelCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusKestrelCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "Microsoft-AspNetCore-Server-Kestrel"; 8 | 9 | internal readonly IncrementCounter ConnectionPerSecond = new IncrementCounter("connections-per-second", "kestrel_connections_per_second", "Number of connections between update intervals"); 10 | internal readonly MeanCounter TotalConnections = new MeanCounter("total-connections", "kestrel_connections_total", "Total Requests"); 11 | internal readonly MeanCounter CurrentConnections = new MeanCounter("current-connections", "kestrel_connections_current_total", "Number of current connections"); 12 | internal readonly IncrementCounter TlsHandshakesPerSecond = new IncrementCounter("tls-handshakes-per-second", "kestrel_tls_handshakes_per_second", "Number of TLS Handshakes made between update intervals"); 13 | internal readonly MeanCounter TotalTlsHandshakes = new MeanCounter("total-tls-handshakes", "kestrel_tls_handshakes_total", "Total number of TLS handshakes made"); 14 | internal readonly MeanCounter CurrentTlsHandshakes = new MeanCounter("current-tls-handshakes", "kestrel_tls_handshakes_current_total", "Number of currently active TLS handshakes"); 15 | internal readonly MeanCounter FailedTlsHandshakes = new MeanCounter("failed-tls-handshakes","kestrel_tls_handshakes_failed_total", "Total number of failed TLS handshakes"); 16 | internal readonly MeanCounter ConnectionQueueLength = new MeanCounter("connection-queue-length", "kestrel_connections_queue_total", "Length of Kestrel Connection Queue"); 17 | internal readonly MeanCounter RequestQueueLength = new MeanCounter("request-queue-length", "kestrel_requests_queue_total", "Length total HTTP request queue"); 18 | internal readonly MeanCounter CurrentUpgradedRequests = new MeanCounter("current-upgraded-requests", "kestrel_requests_upgraded_total", "Current Upgraded Requests (WebSockets)"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusNetNameResolutionCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusNetNameResolutionCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "System.Net.NameResolution"; 8 | 9 | internal readonly MeanCounter DnsLookupsRequested = new MeanCounter("dns-lookups-requested", "net_dns_lookups_requested_total", "DNS Lookups Requested"); 10 | internal readonly MeanCounter DnsLookupsDuration = new MeanCounter("dns-lookups-duration", "net_dns_lookups_duration_milliseconds", "Average DNS Lookup Duration"); 11 | } 12 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusNetSecurityCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusNetSecurityCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "System.Net.Security"; 8 | 9 | internal readonly IncrementCounter TlsHandshakeRate = new IncrementCounter("tls-handshake-rate", "net_security_tls_handshakes_per_second", "TLS handshakes completed"); 10 | internal readonly MeanCounter TotalTlsHandshakes = new MeanCounter("total-tls-handshakes", "net_security_tls_handshakes_total", "Total TLS handshakes completed"); 11 | internal readonly MeanCounter CurrentTlsHandshakes = new MeanCounter("current-tls-handshakes", "net_security_tls_handshakes_current_total", "Current TLS handshakes"); 12 | internal readonly MeanCounter FailedTlsHandshakes = new MeanCounter("failed-tls-handshakes", "net_security_tls_handshakes_failed_total", "Total TLS handshakes failed"); 13 | internal readonly MeanCounter AllTlsSessionsOpen = new MeanCounter("all-tls-sessions-open", "net_security_tls_sessions_total", "All TLS Sessions Active"); 14 | internal readonly MeanCounter Tls10SessionsOpen = new MeanCounter("tls10-sessions-open", "net_security_tls_10_sessions_total", "TLS 1.0 Sessions Active"); 15 | internal readonly MeanCounter Tls11SessionsOpen = new MeanCounter("tls11-sessions-open", "net_security_tls_11_sessions_total", "TLS 1.1 Sessions Active"); 16 | internal readonly MeanCounter Tls12SessionsOpen = new MeanCounter("tls12-sessions-open", "net_security_tls_12_sessions_total", "TLS 1.2 Sessions Active"); 17 | internal readonly MeanCounter Tls13SessionsOpen = new MeanCounter("tls13-sessions-open", "net_security_tls_13_sessions_total", "TLS 1.3 Sessions Active"); 18 | internal readonly MeanCounter AllTlsHandshakeDuration = new MeanCounter("all-tls-handshake-duration", "net_security_handshakes_duration_milliseconds", "TLS Handshake Duration"); 19 | internal readonly MeanCounter Tls10HandshakeDuration = new MeanCounter("tls10-handshake-duration", "net_security_handshakes_tls10_duration_milliseconds", "TLS 1.0 Handshake Duration"); 20 | internal readonly MeanCounter Tls11HandshakeDuration = new MeanCounter("tls11-handshake-duration", "net_security_handshakes_tls11_duration_milliseconds", "TLS 1.1 Handshake Duration"); 21 | internal readonly MeanCounter Tls12HandshakeDuration = new MeanCounter("tls12-handshake-duration", "net_security_handshakes_tls12_duration_milliseconds", "TLS 1.2 Handshake Duration"); 22 | internal readonly MeanCounter Tls13HandshakeDuration = new MeanCounter("tls13-handshake-duration", "net_security_handshakes_tls13_duration_milliseconds", "TLS 1.3 Handshake Duration"); 23 | } 24 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusNetSocketsCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusNetSocketsCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "System.Net.Sockets"; 8 | 9 | internal readonly MeanCounter OutgoingConnectionsEstablished = new MeanCounter("outgoing-connections-established", "net_sockets_outgoing_connections_total", "Outgoing Connections Established"); 10 | internal readonly MeanCounter IncomingConnectionsEstablished = new MeanCounter("incoming-connections-established", "net_sockets_incoming_connections_total", "Incoming Connections Established"); 11 | internal readonly MeanCounter BytesReceived = new MeanCounter("bytes-received", "net_sockets_bytes_received", "Bytes Received"); 12 | internal readonly MeanCounter BytesSent = new MeanCounter("bytes-sent", "net_sockets_bytes_sent", "Bytes Sent"); 13 | internal readonly MeanCounter DatagramsReceived = new MeanCounter("datagrams-received", "net_sockets_datagrams_received", "Datagrams Received"); 14 | internal readonly MeanCounter DatagramsSent = new MeanCounter("datagrams-sent", "net_sockets_datagrams_sent", "Datagrams Sent"); 15 | } 16 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusRuntimeCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusRuntimeCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "System.Runtime"; 8 | 9 | internal MeanCounter RuntimeCpuUsage = new MeanCounter("cpu-usage", "runtime_cpu_usage_ratio", "CPU usage in percents"); 10 | internal MeanCounter RuntimeWorkingSet = new MeanCounter("working-set","runtime_memory_working_set_megabytes", "Working Set in megabytes"); 11 | internal MeanCounter RuntimeGcHeapSize = new MeanCounter("gc-heap-size", "runtime_gc_heap_size_megabytes", "GC Heap Size in megabytes"); 12 | internal IncrementCounter RuntimeGcCountGen0 = new IncrementCounter("gen-0-gc-count", "runtime_gc_gen0_count", "GC Count (Gen 0)"); 13 | internal IncrementCounter RuntimeGcCountGen1 = new IncrementCounter("gen-1-gc-count", "runtime_gc_gen1_count", "GC Count (Gen 1)"); 14 | internal IncrementCounter RuntimeGcCountGen2 = new IncrementCounter("gen-2-gc-count", "runtime_gc_gen2_count", "GC Count (Gen 2)"); 15 | internal MeanCounter RuntimeThreadPoolThreadCount = new MeanCounter("threadpool-thread-count", "runtime_threadpool_threads_total", "ThreadPool Thread Count"); 16 | internal IncrementCounter MonitorLockContentionCount = new IncrementCounter("monitor-lock-contention-count", "runtime_lock_contention_total", "Monitor Lock Contention Count"); 17 | internal MeanCounter RuntimeThreadPoolQueueLength = new MeanCounter("threadpool-queue-length", "runtime_threadpool_queue_length", "ThreadPool Queue Length"); 18 | internal IncrementCounter RuntimeThreadPoolCompletedItemsCount = new IncrementCounter("threadpool-completed-items-count","runtime_threadpool_completed_items_total", "ThreadPool Completed Work Item Count"); 19 | internal IncrementCounter RuntimeAllocRate = new IncrementCounter("alloc-rate", "runtime_allocation_rate_bytes", "Allocation Rate in bytes"); 20 | internal MeanCounter RuntimeActiveTimerCount = new MeanCounter("active-timer-count", "runtime_active_timers_total", "Number of Active Timers"); 21 | internal MeanCounter RuntimeGcFragmentation = new MeanCounter("gc-fragmentation", "runtime_gc_fragmentation_ratio", "GC Fragmentation"); 22 | internal IncrementCounter RuntimeExceptionCount = new IncrementCounter("exception-count","runtime_exceptions_total", "Exception Count"); 23 | internal MeanCounter RuntimeTimeInGc = new MeanCounter("time-in-gc", "runtime_time_in_gc_ratio", "% Time in GC since last GC"); 24 | internal MeanCounter RuntimeGcSizeGen0 = new MeanCounter("gen-0-size", "runtime_gc_size_gen0_bytes", "GC size in bytes (Gen 0)"); 25 | internal MeanCounter RuntimeGcSizeGen1 = new MeanCounter("gen-1-size", "runtime_gc_size_gen1_bytes", "GC size in bytes (Gen 1)"); 26 | internal MeanCounter RuntimeGcSizeGen2 = new MeanCounter("gen-2-size", "runtime_gc_size_gen2_bytes", "GC size in bytes (Gen 2)"); 27 | internal MeanCounter RuntimeGcSizeLoh = new MeanCounter("loh-size", "runtime_gc_size_loh_bytes", "GC size in bytes (LOH)"); 28 | internal MeanCounter RuntimeGcSizePoh = new MeanCounter("poh-size", "runtime_gc_size_poh_bytes", "GC size in bytes (POH)"); 29 | internal MeanCounter RuntimeAssemblyCount = new MeanCounter("assembly-count", "runtime_assemblies_total", "Number of Assemblies Loaded"); 30 | internal MeanCounter RuntimeIlBytesJitted = new MeanCounter("il-bytes-jitted", "runtime_il_jitted_bytes", "IL Bytes Jitted"); 31 | internal MeanCounter RuntimeMethodsJittedCount = new MeanCounter("methods-jitted-count", "runtime_methods_jitted_total", "Number of Methods Jitted"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusSignalRCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusSignalRCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "Microsoft.AspNetCore.Http.Connections"; 8 | 9 | internal readonly MeanCounter ConnectionsStarted = new MeanCounter("connections-started", "signalr_connections_started_total", "Total Connections Started"); 10 | internal readonly MeanCounter ConnectionsStopped = new MeanCounter("connections-stopped", "aspnetcore_requests_current_total", "Current Requests"); 11 | internal readonly MeanCounter ConnectionsTimedOut = new MeanCounter("connections-timed-out", "signalr_connections_timed_out_total", "Total Connections Timed Out"); 12 | internal readonly MeanCounter CurrentConnections = new MeanCounter("current-connections", "signalr_connections_current_total", "Current Connections"); 13 | internal readonly MeanCounter ConnectionsDuration = new MeanCounter("connections-duration", "signalr_connections_duration_milliseconds", "Average Connection Duration"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Adapters/PrometheusSqlClientCounterAdapter.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.EventListeners.Counters; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Adapters 4 | { 5 | internal class PrometheusSqlClientCounterAdapter : BaseAdapter 6 | { 7 | public const string EventSourceName = "Microsoft.Data.SqlClient.EventSource"; 8 | 9 | internal readonly MeanCounter ActiveHardConnections = new MeanCounter("active-hard-connections", "sqlclient_active_hard_connections_total", "Actual active connections are made to servers"); 10 | internal readonly IncrementCounter HardConnects = new IncrementCounter("hard-connects", "sqlclient_hard_connects_per_second", "Actual connections are made to servers"); 11 | internal readonly IncrementCounter HardDisconnects = new IncrementCounter("hard-disconnects", "sqlclient_hard_disconnects_per_second", "Actual disconnections are made to servers"); 12 | internal readonly MeanCounter ActiveSoftConnections = new MeanCounter("active-soft-connects", "sqlclient_active_soft_connections_total", "Active connections got from connection pool"); 13 | internal readonly IncrementCounter SoftConnects = new IncrementCounter("soft-connects", "sqlclient_soft_connects_per_second", "Connections got from connection pool"); 14 | internal readonly IncrementCounter SoftDisconnects = new IncrementCounter("soft-disconnects", "sqlclient_soft_disconnects_per_second", "Connections returned to the connection pool"); 15 | internal readonly MeanCounter NumberOfNonPooledConnections = new MeanCounter("number-of-non-pooled-connections", "sqlclient_non_pooled_connections_total", "Number of connections are not using connection pooling"); 16 | internal readonly MeanCounter NumberOfPooledConnections = new MeanCounter("number-of-pooled-connections", "sqlclient_pooled_connections_total", "Number of connections are managed by connection pooler"); 17 | internal readonly MeanCounter NumberOfActiveConnectionPoolGroups = new MeanCounter("number-of-active-connection-pool-groups", "sqlclient_active_connection_pool_groups_total", "Number of active unique connection strings"); 18 | internal readonly MeanCounter NumberOfInactiveConnectionPoolGroups = new MeanCounter("number-of-inactive-connection-pool-groups", "sqlclient_inactive_connection_pool_groups_total", "Number of unique connection strings waiting for pruning"); 19 | internal readonly MeanCounter NumberOfActiveConnectionPools = new MeanCounter("number-of-active-connection-pools", "sqlclient_active_connection_pools_total", "Number of active connection pools"); 20 | internal readonly MeanCounter NumberOfInactiveConnectionPools = new MeanCounter("number-of-inactive-connection-pools", "sqlclient_inactive_connection_pools_total", "Number of inactive connection pools"); 21 | internal readonly MeanCounter NumberOfActiveConnections = new MeanCounter("number-of-active-connections", "sqlclient_active_connections_total", "Number of active connections"); 22 | internal readonly MeanCounter NumberOfFreeConnections = new MeanCounter("number-of-free-connections", "sqlclient_free_connections_total", "Number of free-ready connections"); 23 | internal readonly MeanCounter NumberOfStasisConnections = new MeanCounter("number-of-stasis-connections", "sqlclient_stasis_connections_total", "Number of connections currently waiting to be ready"); 24 | internal readonly IncrementCounter NumberOfReclaimedConnections = new IncrementCounter("number-of-reclaimed-connections", "sqlclient_reclaimed_connections_total", "Number of reclaimed connections from GC"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Counters/BaseCounter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Counters 4 | { 5 | internal abstract class BaseCounter 6 | { 7 | protected BaseCounter(string name, string displayName, string description) 8 | { 9 | Name = name; 10 | DisplayName = displayName; 11 | Description = description; 12 | } 13 | 14 | public string Name { get; } 15 | protected string DisplayName { get; } 16 | protected string Description { get; } 17 | 18 | public abstract bool TryReadEventCounterData(IDictionary eventData); 19 | } 20 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Counters/CounterUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace Prometheus.Contrib.EventListeners.Counters 6 | { 7 | public class CounterUtils 8 | { 9 | internal static Dictionary GenerateDictionary(TFrom owningType) 10 | { 11 | return owningType.GetType() 12 | .GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic) 13 | .Where(x => x.FieldType.BaseType == typeof(BaseCounter)) 14 | .Select(x => x.GetValue(owningType) as BaseCounter) 15 | .ToDictionary(k => k.Name, k => k); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Counters/IncrementCounter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Counters 4 | { 5 | internal class IncrementCounter : BaseCounter 6 | { 7 | public IncrementCounter(string name, string displayName, string description) : base(name, displayName, description) 8 | { 9 | Metric = Metrics.CreateGauge(DisplayName, Description); 10 | } 11 | 12 | internal Gauge Metric { get; } 13 | 14 | public override bool TryReadEventCounterData(IDictionary eventData) 15 | { 16 | if (!eventData.TryGetValue("Increment", out var increment)) 17 | return false; 18 | 19 | Metric.Set((double)increment); 20 | return true; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/Counters/MeanCounter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Prometheus.Contrib.EventListeners.Counters 4 | { 5 | internal class MeanCounter : BaseCounter 6 | { 7 | public MeanCounter(string name, string displayName, string description) : base(name, displayName, description) 8 | { 9 | Metric = Metrics.CreateGauge(DisplayName, Description); 10 | } 11 | 12 | internal Gauge Metric { get; } 13 | 14 | public override bool TryReadEventCounterData(IDictionary eventData) 15 | { 16 | if (!(eventData.TryGetValue("Mean", out var meanObj) && meanObj is double mean) 17 | || !(eventData.TryGetValue("Count", out var countObj) && countObj is int count)) 18 | { 19 | return false; 20 | } 21 | 22 | var val = mean == 0 && count > 0 ? count : mean; 23 | val = double.IsNaN(val) ? 0 : val; 24 | 25 | Metric.Set(val); 26 | 27 | return true; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/EventListeners/CountersEventListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Prometheus.Contrib.EventListeners.Adapters; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.Tracing; 5 | using Prometheus.Contrib.Core; 6 | 7 | namespace Prometheus.Contrib.EventListeners 8 | { 9 | internal class CountersEventListener : EventListener 10 | { 11 | private readonly int refreshPeriodSeconds; 12 | 13 | private readonly Dictionary counterAdapters = new Dictionary 14 | { 15 | [PrometheusRuntimeCounterAdapter.EventSourceName] = new PrometheusRuntimeCounterAdapter(), 16 | [PrometheusAspNetCoreCounterAdapter.EventSourceName] = new PrometheusAspNetCoreCounterAdapter(), 17 | [PrometheusSignalRCounterAdapter.EventSourceName] = new PrometheusSignalRCounterAdapter(), 18 | [PrometheusGrpcServerCounterAdapter.EventSourceName] = new PrometheusGrpcServerCounterAdapter(), 19 | [PrometheusGrpcClientCounterAdapter.EventSourceName] = new PrometheusGrpcClientCounterAdapter(), 20 | [PrometheusEfCoreCounterAdapter.EventSourceName] = new PrometheusEfCoreCounterAdapter(), 21 | [PrometheusKestrelCounterAdapter.EventSourceName] = new PrometheusKestrelCounterAdapter(), 22 | [PrometheusHttpClientCounterAdapter.EventSourceName] = new PrometheusHttpClientCounterAdapter(), 23 | [PrometheusNetSecurityCounterAdapter.EventSourceName] = new PrometheusNetSecurityCounterAdapter(), 24 | [PrometheusNetNameResolutionCounterAdapter.EventSourceName] = new PrometheusNetNameResolutionCounterAdapter(), 25 | [PrometheusNetSocketsCounterAdapter.EventSourceName] = new PrometheusNetSocketsCounterAdapter(), 26 | [PrometheusSqlClientCounterAdapter.EventSourceName] = new PrometheusSqlClientCounterAdapter() 27 | }; 28 | 29 | internal CountersEventListener(int refreshPeriodSeconds = 10) 30 | { 31 | this.refreshPeriodSeconds = refreshPeriodSeconds; 32 | 33 | EventSourceCreated += OnEventSourceCreated; 34 | } 35 | 36 | private void OnEventSourceCreated(object sender, EventSourceCreatedEventArgs e) 37 | { 38 | if (e.EventSource == null || !counterAdapters.ContainsKey(e.EventSource.Name)) 39 | { 40 | return; 41 | } 42 | 43 | var args = new Dictionary 44 | { 45 | ["EventCounterIntervalSec"] = refreshPeriodSeconds.ToString() 46 | }; 47 | 48 | EnableEvents(e.EventSource, EventLevel.Verbose, EventKeywords.All, args); 49 | } 50 | 51 | protected override void OnEventWritten(EventWrittenEventArgs eventData) 52 | { 53 | if (eventData.EventName is null || !eventData.EventName.Equals("EventCounters") || eventData.Payload == null) 54 | { 55 | return; 56 | } 57 | 58 | if (!counterAdapters.ContainsKey(eventData.EventSource.Name)) 59 | { 60 | return; 61 | } 62 | 63 | foreach (var payload in eventData.Payload) 64 | { 65 | if (payload is IDictionary eventPayload && counterAdapters.TryGetValue(eventData.EventSource.Name, out var adapter)) 66 | { 67 | adapter.OnCounterEvent(eventPayload); 68 | } 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Healthchecks/PrometheusHealthcheckPublisher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Diagnostics.HealthChecks; 5 | 6 | namespace Prometheus.Contrib.Healthchecks 7 | { 8 | public class PrometheusHealthcheckPublisher : IHealthCheckPublisher 9 | { 10 | private static readonly Gauge Healthcheck = Metrics.CreateGauge( 11 | "health_check", 12 | "Prometheus healthcheck.", 13 | new GaugeConfiguration { LabelNames = new[] { "serviceName" } }); 14 | 15 | public Task PublishAsync(HealthReport report, CancellationToken cancellationToken) 16 | { 17 | foreach (var entry in report.Entries) 18 | { 19 | Healthcheck.WithLabels(entry.Key).Set(Convert.ToDouble(entry.Value.Status == HealthStatus.Healthy)); 20 | } 21 | 22 | return Task.CompletedTask; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/Options/SqlMetricsOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Prometheus.Contrib.Options 6 | { 7 | public class SqlMetricsOptions 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/prometheus-net.Contrib/prometheus-net.Contrib.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | Prometheus.Contrib 6 | prometheus-net.Contrib 7 | prometheus-net.Contrib 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/prometheus-net.EasyCaching/DiagnosticServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.Core; 2 | using Prometheus.EasyCaching.Diagnostics; 3 | 4 | namespace Microsoft.Extensions.DependencyInjection 5 | { 6 | public static class DiagnosticServiceCollectionExtensions 7 | { 8 | public static void AddPrometheusEasyCachingMetrics(this IServiceCollection services) 9 | { 10 | var easyCachingListenerHandler = new DiagnosticSourceSubscriber( 11 | name => new EasyCachingListenerHandler(name), 12 | listener => listener.Name.Equals("EasyCachingDiagnosticListener")); 13 | easyCachingListenerHandler.Subscribe(); 14 | 15 | services.AddSingleton(easyCachingListenerHandler); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/prometheus-net.EasyCaching/Diagnostics/EasyCachingListenerHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | using Prometheus.Contrib.Core; 5 | 6 | namespace Prometheus.EasyCaching.Diagnostics 7 | { 8 | public class EasyCachingListenerHandler : DiagnosticListenerHandler 9 | { 10 | private static class PrometheusCounters 11 | { 12 | public static readonly Histogram EasyCachingOperationDuration = Metrics.CreateHistogram( 13 | "easycaching_operation_duration_seconds", 14 | "The duration of EasyCaching operation.", 15 | new HistogramConfiguration() 16 | { 17 | LabelNames = new[] { "operation" } 18 | }); 19 | } 20 | 21 | private readonly PropertyFetcher setBeforeOperationFetcher = new PropertyFetcher("Timestamp"); 22 | private readonly PropertyFetcher setAfterOperationFetcher = new PropertyFetcher("Timestamp"); 23 | 24 | private readonly PropertyFetcher getBeforeOperationFetcher = new PropertyFetcher("Timestamp"); 25 | private readonly PropertyFetcher getAfterOperationFetcher = new PropertyFetcher("Timestamp"); 26 | 27 | private readonly PropertyFetcher removeBeforeOperationFetcher = new PropertyFetcher("Timestamp"); 28 | private readonly PropertyFetcher removeAfterOperationFetcher = new PropertyFetcher("Timestamp"); 29 | 30 | private readonly AsyncLocal writeRemoveLocal = new AsyncLocal(); 31 | private readonly AsyncLocal writeGetLocal = new AsyncLocal(); 32 | private readonly AsyncLocal writeSetLocal = new AsyncLocal(); 33 | 34 | public EasyCachingListenerHandler(string sourceName) : base(sourceName) 35 | { 36 | } 37 | 38 | public override void OnCustom(string name, Activity activity, object payload) 39 | { 40 | switch (name) 41 | { 42 | case "EasyCaching.WriteSetCacheBefore": 43 | { 44 | if (setBeforeOperationFetcher.TryFetch(payload, out var timestampBefore)) 45 | { 46 | this.writeSetLocal.Value = timestampBefore; 47 | } 48 | } 49 | break; 50 | case "EasyCaching.WriteSetCacheAfter": 51 | { 52 | var timestampBefore = this.writeSetLocal.Value; 53 | 54 | if (setAfterOperationFetcher.TryFetch(payload, out var timestampAfter)) 55 | { 56 | var elapsed = TimeSpan.FromTicks(timestampAfter - timestampBefore).TotalSeconds; 57 | 58 | if (elapsed == 0) 59 | { 60 | return; 61 | } 62 | 63 | PrometheusCounters.EasyCachingOperationDuration.WithLabels("set").Observe(elapsed); 64 | } 65 | } 66 | break; 67 | case "EasyCaching.WriteGetCacheBefore": 68 | { 69 | if (getBeforeOperationFetcher.TryFetch(payload, out var timestampBefore)) 70 | { 71 | this.writeGetLocal.Value = timestampBefore; 72 | } 73 | } 74 | break; 75 | case "EasyCaching.WriteGetCacheAfter": 76 | { 77 | var timestampBefore = this.writeGetLocal.Value; 78 | 79 | if (getAfterOperationFetcher.TryFetch(payload, out var timeStampAfter)) 80 | { 81 | var elapsed = TimeSpan.FromTicks(timeStampAfter - timestampBefore).TotalSeconds; 82 | 83 | if (elapsed == 0) 84 | { 85 | return; 86 | } 87 | 88 | PrometheusCounters.EasyCachingOperationDuration.WithLabels("get").Observe(elapsed); 89 | } 90 | } 91 | break; 92 | case "EasyCaching.WriteRemoveCacheBefore": 93 | { 94 | if (removeBeforeOperationFetcher.TryFetch(payload, out var timestampBefore)) 95 | { 96 | this.writeRemoveLocal.Value = timestampBefore; 97 | } 98 | } 99 | break; 100 | case "EasyCaching.WriteRemoveCacheAfter": 101 | { 102 | var timestampBefore = this.writeRemoveLocal.Value; 103 | 104 | if (removeAfterOperationFetcher.TryFetch(payload, out var timeStampAfter)) 105 | { 106 | var elapsed = TimeSpan.FromTicks(timeStampAfter - timestampBefore).TotalSeconds; 107 | 108 | if (elapsed == 0) 109 | { 110 | return; 111 | } 112 | 113 | PrometheusCounters.EasyCachingOperationDuration.WithLabels("remove").Observe(elapsed); 114 | } 115 | } 116 | break; 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/prometheus-net.EasyCaching/prometheus-net.EasyCaching.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | prometheus-net.EasyCaching 6 | Prometheus.EasyCaching 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/prometheus-net.EntityFramework/DiagnosticServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.Core; 2 | using Prometheus.EntityFramework.Diagnostics; 3 | 4 | namespace Microsoft.Extensions.DependencyInjection 5 | { 6 | public static class DiagnosticServiceCollectionExtensions 7 | { 8 | public static void AddPrometheusEntityFrameworkMetrics(this IServiceCollection services) 9 | { 10 | var entityFrameworkListenerHandler = new DiagnosticSourceSubscriber( 11 | name => new EntityFrameworkListenerHandler(name), 12 | listener => listener.Name.Equals("Microsoft.EntityFrameworkCore")); 13 | entityFrameworkListenerHandler.Subscribe(); 14 | 15 | services.AddSingleton(entityFrameworkListenerHandler); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/prometheus-net.EntityFramework/Diagnostics/EntityFrameworkListenerHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using Microsoft.EntityFrameworkCore.Diagnostics; 4 | using Prometheus.Contrib.Core; 5 | 6 | namespace Prometheus.EntityFramework.Diagnostics 7 | { 8 | public class EntityFrameworkListenerHandler : DiagnosticListenerHandler 9 | { 10 | private static class PrometheusCounters 11 | { 12 | public static readonly Histogram EfCoreCommandsDuration = Metrics.CreateHistogram( 13 | "efcore_command_duration_seconds", 14 | "The duration of DB requests processed by an application.", 15 | new HistogramConfiguration { Buckets = Histogram.ExponentialBuckets(0.001, 2, 16) }); 16 | public static readonly Counter EfCoreCommandsErrors = Metrics.CreateCounter( 17 | "efcore_command_errors_total", 18 | "Total DB requests errors", 19 | new CounterConfiguration { LabelNames = new[] { "type" } }); 20 | 21 | public static readonly Counter EfCoreConnectionsOpenTotal = Metrics.CreateCounter("efcore_connection_opened_total", "Total opened DB connections"); 22 | public static readonly Counter EfCoreConnectionsCloseTotal = Metrics.CreateCounter("efcore_connection_closed_total", "Total closed DB connections"); 23 | public static readonly Counter EfCoreConnectionsErrors = Metrics.CreateCounter("efcore_connection_errors_total", "Total DB connections errors"); 24 | 25 | public static readonly Counter EfCoreTransactionsStarted = Metrics.CreateCounter("efcore_transaction_started_total", "Total started transactions."); 26 | public static readonly Counter EfCoreTransactionsCommitedCount = Metrics.CreateCounter("efcore_transaction_committed_total", "Total committed transactions."); 27 | public static readonly Counter EfCoreTransactionsRollbackCount = Metrics.CreateCounter("efcore_transaction_rollback_total", "Total rollback transactions."); 28 | 29 | public static readonly Counter EfCoreDbContextCreatedCount = Metrics.CreateCounter("efcore_dbcontext_created_total", "Total created DBContexts"); 30 | 31 | public static readonly Counter EfCoreQueryWarningsCount = Metrics.CreateCounter( 32 | "efcore_query_warnings_total", 33 | "Total query warnings", 34 | new CounterConfiguration { LabelNames = new[] { "type" } }); 35 | } 36 | 37 | public EntityFrameworkListenerHandler(string sourceName) : base(sourceName) 38 | { 39 | } 40 | 41 | public override void OnCustom(string name, Activity activity, object payload) 42 | { 43 | switch (name) 44 | { 45 | case "Microsoft.EntityFrameworkCore.Infrastructure.ContextInitialized": 46 | PrometheusCounters.EfCoreDbContextCreatedCount.Inc(); 47 | break; 48 | case "Microsoft.EntityFrameworkCore.Infrastructure.ContextDisposed": 49 | break; 50 | 51 | case "Microsoft.EntityFrameworkCore.Database.Connection.ConnectionOpening": 52 | break; 53 | case "Microsoft.EntityFrameworkCore.Database.Connection.ConnectionOpened": 54 | PrometheusCounters.EfCoreConnectionsOpenTotal.Inc(); 55 | break; 56 | case "Microsoft.EntityFrameworkCore.Database.Connection.ConnectionClosing": 57 | break; 58 | case "Microsoft.EntityFrameworkCore.Database.Connection.ConnectionClosed": 59 | PrometheusCounters.EfCoreConnectionsCloseTotal.Inc(); 60 | break; 61 | case "Microsoft.EntityFrameworkCore.Database.Connection.ConnectionError": 62 | PrometheusCounters.EfCoreConnectionsErrors.Inc(); 63 | break; 64 | 65 | case "Microsoft.EntityFrameworkCore.Database.Command.CommandCreating": 66 | break; 67 | case "Microsoft.EntityFrameworkCore.Database.Command.CommandCreated": 68 | break; 69 | case "Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting": 70 | break; 71 | case "Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted": 72 | { 73 | if (payload is CommandExecutedEventData commandExecuted) 74 | PrometheusCounters.EfCoreCommandsDuration.Observe(commandExecuted.Duration.TotalSeconds); 75 | } 76 | break; 77 | case "Microsoft.EntityFrameworkCore.Database.Command.CommandError": 78 | PrometheusCounters.EfCoreCommandsErrors.WithLabels("command").Inc(); 79 | break; 80 | 81 | case "Microsoft.EntityFrameworkCore.Database.Transaction.TransactionStarting": 82 | break; 83 | case "Microsoft.EntityFrameworkCore.Database.Transaction.TransactionStarted": 84 | break; 85 | case "Microsoft.EntityFrameworkCore.Database.Transaction.TransactionCommitting": 86 | break; 87 | case "Microsoft.EntityFrameworkCore.Database.Transaction.TransactionCommitted": 88 | if (payload is TransactionEndEventData _) 89 | PrometheusCounters.EfCoreTransactionsCommitedCount.Inc(); 90 | break; 91 | case "Microsoft.EntityFrameworkCore.Database.Transaction.TransactionRollingBack": 92 | break; 93 | case "Microsoft.EntityFrameworkCore.Database.Transaction.TransactionRolledBack": 94 | if (payload is TransactionEndEventData _) 95 | PrometheusCounters.EfCoreTransactionsRollbackCount.Inc(); 96 | break; 97 | case "Microsoft.EntityFrameworkCore.Database.Transaction.TransactionError": 98 | if (payload is TransactionErrorEventData _) 99 | PrometheusCounters.EfCoreCommandsErrors.WithLabels("transaction").Inc(); 100 | break; 101 | case "Microsoft.EntityFrameworkCore.Database.Transaction.TransactionUsed": 102 | break; 103 | case "Microsoft.EntityFrameworkCore.Database.Transaction.TransactionDisposed": 104 | break; 105 | case "Microsoft.EntityFrameworkCore.Database.Transaction.AmbientTransactionWarning": 106 | break; 107 | case "Microsoft.EntityFrameworkCore.Database.Transaction.AmbientTransactionEnlisted": 108 | break; 109 | case "Microsoft.EntityFrameworkCore.Database.Transaction.ExplicitTransactionEnlisted": 110 | break; 111 | 112 | case "Microsoft.EntityFrameworkCore.Query.QueryPossibleUnintendedUseOfEqualsWarning": 113 | PrometheusCounters.EfCoreQueryWarningsCount.WithLabels("QueryPossibleUnintendedUseOfEqualsWarning").Inc(); 114 | break; 115 | case "Microsoft.EntityFrameworkCore.Query.QueryPossibleExceptionWithAggregateOperatorWarning": 116 | PrometheusCounters.EfCoreQueryWarningsCount.WithLabels("QueryPossibleExceptionWithAggregateOperatorWarning").Inc(); 117 | break; 118 | case "Microsoft.EntityFrameworkCore.Query.ModelValidationKeyDefaultValueWarning": 119 | PrometheusCounters.EfCoreQueryWarningsCount.WithLabels("ModelValidationKeyDefaultValueWarning").Inc(); 120 | break; 121 | case "Microsoft.EntityFrameworkCore.Query.BoolWithDefaultWarning": 122 | PrometheusCounters.EfCoreQueryWarningsCount.WithLabels("BoolWithDefaultWarning").Inc(); 123 | break; 124 | case "Microsoft.EntityFrameworkCore.Query.QueryExecutionPlanned": 125 | break; 126 | 127 | case "Microsoft.EntityFrameworkCore.Update.BatchReadyForExecution": 128 | break; 129 | case "Microsoft.EntityFrameworkCore.Update.BatchSmallerThanMinBatchSize": 130 | break; 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/prometheus-net.EntityFramework/prometheus-net.EntityFramework.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | prometheus-net.EntityFramework 6 | Prometheus.EntityFramework 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/prometheus-net.Esquio/DiagnosticServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Prometheus.Contrib.Core; 2 | using Prometheus.Esquio.Diagnostics; 3 | 4 | namespace Microsoft.Extensions.DependencyInjection 5 | { 6 | public static class DiagnosticServiceCollectionExtensions 7 | { 8 | public static void AddPrometheusMassTransitMetrics(this IServiceCollection services) 9 | { 10 | var esquioListenerHandler = new DiagnosticSourceSubscriber( 11 | name => new EsquioListenerHandler(name), 12 | listener => listener.Name.Equals("Esquio")); 13 | esquioListenerHandler.Subscribe(); 14 | 15 | services.AddSingleton(esquioListenerHandler); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/prometheus-net.Esquio/Diagnostics/EsquioListenerHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Esquio; 3 | using Esquio.Diagnostics; 4 | using Prometheus.Contrib.Core; 5 | 6 | namespace Prometheus.Esquio.Diagnostics 7 | { 8 | public class EsquioListenerHandler : DiagnosticListenerHandler 9 | { 10 | private static class PrometheusCounters 11 | { 12 | public static readonly Counter FeatureEvaluation = Metrics.CreateCounter( 13 | "esquio_feature_evaluation_total", 14 | "Feature evaluation count", 15 | new CounterConfiguration { LabelNames = new[] { "product", "feature", "enabled" } }); 16 | 17 | public static readonly Counter FeatureEvaluationThrows = Metrics.CreateCounter( 18 | "esquio_feature_evaluation_throws_total", 19 | "Feature evaluation throws count", 20 | new CounterConfiguration { LabelNames = new[] { "product", "feature" } }); 21 | 22 | public static readonly Counter FeatureEvaluationNotFound = Metrics.CreateCounter( 23 | "esquio_feature_evaluation_notfound_total", 24 | "Feature evaluation not found count", 25 | new CounterConfiguration { LabelNames = new[] { "product", "feature" } }); 26 | 27 | public static readonly Counter ToggleEvaluation = Metrics.CreateCounter( 28 | "esquio_toggle_evaluation_total", 29 | "Toggle evaluation count", 30 | new CounterConfiguration { LabelNames = new[] { "product", "feature", "type" } }); 31 | } 32 | 33 | public EsquioListenerHandler(string sourceName) : base(sourceName) 34 | { 35 | } 36 | 37 | public override void OnCustom(string name, Activity activity, object payload) 38 | { 39 | switch (activity.OperationName) 40 | { 41 | case EsquioConstants.ESQUIO_ENDFEATURE_ACTIVITY_NAME when payload is FeatureEvaluatedEventData featureEvaluated: 42 | PrometheusCounters.FeatureEvaluation 43 | .WithLabels(featureEvaluated.Product, featureEvaluated.Feature, featureEvaluated.Enabled.ToString()) 44 | .Inc(); 45 | break; 46 | case EsquioConstants.ESQUIO_THROWFEATURE_ACTIVITY_NAME when payload is FeatureThrowEventData featureThrow: 47 | PrometheusCounters.FeatureEvaluationThrows 48 | .WithLabels(featureThrow.Product, featureThrow.Feature) 49 | .Inc(); 50 | break; 51 | case EsquioConstants.ESQUIO_NOTFOUNDFEATURE_ACTIVITY_NAME when payload is FeatureNotFoundEventData featureNotFound: 52 | PrometheusCounters.FeatureEvaluationNotFound 53 | .WithLabels(featureNotFound.Product, featureNotFound.Feature) 54 | .Inc(); 55 | break; 56 | case EsquioConstants.ESQUIO_ENDTOGGLE_ACTIVITY_NAME when payload is ToggleEvaluatedEventData toggleEvaluated: 57 | PrometheusCounters.ToggleEvaluation 58 | .WithLabels(toggleEvaluated.Product, toggleEvaluated.Feature, toggleEvaluated.ToggleType) 59 | .Inc(); 60 | break; 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/prometheus-net.Esquio/prometheus-net.Esquio.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | prometheus-net.Esquio 6 | Prometheus.Esquio 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/prometheus-net.IdentityServer/DiagnosticServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using IdentityServer4.Services; 2 | using Prometheus.IdentityServer.Sinks; 3 | 4 | namespace Microsoft.Extensions.DependencyInjection 5 | { 6 | public static class DiagnosticServiceCollectionExtensions 7 | { 8 | public static void AddPrometheusIdentityServerMetrics(this IServiceCollection services) 9 | { 10 | services.AddSingleton(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/prometheus-net.IdentityServer/Sinks/PrometheusEventsSink.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using IdentityServer4.Events; 3 | using IdentityServer4.Services; 4 | 5 | namespace Prometheus.IdentityServer.Sinks 6 | { 7 | public class PrometheusEventsSink : IEventSink 8 | { 9 | private static readonly Counter ApiAuthenticationSuccessCount = Metrics.CreateCounter( 10 | "idsrv_api_authentication_success_total", 11 | "Gets raised for successful API authentication at the introspection endpoint.", 12 | new CounterConfiguration { LabelNames = new[] { "apiName" } }); 13 | 14 | private static readonly Counter ApiAuthenticationFailureCount = Metrics.CreateCounter( 15 | "idsrv_api_authentication_failure_total", 16 | "Gets raised for failed API authentication at the introspection endpoint.", 17 | new CounterConfiguration { LabelNames = new[] { "apiName" } }); 18 | 19 | private static readonly Counter ClientAuthenticationSuccessCount = Metrics.CreateCounter( 20 | "idsrv_client_authentication_success_total", 21 | "Gets raised for successful client authentication at the token endpoint.", 22 | new CounterConfiguration { LabelNames = new[] { "client" } }); 23 | 24 | private static readonly Counter ClientAuthenticationFailureCount = Metrics.CreateCounter( 25 | "idsrv_client_authentication_failure_total", 26 | "Gets raised for failed client authentication at the token endpoint.", 27 | new CounterConfiguration { LabelNames = new[] { "client" } }); 28 | 29 | private static readonly Counter TokenIssuedSuccessCount = Metrics.CreateCounter( 30 | "idsrv_token_issued_success_total", 31 | "Gets raised for successful attempts to request access tokens.", 32 | new CounterConfiguration { LabelNames = new[] { "flow" } }); 33 | 34 | private static readonly Counter TokenIssuedFailureCount = Metrics.CreateCounter( 35 | "idsrv_token_issued_failure_total", 36 | "Gets raised for failed attempts to request access tokens.", 37 | new CounterConfiguration { LabelNames = new[] { "flow", "error" } }); 38 | 39 | private static readonly Counter TokenIntrospectionSuccessCount = Metrics.CreateCounter( 40 | "idsrv_token_introspection_success_total", 41 | "Gets raised for successful attempts to request identity tokens, access tokens, refresh tokens and authorization codes."); 42 | 43 | private static readonly Counter TokenIntrospectionFailureCount = Metrics.CreateCounter( 44 | "idsrv_token_introspection_failure_total", 45 | "Gets raised for failed attempts to request identity tokens, access tokens, refresh tokens and authorization codes."); 46 | 47 | private static readonly Counter TokenRevokedSuccessCount = Metrics.CreateCounter( 48 | "idsrv_token_revoked_success_total", 49 | "Gets raised for successful token revocation requests."); 50 | 51 | private static readonly Counter UserLoginSuccessCount = Metrics.CreateCounter( 52 | "idsrv_user_login_success_total", 53 | "Gets raised by the UI for successful user logins.", 54 | new CounterConfiguration { LabelNames = new[] { "client" } }); 55 | 56 | private static readonly Counter UserLoginFailureCount = Metrics.CreateCounter( 57 | "idsrv_user_login_failure_total", 58 | "Gets raised by the UI for failed user logins.", 59 | new CounterConfiguration { LabelNames = new[] { "client" } }); 60 | 61 | private static readonly Counter UserLogoutSuccessCount = Metrics.CreateCounter( 62 | "idsrv_user_logout_success_total", 63 | "Gets raised for successful logout requests."); 64 | 65 | private static readonly Counter ConsentGrantedCount = Metrics.CreateCounter( 66 | "idsrv_consent_granted_total", 67 | "Gets raised in the consent UI."); 68 | 69 | private static readonly Counter ConsentDeniedCount = Metrics.CreateCounter( 70 | "idsrv_consent_denied_total", 71 | "Gets raised in the consent UI."); 72 | 73 | private static readonly Counter UnhandledExceptionCount = Metrics.CreateCounter( 74 | "idsrv_unhandled_exceptions_total", 75 | "Gets raised for unhandled exceptions."); 76 | 77 | private static readonly Counter DeviceAuthorizationSuccessCount = Metrics.CreateCounter( 78 | "idsrv_device_authorization_success_total", 79 | "Gets raised for successful device authorization requests."); 80 | 81 | private static readonly Counter DeviceAuthorizationFailureCount = Metrics.CreateCounter( 82 | "idsrv_device_authorization_success_total", 83 | "Gets raised for failed device authorization requests."); 84 | 85 | public Task PersistAsync(Event evt) 86 | { 87 | switch (evt) 88 | { 89 | case ApiAuthenticationSuccessEvent authenticationSuccessEvent: 90 | ApiAuthenticationSuccessCount.WithLabels(authenticationSuccessEvent.ApiName).Inc(); 91 | break; 92 | case ApiAuthenticationFailureEvent apiAuthenticationFailureEvent: 93 | ApiAuthenticationFailureCount.WithLabels(apiAuthenticationFailureEvent.ApiName).Inc(); 94 | break; 95 | 96 | case ClientAuthenticationSuccessEvent clientAuthSuccess: 97 | ClientAuthenticationSuccessCount.WithLabels(clientAuthSuccess.ClientId).Inc(); 98 | break; 99 | case ClientAuthenticationFailureEvent clientAuthFailure: 100 | ClientAuthenticationFailureCount.WithLabels(clientAuthFailure.ClientId).Inc(); 101 | break; 102 | case TokenIssuedSuccessEvent tokenIssuedSuccess: 103 | TokenIssuedSuccessCount.WithLabels(tokenIssuedSuccess.GrantType).Inc(); 104 | break; 105 | case TokenIssuedFailureEvent tokenIssuedFailure: 106 | TokenIssuedFailureCount.WithLabels(tokenIssuedFailure.GrantType, tokenIssuedFailure.Error).Inc(); 107 | break; 108 | case TokenIntrospectionSuccessEvent _: 109 | TokenIntrospectionSuccessCount.Inc(); 110 | break; 111 | case TokenIntrospectionFailureEvent _: 112 | TokenIntrospectionFailureCount.Inc(); 113 | break; 114 | case TokenRevokedSuccessEvent _: 115 | TokenRevokedSuccessCount.Inc(); 116 | break; 117 | case UserLoginSuccessEvent userLoginSuccess: 118 | UserLoginSuccessCount.WithLabels(userLoginSuccess.ClientId).Inc(); 119 | break; 120 | case UserLoginFailureEvent userLoginFailure: 121 | UserLoginFailureCount.WithLabels(userLoginFailure.ClientId).Inc(); 122 | break; 123 | case UserLogoutSuccessEvent _: 124 | UserLogoutSuccessCount.Inc(); 125 | break; 126 | case ConsentGrantedEvent _: 127 | ConsentGrantedCount.Inc(); 128 | break; 129 | case ConsentDeniedEvent _: 130 | ConsentDeniedCount.Inc(); 131 | break; 132 | case UnhandledExceptionEvent _: 133 | UnhandledExceptionCount.Inc(); 134 | break; 135 | case DeviceAuthorizationSuccessEvent _: 136 | DeviceAuthorizationSuccessCount.Inc(); 137 | break; 138 | case DeviceAuthorizationFailureEvent _: 139 | DeviceAuthorizationFailureCount.Inc(); 140 | break; 141 | } 142 | 143 | return Task.CompletedTask; 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/prometheus-net.IdentityServer/prometheus-net.IdentityServer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | prometheus-net.IdentityServer 6 | Prometheus.IdentityServer 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | --------------------------------------------------------------------------------