├── .github
└── workflows
│ ├── publish-nuget-packages.yaml
│ └── run-tests.yaml
├── .gitignore
├── LICENSE.txt
├── README.md
├── TODO.txt
├── docs
├── grafana-example.PNG
├── metrics-exposed-3.1.md
├── metrics-exposed-5.0.md
└── metrics-exposed.md
├── examples
├── AspNetCoreExample
│ ├── AspNetCoreExample.csproj
│ ├── Controllers
│ │ ├── CollectorController.cs
│ │ ├── NoOverheadController.cs
│ │ └── SimulateController.cs
│ ├── Dockerfile
│ ├── Options.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── docker-compose.yml
├── grafana
│ └── provisioning
│ │ ├── dashboards
│ │ ├── Debugging_metrics.json
│ │ ├── NET_runtime_metrics_dashboard.json
│ │ └── dashboard.yml
│ │ └── datasources
│ │ └── datasource.yml
└── prometheus
│ └── prometheus.yml
├── prometheus-net.DotNetRuntime.sln
├── src
├── Benchmarks
│ ├── Benchmarks.csproj
│ ├── Benchmarks
│ │ ├── AspNetBenchmarkBase.cs
│ │ ├── BaselineBenchmark.cs
│ │ ├── DefaultBenchmark.cs
│ │ ├── DictBenchmark.cs
│ │ ├── DotNetRuntimeStatsBenchmarkBase.cs
│ │ ├── EventCounterParserBenchmark.cs
│ │ ├── PrometheusMTBenchmark.cs
│ │ ├── PrometheusSTBenchmark.cs
│ │ └── ReactiveBenchmark.cs
│ ├── Controllers
│ │ └── BenchmarkController.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ └── appsettings.json
├── Common.csproj
├── prometheus-net.DotNetRuntime.Tests
│ ├── DotNetRuntimeStatsBuilderTests.cs
│ ├── DotNetRuntimeStatsCollectorTests.cs
│ ├── EventListening
│ │ ├── EventCounterParserBaseTests.cs
│ │ ├── EventParserTypes.cs
│ │ ├── IEventParserTests.cs
│ │ ├── Parsers
│ │ │ ├── EventListenerIntegrationTestBase.cs
│ │ │ ├── SystemRuntimeCounterTests.cs
│ │ │ └── Util
│ │ │ │ ├── EventPairTimerTests.cs
│ │ │ │ └── SamplingRateTests.cs
│ │ └── TestHelpers.cs
│ ├── ExtensionTests.cs
│ ├── IntegrationTests
│ │ ├── ContentionTests.cs
│ │ ├── ExceptionTests.cs
│ │ ├── GcTests.cs
│ │ ├── Helpers.cs
│ │ ├── IntegrationTestBase.cs
│ │ ├── JitCompilerTests.cs
│ │ ├── SocketsTests.cs
│ │ └── ThreadPoolTests.cs
│ ├── Metrics
│ │ └── Producers
│ │ │ └── Util
│ │ │ ├── LabelGeneratorTests.cs
│ │ │ ├── RatioTests.cs
│ │ │ └── StringExtensionsTests.cs
│ ├── Properties.cs
│ └── prometheus-net.DotNetRuntime.Tests.csproj
└── prometheus-net.DotNetRuntime
│ ├── CaptureLevel.cs
│ ├── DotNetRuntimeStatsBuilder.cs
│ ├── DotNetRuntimeStatsCollector.cs
│ ├── EventConsumption.cs
│ ├── EventListening
│ ├── CounterNameAttribute.cs
│ ├── Counters.cs
│ ├── DotNetEventListener.cs
│ ├── EventCounterParserBase.cs
│ ├── EventParserTypes.cs
│ ├── IEventCounterListener.cs
│ ├── IEventCounterParser.cs
│ ├── IEventListener.cs
│ ├── IEventParser.cs
│ ├── IEvents.cs
│ ├── Parsers
│ │ ├── ContentionEventParser.cs
│ │ ├── ExceptionEventParser.cs
│ │ ├── GcEventParser.cs
│ │ ├── JitEventParser.cs
│ │ ├── RuntimeEventParser.cs
│ │ ├── SocketsEventParser.cs
│ │ ├── ThreadPoolEventParser.cs
│ │ └── Util
│ │ │ ├── Cache.cs
│ │ │ ├── EventExtensions.cs
│ │ │ ├── EventPairTimer.cs
│ │ │ └── SamplingRate.cs
│ └── Sources
│ │ ├── DotNetRuntimeEventSource.cs
│ │ ├── FrameworkEventSource.cs
│ │ └── SystemRuntimeEventSource.cs
│ ├── Extensions.cs
│ ├── ListenerRegistration.cs
│ ├── Metrics
│ ├── IMetricProducer.cs
│ ├── MetricExtensions.cs
│ └── Producers
│ │ ├── ContentionMetricsProducer.cs
│ │ ├── ExceptionMetricsProducer.cs
│ │ ├── GcMetricsProducer.cs
│ │ ├── JitMetricsProducer.cs
│ │ ├── SocketsMetricProducer.cs
│ │ ├── ThreadPoolMetricsProducer.cs
│ │ └── Util
│ │ ├── Constants.cs
│ │ ├── LabelGenerator.cs
│ │ ├── Ratio.cs
│ │ └── StringExtensions.cs
│ ├── Properties.cs
│ ├── SampleRate.cs
│ └── prometheus-net.DotNetRuntime.csproj
└── tools
└── DocsGenerator
├── DocsGenerator.csproj
├── Program.cs
├── README.md
└── XmlDocReading.cs
/.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: '6.0.x'
15 | - run: |
16 | echo "Github ref is ${GITHUB_REF}"
17 | arrTag=(${GITHUB_REF//\// })
18 | VERSION="${arrTag[2]}"
19 | echo "Version: $VERSION"
20 | dotnet pack src/prometheus-net.DotNetRuntime --include-symbols -c "Release" -p:Version=$VERSION --output "build/"
21 | dotnet nuget push "build/prometheus-net.DotNetRuntime.*.symbols.nupkg" -k ${{ secrets.NUGET_API_KEY }} -s "https://api.nuget.org/v3/index.json" -n true
22 |
--------------------------------------------------------------------------------
/.github/workflows/run-tests.yaml:
--------------------------------------------------------------------------------
1 | name: run-tests
2 |
3 | on:
4 | push:
5 | branches: master
6 | pull_request:
7 |
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Setup .NET Core 3.1
13 | uses: actions/setup-dotnet@v1
14 | with:
15 | dotnet-version: 3.1.x
16 | - name: Setup .NET Core 5.0
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 5.0.x
20 | - name: Setup .NET Core 6.0
21 | uses: actions/setup-dotnet@v1
22 | with:
23 | dotnet-version: '6.0.x'
24 | include-prerelease: true
25 | - uses: actions/checkout@v1
26 | # This test constantly passes localy (windows + linux) but fails in the test environment. Don't have the time/ inclination to figure out why this is right now..
27 | - run: dotnet test -c "Debug" --filter Name!=When_blocking_work_is_executed_on_the_thread_pool_then_thread_pool_delays_are_measured
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 | nupkg/
7 |
8 | # Visual Studio Code
9 | .vscode
10 |
11 | # User-specific files
12 | *.suo
13 | *.user
14 | *.userosscache
15 | *.sln.docstates
16 |
17 | # Build results
18 | [Dd]ebug/
19 | [Dd]ebugPublic/
20 | [Rr]elease/
21 | [Rr]eleases/
22 | x64/
23 | x86/
24 | build/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Oo]ut/
29 | msbuild.log
30 | msbuild.err
31 | msbuild.wrn
32 | bin/
33 | obj/
34 |
35 | # Visual Studio 2015
36 | .vs/
37 |
38 | # Rider
39 | .idea/
40 | BenchmarkDotNet.Artifacts/
41 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [year] [fullname]
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # prometheus-net.DotNetMetrics
2 | A plugin for the [prometheus-net](https://github.com/prometheus-net/prometheus-net) package, [exposing .NET core runtime metrics](docs/metrics-exposed.md) including:
3 | - Garbage collection collection frequencies and timings by generation/ type, pause timings and GC CPU consumption ratio
4 | - Heap size by generation
5 | - Bytes allocated by small/ large object heap
6 | - JIT compilations and JIT CPU consumption ratio
7 | - Thread pool size, scheduling delays and reasons for growing/ shrinking
8 | - Lock contention
9 | - Exceptions thrown, broken down by type
10 |
11 | These metrics are essential for understanding the performance of any non-trivial application. Even if your application is well instrumented, you're only getting half the story- what the runtime is doing completes the picture.
12 |
13 | ## Using this package
14 | ### Requirements
15 | - .NET 5.0+ recommended, .NET core 3.1+ is supported
16 | - The [prometheus-net](https://github.com/prometheus-net/prometheus-net) package
17 |
18 | ### Install it
19 | The package can be installed from [nuget](https://www.nuget.org/packages/prometheus-net.DotNetRuntime):
20 | ```powershell
21 | dotnet add package prometheus-net.DotNetRuntime
22 | ```
23 |
24 | ### Start collecting metrics
25 | You can start metric collection with:
26 | ```csharp
27 | IDisposable collector = DotNetRuntimeStatsBuilder.Default().StartCollecting()
28 | ```
29 |
30 | You can customize the types of .NET metrics collected via the `Customize` method:
31 | ```csharp
32 | IDisposable collector = DotNetRuntimeStatsBuilder
33 | .Customize()
34 | .WithContentionStats()
35 | .WithJitStats()
36 | .WithThreadPoolStats()
37 | .WithGcStats()
38 | .WithExceptionStats()
39 | .StartCollecting();
40 | ```
41 |
42 | Once the collector is registered, you should see metrics prefixed with `dotnet_` visible in your metric output (make sure you are [exporting your metrics](https://github.com/prometheus-net/prometheus-net#http-handler)).
43 |
44 | ### Choosing a `CaptureLevel`
45 | By default the library will default generate metrics based on [event counters](https://docs.microsoft.com/en-us/dotnet/core/diagnostics/event-counters). This allows for basic instrumentation of applications with very little performance overhead.
46 |
47 | You can enable higher-fidelity metrics by providing a custom `CaptureLevel`, e.g:
48 | ```
49 | DotNetRuntimeStatsBuilder
50 | .Customize()
51 | .WithGcStats(CaptureLevel.Informational)
52 | .WithExceptionStats(CaptureLevel.Errors)
53 | ...
54 | ```
55 |
56 | Most builder methods allow the passing of a custom `CaptureLevel`- see the [documentation on exposed metrics](docs/metrics-exposed.md) for more information.
57 |
58 | ### Performance impact of `CaptureLevel.Errors`+
59 | The harder you work the .NET core runtime, the more events it generates. Event generation and processing costs can stack up, especially around these types of events:
60 | - **JIT stats**: each method compiled by the JIT compiler emits two events. Most JIT compilation is performed at startup and depending on the size of your application, this could impact your startup performance.
61 | - **GC stats with `CaptureLevel.Verbose`**: every 100KB of allocations, an event is emitted. If you are consistently allocating memory at a rate > 1GB/sec, you might like to disable GC stats.
62 | - **Exception stats with `CaptureLevel.Errors`**: for every exception throw, an event is generated.
63 |
64 | #### Recycling collectors
65 | There have been long-running [performance issues since .NET core 3.1](https://github.com/dotnet/runtime/issues/43985#issuecomment-800629516) that could see CPU consumption grow over time when long-running trace sessions are used.
66 | While many of the performance issues have been addressed now in .NET 6.0, a workaround was identified: stopping and starting (AKA recycling) collectors periodically helped reduce CPU consumption:
67 | ```
68 | IDisposable collector = DotNetRuntimeStatsBuilder.Default()
69 | // Recycles all collectors once every day
70 | .RecycleCollectorsEvery(TimeSpan.FromDays(1))
71 | .StartCollecting()
72 | ```
73 |
74 | While this [has been observed to reduce CPU consumption](https://github.com/djluck/prometheus-net.DotNetRuntime/issues/6#issuecomment-784540220) this technique has been identified as a [possible culprit that can lead
75 | to application instability](https://github.com/djluck/prometheus-net.DotNetRuntime/issues/72).
76 |
77 | Behaviour on different runtime versions is:
78 | - .NET core 3.1: recycling verified to cause massive instability, cannot enable recycling.
79 | - .NET 5.0: recycling verified to be beneficial, recycling every day enabled by default.
80 | - .NET 6.0+: recycling verified to be less necesarry due to long-standing issues being addressed although [some users report recycling to be beneficial](https://github.com/djluck/prometheus-net.DotNetRuntime/pull/73#issuecomment-1308558226),
81 | disabled by default but recycling can be enabled.
82 |
83 | > TLDR: If you observe increasing CPU over time, try enabling recycling. If you see unexpected crashes after using this application, try disabling recycling.
84 |
85 |
86 | ## Examples
87 | An example `docker-compose` stack is available in the [`examples/`](examples/) folder. Start it with:
88 |
89 | ```
90 | docker-compose up -d
91 | ```
92 |
93 | You can then visit [`http://localhost:3000`](http://localhost:3000) to view metrics being generated by a sample application.
94 |
95 | ### Grafana dashboard
96 | The metrics exposed can drive a rich dashboard, giving you a graphical insight into the performance of your application ( [exported dashboard available here](examples/grafana/provisioning/dashboards/NET_runtime_metrics_dashboard.json)):
97 |
98 | 
99 |
100 | ## Further reading
101 | - The mechanism for listening to runtime events is outlined in the [.NET core 2.2 release notes](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-2-2#core).
102 | - A partial list of core CLR events is available in the [ETW events documentation](https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-events).
--------------------------------------------------------------------------------
/docs/grafana-example.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djluck/prometheus-net.DotNetRuntime/ccafba9c677aabebd081267e651893a0c2e5849b/docs/grafana-example.PNG
--------------------------------------------------------------------------------
/docs/metrics-exposed.md:
--------------------------------------------------------------------------------
1 | # Metrics exposed
2 |
3 | The documents below catalog the metrics exposed, depending on the .NET runtime version an application is using:
4 | - [.NET 5.0](./metrics-exposed-5.0.md)
5 | - [.NET core 3.1](./metrics-exposed-3.1.md)
6 |
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/AspNetCoreExample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net5.0
4 | 8
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/Controllers/CollectorController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Net;
6 | using System.Runtime.InteropServices;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using Microsoft.AspNetCore.Mvc;
10 |
11 | namespace AspNetCoreExample.Controllers
12 | {
13 | [Route("api/[controller]")]
14 | [ApiController]
15 | public class CollectorController : ControllerBase
16 | {
17 | // GET api/values
18 | [HttpGet]
19 | [Route("enable")]
20 | public async Task Enable()
21 | {
22 |
23 | if (Startup.Collector != null)
24 | return new JsonResult(new { Status = "Failed - already enabled"}) { StatusCode = (int)HttpStatusCode.InternalServerError};
25 |
26 | Startup.Collector = Startup.CreateCollector();
27 |
28 | return new JsonResult(new { Status = "Ok- started and assigned collector"});
29 | }
30 |
31 | [HttpGet]
32 | [Route("disable")]
33 | public async Task Disable()
34 | {
35 | if (Startup.Collector == null)
36 | return new JsonResult(new { Status = "Failed - already disable"}) { StatusCode = (int)HttpStatusCode.InternalServerError};
37 |
38 | Startup.Collector.Dispose();
39 | Startup.Collector = null;
40 |
41 | return new JsonResult(new { Status = "Ok- stopped the collector"});
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/Controllers/NoOverheadController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Microsoft.AspNetCore.Mvc;
8 |
9 | namespace AspNetCoreExample.Controllers
10 | {
11 | [Route("api/[controller]")]
12 | [ApiController]
13 | public class NoOverheadController : ControllerBase
14 | {
15 | [HttpGet]
16 | public async Task>> Get2()
17 | {
18 | return new string[] {"value1", "value2"};
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/Controllers/SimulateController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Net.Http;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using Microsoft.AspNetCore.Mvc;
9 |
10 | namespace AspNetCoreExample.Controllers
11 | {
12 | [Route("api/[controller]")]
13 | [ApiController]
14 | public class SimulateController : ControllerBase
15 | {
16 | private readonly IHttpClientFactory _httpClientFactory;
17 |
18 | public SimulateController(IHttpClientFactory httpClientFactory)
19 | {
20 | _httpClientFactory = httpClientFactory;
21 | }
22 |
23 | [HttpGet]
24 | public async Task>> Get(
25 | bool simulateAlloc = true,
26 | bool simulateJit = true,
27 | bool simulateException = true,
28 | bool simulateBlocking = false,
29 | bool simulateOutgoingNetwork = true)
30 | {
31 | var r = new Random();
32 | if (simulateAlloc)
33 | {
34 | // assign some SOH memory
35 | var x = new byte[r.Next(1024, 1024 * 64)];
36 |
37 | // assign some LOH memory
38 | x = new byte[r.Next(1024 * 90, 1024 * 100)];
39 | }
40 |
41 | // await a task (will result in a Task being scheduled on the thread pool)
42 | await Task.Yield();
43 |
44 | if (simulateJit)
45 | {
46 | var val = r.Next();
47 | CompileMe(() => val);
48 | }
49 |
50 | if (simulateException)
51 | {
52 | try
53 | {
54 | var divide = 0;
55 | var result = 1 / divide;
56 | }
57 | catch
58 | {
59 | }
60 | }
61 |
62 | if (simulateBlocking)
63 | {
64 | Thread.Sleep(100);
65 | }
66 |
67 | if (simulateOutgoingNetwork)
68 | {
69 | using var client = _httpClientFactory.CreateClient();
70 | using var _ = await client.GetAsync("https://httpstat.us/200");
71 | }
72 |
73 | return new string[] {"value1" + r.Next(), "value2"+ r.Next()};
74 | }
75 |
76 | private void CompileMe(Expression> func)
77 | {
78 | func.Compile()();
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/Dockerfile:
--------------------------------------------------------------------------------
1 | #FROM mcr.microsoft.com/dotnet/core/sdk:3.1.406 AS build
2 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
3 | WORKDIR /src
4 | COPY . .
5 | RUN dotnet publish "examples/AspNetCoreExample" -c Release -o /app
6 |
7 | #FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.3 AS final
8 | #FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.10 AS final
9 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 as final
10 | WORKDIR /app
11 | COPY --from=build /app /app
12 | ENTRYPOINT ["dotnet", "AspNetCoreExample.dll"]
13 |
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/Options.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AspNetCoreExample
4 | {
5 | public class Options
6 | {
7 | public bool EnableMetrics { get; set; } = true;
8 | public bool UseDefaultMetrics { get; set; } = false;
9 | public bool UseDebuggingMetrics { get; set; } = false;
10 | public TimeSpan RecycleEvery { get; set; } = TimeSpan.FromDays(1);
11 | public int? MinThreadPoolSize { get; set; } = null;
12 | }
13 | }
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.AspNetCore;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.Extensions.Configuration;
5 | using Prometheus.DotNetRuntime;
6 |
7 | namespace AspNetCoreExample
8 | {
9 | public class Program
10 | {
11 | public static void Main(string[] args)
12 | {
13 | CreateWebHostBuilder(args).Build().Run();
14 | }
15 |
16 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
17 | WebHost.CreateDefaultBuilder(args)
18 | .ConfigureAppConfiguration(opts =>
19 | {
20 | opts.AddEnvironmentVariables("Example");
21 | })
22 | .ConfigureKestrel(opts =>
23 | {
24 | opts.AllowSynchronousIO = true;
25 | })
26 | .UseStartup();
27 | }
28 | }
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "AspNetCoreExample": {
5 | "commandName": "Project",
6 | "launchBrowser": true,
7 | "launchUrl": "metrics",
8 | "applicationUrl": "http://localhost:5000",
9 | "environmentVariables": {
10 | "ASPNETCORE_ENVIRONMENT": "Development",
11 | "Example__UseDebuggingMetrics": "true",
12 | "Example__UseDefaultMetrics": "true"
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.Tracing;
4 | using System.Linq;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Microsoft.AspNetCore.Builder;
8 | using Microsoft.AspNetCore.Hosting;
9 | using Microsoft.AspNetCore.HttpsPolicy;
10 | using Microsoft.AspNetCore.Mvc;
11 | using Microsoft.Extensions.Configuration;
12 | using Microsoft.Extensions.DependencyInjection;
13 | using Microsoft.Extensions.Logging;
14 | using Microsoft.Extensions.Options;
15 | using Prometheus;
16 | using Prometheus.DotNetRuntime;
17 |
18 | namespace AspNetCoreExample
19 | {
20 | public class Startup
21 | {
22 | private static Options _options;
23 | public static IDisposable Collector;
24 | private static ILogger _logger;
25 |
26 | public Startup(IConfiguration configuration, ILogger logger)
27 | {
28 | Configuration = configuration;
29 |
30 | _options = new Options();
31 | _logger = logger;
32 | configuration.Bind("Example", _options);
33 |
34 | if (_options.EnableMetrics)
35 | {
36 | Collector = CreateCollector();
37 | }
38 | else
39 | logger.LogWarning($"prometheus-net.DotNetRuntime was NOT started- {_options.EnableMetrics} was set to false");
40 |
41 | if (_options.MinThreadPoolSize.HasValue)
42 | {
43 | logger.LogInformation($"Setting minimum thread pool size of {_options.MinThreadPoolSize.Value}");
44 | ThreadPool.SetMinThreads(_options.MinThreadPoolSize.Value, 1);
45 | }
46 | }
47 |
48 | public IConfiguration Configuration { get; }
49 |
50 | // This method gets called by the runtime. Use this method to add services to the container.
51 | public void ConfigureServices(IServiceCollection services)
52 | {
53 | services.AddMvc();
54 | services.AddHttpClient();
55 | }
56 |
57 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
58 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
59 | {
60 | if (env.IsDevelopment())
61 | {
62 | app.UseDeveloperExceptionPage();
63 | }
64 |
65 | app.UseRouting();
66 |
67 | app.UseEndpoints(endpoints =>
68 | {
69 | // Mapping of endpoints goes here:
70 | endpoints.MapControllers();
71 | });
72 |
73 | app.UseMetricServer();
74 | }
75 |
76 | public static IDisposable CreateCollector()
77 | {
78 | _logger.LogInformation($"Configuring prometheus-net.DotNetRuntime: will recycle event listeners every {_options.RecycleEvery} ({_options.RecycleEvery.TotalSeconds:N0} seconds).");
79 |
80 | var builder = DotNetRuntimeStatsBuilder.Default();
81 |
82 | if (!_options.UseDefaultMetrics)
83 | {
84 | builder = DotNetRuntimeStatsBuilder.Customize()
85 | .WithContentionStats(CaptureLevel.Informational)
86 | .WithGcStats(CaptureLevel.Verbose)
87 | .WithThreadPoolStats(CaptureLevel.Informational)
88 | .WithExceptionStats(CaptureLevel.Errors)
89 | .WithJitStats();
90 | }
91 |
92 | builder
93 | #if NET5_0_OR_GREATER
94 | .RecycleCollectorsEvery(_options.RecycleEvery)
95 | #endif
96 | .WithErrorHandler(ex => _logger.LogError(ex, "Unexpected exception occurred in prometheus-net.DotNetRuntime"));
97 |
98 | if (_options.UseDebuggingMetrics)
99 | {
100 | _logger.LogInformation("Using debugging metrics.");
101 | builder.WithDebuggingMetrics(true);
102 | }
103 |
104 | _logger.LogInformation("Starting prometheus-net.DotNetRuntime...");
105 |
106 | return builder
107 | .StartCollecting();
108 | }
109 | }
110 | }
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning",
5 | "System": "Warning",
6 | "Microsoft": "Warning"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/AspNetCoreExample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning",
5 | "AspNetCoreExample" : "Information"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/examples/docker-compose.yml:
--------------------------------------------------------------------------------
1 | # With thanks to https://github.com/stefanprodan/dockprom for this excellent template for providing prom + grafana
2 | version: '2.1'
3 |
4 | networks:
5 | monitor-net:
6 | driver: bridge
7 |
8 | volumes:
9 | prometheus_data: {}
10 | grafana_data: {}
11 |
12 | services:
13 |
14 | prometheus:
15 | image: prom/prometheus:v2.22.0
16 | container_name: prometheus
17 | volumes:
18 | - ./prometheus:/etc/prometheus
19 | - prometheus_data:/prometheus
20 | command:
21 | - '--config.file=/etc/prometheus/prometheus.yml'
22 | - '--storage.tsdb.path=/prometheus'
23 | - '--web.console.libraries=/etc/prometheus/console_libraries'
24 | - '--web.console.templates=/etc/prometheus/consoles'
25 | - '--storage.tsdb.retention.time=200h'
26 | - '--web.enable-lifecycle'
27 | restart: unless-stopped
28 | expose:
29 | - 9090
30 | ports:
31 | - 9090:9090
32 | networks:
33 | - monitor-net
34 | labels:
35 | org.label-schema.group: "monitoring"
36 |
37 | grafana:
38 | image: grafana/grafana:7.3.1
39 | container_name: grafana
40 | volumes:
41 | - grafana_data:/var/lib/grafana
42 | - ./grafana/provisioning:/etc/grafana/provisioning
43 | environment:
44 | - GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin}
45 | - GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
46 | - GF_USERS_ALLOW_SIGN_UP=false
47 | - GF_AUTH_ANONYMOUS_ENABLED=true
48 | - GF_AUTH_ANONYMOUS_ORG_ROLE=Editor
49 | restart: unless-stopped
50 | ports:
51 | - 3000:3000
52 | networks:
53 | - monitor-net
54 | labels:
55 | org.label-schema.group: "monitoring"
56 |
57 | aspexample:
58 | build:
59 | context: ../
60 | dockerfile: examples/AspNetCoreExample/Dockerfile
61 | expose:
62 | - 5000
63 | environment:
64 | - ASPNETCORE_URLS=http://+:5000
65 | # Additional vars that can be set to tweak behaviour
66 | - Example__UseDefaultMetrics=true
67 | #- Example__EnableMetrics=false
68 | #- Example__UseDebuggingMetrics=true
69 | #- Example__RecycleEvery=00:10:00
70 | #- Example__MinThreadPoolSize=100
71 | ports:
72 | - 5001:5000
73 | mem_limit: "200M"
74 | networks:
75 | - monitor-net
76 |
77 | bombardier:
78 | image: alpine/bombardier
79 | command: -c 25 -d 1000h -r 100 -t 15s http://aspexample:5000/api/simulate
80 | # High intensity
81 | # command: -c 1000 -d 1000h -r 2000 -t 10s http://aspexample:5000/api/simulate
82 | networks:
83 | - monitor-net
--------------------------------------------------------------------------------
/examples/grafana/provisioning/dashboards/dashboard.yml:
--------------------------------------------------------------------------------
1 | apiVersion: 1
2 |
3 | providers:
4 | - name: 'Prometheus'
5 | orgId: 1
6 | folder: ''
7 | type: file
8 | disableDeletion: false
9 | editable: true
10 | allowUiUpdates: true
11 | options:
12 | path: /etc/grafana/provisioning/dashboards
--------------------------------------------------------------------------------
/examples/grafana/provisioning/datasources/datasource.yml:
--------------------------------------------------------------------------------
1 | apiVersion: 1
2 |
3 | datasources:
4 | - name: Prometheus
5 | type: prometheus
6 | access: proxy
7 | orgId: 1
8 | url: http://prometheus:9090
9 | basicAuth: false
10 | isDefault: true
11 | editable: true
--------------------------------------------------------------------------------
/examples/prometheus/prometheus.yml:
--------------------------------------------------------------------------------
1 | global:
2 | scrape_interval: 15s
3 | evaluation_interval: 15s
4 |
5 |
6 | # A scrape configuration containing exactly one endpoint to scrape.
7 | scrape_configs:
8 | - job_name: 'dotnetruntime'
9 | scrape_interval: 5s
10 | static_configs:
11 | - targets: ['aspexample:5000', 'host.docker.internal:5000']
12 |
13 |
14 | - job_name: 'prometheus'
15 | scrape_interval: 10s
16 | static_configs:
17 | - targets: ['localhost:9090']
18 |
19 |
--------------------------------------------------------------------------------
/prometheus-net.DotNetRuntime.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "prometheus-net.DotNetRuntime", "src\prometheus-net.DotNetRuntime\prometheus-net.DotNetRuntime.csproj", "{A40AD08A-53CB-40F3-A6D8-6FFCEC024289}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "prometheus-net.DotNetRuntime.Tests", "src\prometheus-net.DotNetRuntime.Tests\prometheus-net.DotNetRuntime.Tests.csproj", "{7F4E2E72-5745-4312-B238-CD7B731957B0}"
6 | EndProject
7 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{31AD912F-A1DC-434A-8C8D-049F4BBD67D4}"
8 | EndProject
9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreExample", "examples\AspNetCoreExample\AspNetCoreExample.csproj", "{D01E9ED3-E35C-4F44-A5AD-5350E43AA636}"
10 | EndProject
11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "src\Benchmarks\Benchmarks.csproj", "{DD607E45-45AD-4F9D-9102-82BD99E49BEC}"
12 | EndProject
13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{D8594A14-5AC8-40F3-B346-38A266B235E0}"
14 | EndProject
15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocsGenerator", "tools\DocsGenerator\DocsGenerator.csproj", "{193B461A-49E4-4178-B88C-BA0EF6B4FC55}"
16 | EndProject
17 | Global
18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 | Release|Any CPU = Release|Any CPU
20 | Debug|Any CPU = Debug|Any CPU
21 | EndGlobalSection
22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
23 | {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {A40AD08A-53CB-40F3-A6D8-6FFCEC024289}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {A40AD08A-53CB-40F3-A6D8-6FFCEC024289}.Release|Any CPU.Build.0 = Release|Any CPU
33 | {A40AD08A-53CB-40F3-A6D8-6FFCEC024289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {A40AD08A-53CB-40F3-A6D8-6FFCEC024289}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {7F4E2E72-5745-4312-B238-CD7B731957B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
36 | {7F4E2E72-5745-4312-B238-CD7B731957B0}.Release|Any CPU.Build.0 = Release|Any CPU
37 | {7F4E2E72-5745-4312-B238-CD7B731957B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {7F4E2E72-5745-4312-B238-CD7B731957B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {193B461A-49E4-4178-B88C-BA0EF6B4FC55}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {193B461A-49E4-4178-B88C-BA0EF6B4FC55}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {193B461A-49E4-4178-B88C-BA0EF6B4FC55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {193B461A-49E4-4178-B88C-BA0EF6B4FC55}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | EndGlobalSection
44 | GlobalSection(NestedProjects) = preSolution
45 | {D01E9ED3-E35C-4F44-A5AD-5350E43AA636} = {31AD912F-A1DC-434A-8C8D-049F4BBD67D4}
46 | {193B461A-49E4-4178-B88C-BA0EF6B4FC55} = {D8594A14-5AC8-40F3-B346-38A266B235E0}
47 | EndGlobalSection
48 | EndGlobal
49 |
--------------------------------------------------------------------------------
/src/Benchmarks/Benchmarks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | netcoreapp3.0
6 | InProcess
7 | true
8 |
9 |
10 |
11 | true
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | true
34 | PreserveNewest
35 | Never
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/Benchmarks/Benchmarks/AspNetBenchmarkBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Http;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using BenchmarkDotNet.Attributes;
8 | using Microsoft.AspNetCore;
9 | using Microsoft.AspNetCore.Hosting;
10 | using Prometheus.DotNetRuntime;
11 |
12 | namespace Benchmarks.Benchmarks
13 | {
14 | [BenchmarkCategory("aspnet")]
15 | public abstract class AspNetBenchmarkBase
16 | {
17 | public const int NumRequests = 10_000;
18 |
19 | private IWebHost webHost;
20 | private CancellationTokenSource ctSource = new CancellationTokenSource();
21 | private HttpClient client;
22 | private byte[][] buffers;
23 | private Task webHostTask;
24 |
25 | public int NumHttpConnections { get; set; } = 50;
26 |
27 | [GlobalSetup]
28 | public void GlobalSetup()
29 | {
30 | PreGlobalSetup();
31 | webHost = WebHost.CreateDefaultBuilder()
32 | .UseStartup()
33 | .ConfigureKestrel(cfg => { cfg.ListenLocalhost(5000); })
34 | .Build();
35 | webHostTask = webHost.RunAsync(ctSource.Token);
36 |
37 | // preallocate buffers to avoid having them counted as part of each benchmark run
38 | buffers = new byte[NumHttpConnections][];
39 | for (int i = 0; i < buffers.Length; i++)
40 | buffers[i] = new byte[1024 * 64];
41 |
42 | client = new HttpClient(new SocketsHttpHandler() { MaxConnectionsPerServer = NumHttpConnections });
43 | }
44 |
45 | protected virtual void PreGlobalSetup()
46 | {
47 | }
48 |
49 | protected virtual void PostGlobalCleanup()
50 | {
51 | }
52 |
53 | [GlobalCleanup]
54 | public void GlobalCleanup()
55 | {
56 | ctSource.Cancel();
57 | client.Dispose();
58 | webHostTask.Wait();
59 | PostGlobalCleanup();
60 | }
61 |
62 | protected async Task MakeHttpRequests()
63 | {
64 | var requestsRemaining = NumRequests;
65 | var tasks = Enumerable.Range(0, NumHttpConnections)
66 | .Select(async n =>
67 | {
68 | var buffer = buffers[n];
69 | while (Interlocked.Decrement(ref requestsRemaining) > 0)
70 | {
71 | await using var response = await client.GetStreamAsync("http://localhost:5000/api/benchmark");
72 | while (await response.ReadAsync(buffer, 0, buffer.Length) > 0)
73 | {
74 | }
75 | }
76 | });
77 |
78 | await Task.WhenAll(tasks);
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/src/Benchmarks/Benchmarks/BaselineBenchmark.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using BenchmarkDotNet.Attributes;
4 | using Prometheus.DotNetRuntime;
5 |
6 | namespace Benchmarks.Benchmarks
7 | {
8 | public class BaselineBenchmark : AspNetBenchmarkBase
9 | {
10 | [Benchmark(Description = "No stats collectors enabled", Baseline = true, OperationsPerInvoke = NumRequests)]
11 | public async Task Make_Requests()
12 | {
13 | await MakeHttpRequests();
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Benchmarks/Benchmarks/DefaultBenchmark.cs:
--------------------------------------------------------------------------------
1 | using Prometheus.DotNetRuntime;
2 |
3 | namespace Benchmarks.Benchmarks
4 | {
5 | public class DefaultBenchmark : DotNetRuntimeStatsBenchmarkBase
6 | {
7 | protected override DotNetRuntimeStatsBuilder.Builder GetStatsBuilder()
8 | {
9 | return DotNetRuntimeStatsBuilder.Default();
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/Benchmarks/Benchmarks/DictBenchmark.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Reactive;
5 | using System.Reactive.Disposables;
6 | using System.Reactive.Linq;
7 | using System.Reactive.Subjects;
8 | using System.Threading;
9 | using System.Threading.Channels;
10 | using BenchmarkDotNet.Attributes;
11 |
12 | namespace Benchmarks.Benchmarks
13 | {
14 | public class DictBenchmark
15 | {
16 | private Dictionary _dict ;
17 | private ConcurrentDictionary _connDict;
18 |
19 | public DictBenchmark()
20 | {
21 | }
22 |
23 | [IterationSetup]
24 | public void Setup()
25 | {
26 | _dict = new Dictionary(12000);
27 | _connDict = new (concurrencyLevel: Environment.ProcessorCount, 20000);
28 |
29 | }
30 |
31 | [Benchmark]
32 | public void AddToDict()
33 | {
34 | for (int i = 0; i < 10000; i++)
35 | _dict.Add(i, "test value");
36 | }
37 |
38 | [Benchmark]
39 | public void AddToConcurrentDict()
40 | {
41 | for (int i = 0; i < 10000; i++)
42 | _connDict.TryAdd(i, "test value");
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/Benchmarks/Benchmarks/DotNetRuntimeStatsBenchmarkBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using BenchmarkDotNet.Attributes;
5 | using Prometheus.DotNetRuntime;
6 |
7 | namespace Benchmarks.Benchmarks
8 | {
9 | public abstract class DotNetRuntimeStatsBenchmarkBase : AspNetBenchmarkBase
10 | {
11 | private IDisposable collector;
12 |
13 | protected override void PreGlobalSetup()
14 | {
15 | collector = GetStatsBuilder().StartCollecting();
16 | }
17 |
18 | protected override void PostGlobalCleanup()
19 | {
20 | collector.Dispose();
21 | }
22 |
23 | [Benchmark(Baseline = false, OperationsPerInvoke = NumRequests)]
24 | public async Task Make_Requests()
25 | {
26 | await MakeHttpRequests();
27 | }
28 |
29 | protected abstract DotNetRuntimeStatsBuilder.Builder GetStatsBuilder();
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Benchmarks/Benchmarks/EventCounterParserBenchmark.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.ObjectModel;
3 | using System.Diagnostics.Tracing;
4 | using System.Linq;
5 | using BenchmarkDotNet.Attributes;
6 | using Fasterflect;
7 | using Prometheus.DotNetRuntime.EventListening;
8 |
9 | namespace Benchmarks.Benchmarks
10 | {
11 | public class EventCounterParserBenchmark
12 | {
13 | private EventWrittenEventArgs _meanValue;
14 | private EventWrittenEventArgs _incrCounter;
15 | private DummyTypeEventCounterParser _parser;
16 |
17 | public EventCounterParserBenchmark()
18 | {
19 | _meanValue = CreateCounterEventWrittenEventArgs(
20 | ("Name", "test-mean-counter"),
21 | ("DisplayName", "some value"),
22 | ("Mean", 5.0),
23 | ("StandardDeviation", 1),
24 | ("Count", 1),
25 | ("Min", 1),
26 | ("Max", 1),
27 | ("IntervalSec", 1),
28 | ("Series", 1),
29 | ("CounterType", "Mean"),
30 | ("Metadata", ""),
31 | ("DisplayUnits", "")
32 | );
33 |
34 | _incrCounter = CreateCounterEventWrittenEventArgs(
35 | ("Name", "test-incrementing-counter"),
36 | ("DisplayName", "some value"),
37 | ("DisplayRateTimeScale", TimeSpan.FromSeconds(10)),
38 | ("Increment", 6.0),
39 | ("IntervalSec", 1),
40 | ("Series", 1),
41 | ("CounterType", "Sum"),
42 | ("Metadata", ""),
43 | ("DisplayUnits", "")
44 | );
45 |
46 | _parser = new DummyTypeEventCounterParser();
47 | var total = 0.0;
48 | _parser.TestIncrementingCounter += e => total += e.IncrementedBy;
49 |
50 | var last = 0.0;
51 | _parser.TestMeanCounter += e => last = e.Mean;
52 | }
53 |
54 | [Benchmark]
55 | public void ParseIncrementingCounter()
56 | {
57 | _parser.ProcessEvent(_incrCounter);
58 | }
59 |
60 | [Benchmark]
61 | public void ParseMeanCounter()
62 | {
63 | _parser.ProcessEvent(_meanValue);
64 | }
65 |
66 | public static EventWrittenEventArgs CreateEventWrittenEventArgs(int eventId, DateTime? timestamp = null, params object[] payload)
67 | {
68 | var args = (EventWrittenEventArgs)typeof(EventWrittenEventArgs).CreateInstance(new []{ typeof(EventSource)}, Flags.NonPublic | Flags.Instance, new object[] { null});
69 | args.SetPropertyValue("EventId", eventId);
70 | args.SetPropertyValue("Payload", new ReadOnlyCollection