├── .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 |  [](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