diff --git a/docs/metrics-exposed-3.1.md b/docs/metrics-exposed-3.1.md new file mode 100644 index 0000000..293eb3d --- /dev/null +++ b/docs/metrics-exposed-3.1.md @@ -0,0 +1,122 @@ +# `.net 3.1` metrics + +Each subheading details the metrics produced by calling builder methods with the specified `CaptureLevel`. + +## Default metrics + +Metrics that are included by default, regardless of what stats collectors are enabled. + +| Name | Type | Description | Labels | +| ------------------- | ------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | +| `dotnet_build_info` | `Gauge` | Build information about prometheus\-net.DotNetRuntime and the environment | `version`, `target_framework`, `runtime_version`, `os_version`, `process_architecture`, `gc_mode` | +| `process_cpu_count` | `Gauge` | The number of processor cores available to this process. | | + +## `.WithJitStats()` + +Include metrics summarizing the volume of methods being compiled + by the Just\-In\-Time compiler. + +### `CaptureLevel.Verbose` + +Includes metrics generated by `CaptureLevel.Counters` plus: + +| Name | Type | Description | Labels | +| --------------------------------- | --------- | ------------------------------------------------------------------------------------------------- | --------- | +| `dotnet_jit_cpu_ratio` | `Gauge` | The amount of total CPU time consumed spent JIT'ing | | +| `dotnet_jit_method_total` | `Counter` | Total number of methods compiled by the JIT compiler, broken down by compilation for dynamic code | `dynamic` | +| `dotnet_jit_method_seconds_total` | `Counter` | Total number of seconds spent in the JIT compiler, broken down by compilation for dynamic code | `dynamic` | + +## `.WithGcStats()` + +Include metrics recording the frequency and duration of garbage collections\/ pauses, heap sizes and + volume of allocations. + +### `CaptureLevel.Counters` + +| Name | Type | Description | Labels | +| ---------------------------------------- | --------- | ---------------------------------------------------------------------------------------------- | --------------- | +| `dotnet_gc_collection_count_total` | `Counter` | Counts the number of garbage collections that have occurred, broken down by generation number. | `gc_generation` | +| `dotnet_gc_heap_size_bytes` | `Gauge` | The current size of all heaps (only updated after a garbage collection) | `gc_generation` | +| `dotnet_gc_pause_ratio` | `Gauge` | The percentage of time the process spent paused for garbage collection | | +| `dotnet_gc_memory_total_available_bytes` | `Gauge` | The upper limit on the amount of physical memory .NET can allocate to | | +| `dotnet_gc_allocated_bytes_total` | `Counter` | The total number of bytes allocated on the managed heap | | + +### `CaptureLevel.Informational` + +Includes metrics generated by `CaptureLevel.Counters` plus: + +| Name | Type | Description | Labels | +| ------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| `dotnet_gc_finalization_queue_length` | `Gauge` | The number of objects waiting to be finalized | | +| `dotnet_gc_collection_count_total` | `Counter` | Counts the number of garbage collections that have occurred, broken down by generation number and the reason for the collection. | `gc_generation`, `gc_reason` | +| `dotnet_gc_pause_seconds` | `Histogram` | The amount of time execution was paused for garbage collection | | +| `dotnet_gc_cpu_ratio` | `Gauge` | The percentage of process CPU time spent running garbage collections | | +| `dotnet_gc_collection_seconds` | `Histogram` | The amount of time spent running garbage collections | `gc_generation`, `gc_type` | +| `dotnet_gc_pinned_objects` | `Gauge` | The number of pinned objects | | + +### `CaptureLevel.Verbose` + +Includes metrics generated by `CaptureLevel.Counters`, `CaptureLevel.Informational` plus: + +| Name | Type | Description | Labels | +| --------------------------------- | --------- | ------------------------------------------------------- | --------- | +| `dotnet_gc_allocated_bytes_total` | `Counter` | The total number of bytes allocated on the managed heap | `gc_heap` | + +## `.WithExceptionStats()` + +Include metrics that measure the number of exceptions thrown. + +### `CaptureLevel.Counters` + +| Name | Type | Description | Labels | +| ------------------------- | --------- | -------------------------- | ------ | +| `dotnet_exceptions_total` | `Counter` | Count of exceptions thrown | | + +### `CaptureLevel.Errors` + +Includes metrics generated by `CaptureLevel.Counters` plus: + +| Name | Type | Description | Labels | +| ------------------------- | --------- | ----------------------------------------------- | ------ | +| `dotnet_exceptions_total` | `Counter` | Count of exceptions thrown, broken down by type | `type` | + +## `.WithContentionStats()` + +Include metrics around volume of locks contended. + +### `CaptureLevel.Counters` + +| Name | Type | Description | Labels | +| ------------------------- | --------- | ----------------------------- | ------ | +| `dotnet_contention_total` | `Counter` | The number of locks contended | | + +### `CaptureLevel.Informational` + +Includes metrics generated by `CaptureLevel.Counters` plus: + +| Name | Type | Description | Labels | +| --------------------------------- | --------- | ----------------------------------------------- | ------ | +| `dotnet_contention_seconds_total` | `Counter` | The total amount of time spent contending locks | | + +## `.WithThreadPoolStats()` + +Include metrics around the size of the worker and IO thread pools and reasons + for worker thread pool changes. + +### `CaptureLevel.Counters` + +| Name | Type | Description | Labels | +| ------------------------------------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------- | ------ | +| `dotnet_threadpool_timer_count` | `Gauge` | The number of timers active | | +| `dotnet_threadpool_queue_length` | `Histogram` | Measures the queue length of the thread pool. Values greater than 0 indicate a backlog of work for the threadpool to process. | | +| `dotnet_threadpool_throughput_total` | `Counter` | The total number of work items that have finished execution in the thread pool | | +| `dotnet_threadpool_num_threads` | `Gauge` | The number of active threads in the thread pool | | + +### `CaptureLevel.Informational` + +Includes metrics generated by `CaptureLevel.Counters` plus: + +| Name | Type | Description | Labels | +| ------------------------------------- | --------- | ------------------------------------------------------------------------------------------------- | ------------------- | +| `dotnet_threadpool_adjustments_total` | `Counter` | The total number of changes made to the size of the thread pool, labeled by the reason for change | `adjustment_reason` | +| `dotnet_threadpool_io_num_threads` | `Gauge` | The number of active threads in the IO thread pool | | diff --git a/docs/metrics-exposed-5.0.md b/docs/metrics-exposed-5.0.md new file mode 100644 index 0000000..6fe01c4 --- /dev/null +++ b/docs/metrics-exposed-5.0.md @@ -0,0 +1,130 @@ +# `.net 5.0` metrics + +Each subheading details the metrics produced by calling builder methods with the specified `CaptureLevel`. + +## Default metrics + +Metrics that are included by default, regardless of what stats collectors are enabled. + +| Name | Type | Description | Labels | +| ------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | +| `dotnet_build_info` | `Gauge` | Build information about prometheus\-net.DotNetRuntime and the environment | `version`, `target_framework`, `runtime_version`, `os_version`, `process_architecture`, `gc_mode` | +| `dotnet_internal_recycle_count` | `Counter` | prometheus\-net.DotNetRuntime internal metric. Counts the number of times the underlying event listeners have been recycled | | +| `process_cpu_count` | `Gauge` | The number of processor cores available to this process. | | + +## `.WithJitStats()` + +Include metrics summarizing the volume of methods being compiled + by the Just\-In\-Time compiler. + +### `CaptureLevel.Counters` + +| Name | Type | Description | Labels | +| ------------------------- | --------- | ---------------------------------------------------- | ------ | +| `dotnet_jit_il_bytes` | `Gauge` | Total bytes of IL compiled by the JIT compiler | | +| `dotnet_jit_method_total` | `Counter` | Total number of methods compiled by the JIT compiler | | + +### `CaptureLevel.Verbose` + +Includes metrics generated by `CaptureLevel.Counters` plus: + +| Name | Type | Description | Labels | +| --------------------------------- | --------- | ------------------------------------------------------------------------------------------------- | --------- | +| `dotnet_jit_cpu_ratio` | `Gauge` | The amount of total CPU time consumed spent JIT'ing | | +| `dotnet_jit_method_total` | `Counter` | Total number of methods compiled by the JIT compiler, broken down by compilation for dynamic code | `dynamic` | +| `dotnet_jit_method_seconds_total` | `Counter` | Total number of seconds spent in the JIT compiler, broken down by compilation for dynamic code | `dynamic` | + +## `.WithGcStats()` + +Include metrics recording the frequency and duration of garbage collections\/ pauses, heap sizes and + volume of allocations. + +### `CaptureLevel.Counters` + +| Name | Type | Description | Labels | +| ---------------------------------------- | --------- | ---------------------------------------------------------------------------------------------- | --------------- | +| `dotnet_gc_allocated_bytes_total` | `Counter` | The total number of bytes allocated on the managed heap | | +| `dotnet_gc_collection_count_total` | `Counter` | Counts the number of garbage collections that have occurred, broken down by generation number. | `gc_generation` | +| `dotnet_gc_memory_total_available_bytes` | `Gauge` | The upper limit on the amount of physical memory .NET can allocate to | | +| `dotnet_gc_pause_ratio` | `Gauge` | The percentage of time the process spent paused for garbage collection | | +| `dotnet_gc_heap_size_bytes` | `Gauge` | The current size of all heaps (only updated after a garbage collection) | `gc_generation` | + +### `CaptureLevel.Informational` + +Includes metrics generated by `CaptureLevel.Counters` plus: + +| Name | Type | Description | Labels | +| ------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| `dotnet_gc_pause_seconds` | `Histogram` | The amount of time execution was paused for garbage collection | | +| `dotnet_gc_cpu_ratio` | `Gauge` | The percentage of process CPU time spent running garbage collections | | +| `dotnet_gc_collection_count_total` | `Counter` | Counts the number of garbage collections that have occurred, broken down by generation number and the reason for the collection. | `gc_generation`, `gc_reason` | +| `dotnet_gc_collection_seconds` | `Histogram` | The amount of time spent running garbage collections | `gc_generation`, `gc_type` | +| `dotnet_gc_finalization_queue_length` | `Gauge` | The number of objects waiting to be finalized | | +| `dotnet_gc_pinned_objects` | `Gauge` | The number of pinned objects | | + +### `CaptureLevel.Verbose` + +Includes metrics generated by `CaptureLevel.Counters`, `CaptureLevel.Informational` plus: + +| Name | Type | Description | Labels | +| --------------------------------- | --------- | ------------------------------------------------------- | --------- | +| `dotnet_gc_allocated_bytes_total` | `Counter` | The total number of bytes allocated on the managed heap | `gc_heap` | + +## `.WithExceptionStats()` + +Include metrics that measure the number of exceptions thrown. + +### `CaptureLevel.Counters` + +| Name | Type | Description | Labels | +| ------------------------- | --------- | -------------------------- | ------ | +| `dotnet_exceptions_total` | `Counter` | Count of exceptions thrown | | + +### `CaptureLevel.Errors` + +Includes metrics generated by `CaptureLevel.Counters` plus: + +| Name | Type | Description | Labels | +| ------------------------- | --------- | ----------------------------------------------- | ------ | +| `dotnet_exceptions_total` | `Counter` | Count of exceptions thrown, broken down by type | `type` | + +## `.WithContentionStats()` + +Include metrics around volume of locks contended. + +### `CaptureLevel.Counters` + +| Name | Type | Description | Labels | +| ------------------------- | --------- | ----------------------------- | ------ | +| `dotnet_contention_total` | `Counter` | The number of locks contended | | + +### `CaptureLevel.Informational` + +Includes metrics generated by `CaptureLevel.Counters` plus: + +| Name | Type | Description | Labels | +| --------------------------------- | --------- | ----------------------------------------------- | ------ | +| `dotnet_contention_seconds_total` | `Counter` | The total amount of time spent contending locks | | + +## `.WithThreadPoolStats()` + +Include metrics around the size of the worker and IO thread pools and reasons + for worker thread pool changes. + +### `CaptureLevel.Counters` + +| Name | Type | Description | Labels | +| ------------------------------------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------- | ------ | +| `dotnet_threadpool_queue_length` | `Histogram` | Measures the queue length of the thread pool. Values greater than 0 indicate a backlog of work for the threadpool to process. | | +| `dotnet_threadpool_timer_count` | `Gauge` | The number of timers active | | +| `dotnet_threadpool_throughput_total` | `Counter` | The total number of work items that have finished execution in the thread pool | | +| `dotnet_threadpool_num_threads` | `Gauge` | The number of active threads in the thread pool | | + +### `CaptureLevel.Informational` + +Includes metrics generated by `CaptureLevel.Counters` plus: + +| Name | Type | Description | Labels | +| ------------------------------------- | --------- | ------------------------------------------------------------------------------------------------- | ------------------- | +| `dotnet_threadpool_io_num_threads` | `Gauge` | The number of active threads in the IO thread pool | | +| `dotnet_threadpool_adjustments_total` | `Counter` | The total number of changes made to the size of the thread pool, labeled by the reason for change | `adjustment_reason` | diff --git a/docs/metrics-exposed.md b/docs/metrics-exposed.md index 2ae8593..45349f8 100644 --- a/docs/metrics-exposed.md +++ b/docs/metrics-exposed.md @@ -1,120 +1,5 @@ # Metrics exposed -A breakdown of all the metrics exposed by this library. Each subheading details the metrics produced by calling builder methods with the specified `CaptureLevel`. - -## Default metrics - -Metrics that are included by default, regardless of what stats collectors are enabled. - -| Name | Type | Description | Labels | -| ------------------- | ------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -| `dotnet_build_info` | `Gauge` | Build information about prometheus\-net.DotNetRuntime and the environment | `version`, `target_framework`, `runtime_version`, `os_version`, `process_architecture`, `gc_mode` | -| `process_cpu_count` | `Gauge` | The number of processor cores available to this process. | | - -## `.WithExceptionStats()` - -Include metrics that measure the number of exceptions thrown. - -### `CaptureLevel.Counters` - -| Name | Type | Description | Labels | -| ------------------------- | --------- | -------------------------- | ------ | -| `dotnet_exceptions_total` | `Counter` | Count of exceptions thrown | `type` | - -### `CaptureLevel.Errors` - -Includes metrics generated by `CaptureLevel.Counters` plus: - -| Name | Type | Description | Labels | -| ------------------------- | --------- | ----------------------------------------------- | ------ | -| `dotnet_exceptions_total` | `Counter` | Count of exceptions thrown, broken down by type | | - -## `.WithGcStats()` - -Include metrics recording the frequency and duration of garbage collections\/ pauses, heap sizes and - volume of allocations. - -### `CaptureLevel.Counters` - -| Name | Type | Description | Labels | -| ---------------------------------------- | --------- | ---------------------------------------------------------------------------------------------- | --------------- | -| `dotnet_gc_allocated_bytes_total` | `Counter` | The total number of bytes allocated on the managed heap | | -| `dotnet_gc_memory_total_available_bytes` | `Gauge` | The upper limit on the amount of physical memory .NET can allocate to | | -| `dotnet_gc_pause_ratio` | `Gauge` | The percentage of time the process spent paused for garbage collection | | -| `dotnet_gc_collection_count_total` | `Counter` | Counts the number of garbage collections that have occurred, broken down by generation number. | `gc_generation` | -| `dotnet_gc_heap_size_bytes` | `Gauge` | The current size of all heaps (only updated after a garbage collection) | `gc_generation` | - -### `CaptureLevel.Informational` - -Includes metrics generated by `CaptureLevel.Counters` plus: - -| Name | Type | Description | Labels | -| ------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -| `dotnet_gc_pause_seconds` | `Histogram` | The amount of time execution was paused for garbage collection | | -| `dotnet_gc_pinned_objects` | `Gauge` | The number of pinned objects | | -| `dotnet_gc_collection_seconds` | `Histogram` | The amount of time spent running garbage collections | `gc_generation`, `gc_type` | -| `dotnet_gc_collection_count_total` | `Counter` | Counts the number of garbage collections that have occurred, broken down by generation number and the reason for the collection. | `gc_generation`, `gc_reason` | -| `dotnet_gc_cpu_ratio` | `Gauge` | The percentage of process CPU time spent running garbage collections | | -| `dotnet_gc_finalization_queue_length` | `Gauge` | The number of objects waiting to be finalized | | - -### `CaptureLevel.Verbose` - -Includes metrics generated by `CaptureLevel.Counters`, `CaptureLevel.Informational` plus: - -| Name | Type | Description | Labels | -| --------------------------------- | --------- | ------------------------------------------------------- | --------- | -| `dotnet_gc_allocated_bytes_total` | `Counter` | The total number of bytes allocated on the managed heap | `gc_heap` | - -## `.WithContentionStats()` - -Include metrics around volume of locks contended. - -### `CaptureLevel.Counters` - -| Name | Type | Description | Labels | -| ------------------------- | --------- | ----------------------------- | ------ | -| `dotnet_contention_total` | `Counter` | The number of locks contended | | - -### `CaptureLevel.Informational` - -Includes metrics generated by `CaptureLevel.Counters` plus: - -| Name | Type | Description | Labels | -| --------------------------------- | --------- | ----------------------------------------------- | ------ | -| `dotnet_contention_seconds_total` | `Counter` | The total amount of time spent contending locks | | - -## `.WithThreadPoolStats()` - -Include metrics around the size of the worker and IO thread pools and reasons - for worker thread pool changes. - -### `CaptureLevel.Counters` - -| Name | Type | Description | Labels | -| ------------------------------------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------- | ------ | -| `dotnet_threadpool_queue_length` | `Histogram` | Measures the queue length of the thread pool. Values greater than 0 indicate a backlog of work for the threadpool to process. | | -| `dotnet_threadpool_throughput_total` | `Counter` | The total number of work items that have finished execution in the thread pool | | -| `dotnet_threadpool_timer_count` | `Gauge` | The number of timers active | | -| `dotnet_threadpool_num_threads` | `Gauge` | The number of active threads in the thread pool | | - -### `CaptureLevel.Informational` - -Includes metrics generated by `CaptureLevel.Counters` plus: - -| Name | Type | Description | Labels | -| ------------------------------------- | --------- | ------------------------------------------------------------------------------------------------- | ------------------- | -| `dotnet_threadpool_io_num_threads` | `Gauge` | The number of active threads in the IO thread pool | | -| `dotnet_threadpool_adjustments_total` | `Counter` | The total number of changes made to the size of the thread pool, labeled by the reason for change | `adjustment_reason` | - -## `.WithJitStats()` - -Include metrics summarizing the volume of methods being compiled - by the Just\-In\-Time compiler. - -### `CaptureLevel.Verbose` - -| Name | Type | Description | Labels | -| --------------------------------- | --------- | ---------------------------------------------------- | --------- | -| `dotnet_jit_method_seconds_total` | `Counter` | Total number of seconds spent in the JIT compiler | `dynamic` | -| `dotnet_jit_cpu_ratio` | `Gauge` | The amount of total CPU time consumed spent JIT'ing | | -| `dotnet_jit_method_total` | `Counter` | Total number of methods compiled by the JIT compiler | `dynamic` | +The documents below catalog the metrics exposed, depending on the .NET runtime version an application is using: +- [.NET 5.0](./metrics-exposed-5.0.md) +- [.NET core 3.1](./metrics-exposed-3.1.md) diff --git a/examples/AspNetCoreExample/AspNetCoreExample.csproj b/examples/AspNetCoreExample/AspNetCoreExample.csproj index f51d0fc..9a2eecd 100644 --- a/examples/AspNetCoreExample/AspNetCoreExample.csproj +++ b/examples/AspNetCoreExample/AspNetCoreExample.csproj @@ -1,6 +1,6 @@  - netcoreapp3.1 + net5.0 8 diff --git a/examples/AspNetCoreExample/Dockerfile b/examples/AspNetCoreExample/Dockerfile index 9056391..051cf9f 100644 --- a/examples/AspNetCoreExample/Dockerfile +++ b/examples/AspNetCoreExample/Dockerfile @@ -1,13 +1,12 @@ -# FROM mcr.microsoft.com/dotnet/core/sdk:3.1.201 AS build -FROM mcr.microsoft.com/dotnet/core/sdk:3.1.406 AS build -#FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build +#FROM mcr.microsoft.com/dotnet/core/sdk:3.1.406 AS build +FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build WORKDIR /src COPY . . RUN dotnet publish "examples/AspNetCoreExample" -c Release -o /app #FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.3 AS final -FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.10 AS final -#FROM mcr.microsoft.com/dotnet/aspnet:5.0 as final +#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.10 AS final +FROM mcr.microsoft.com/dotnet/aspnet:5.0 as final WORKDIR /app COPY --from=build /app /app ENTRYPOINT ["dotnet", "AspNetCoreExample.dll"] diff --git a/src/prometheus-net.DotNetRuntime.Tests/DotNetRuntimeStatsBuilderTests.cs b/src/prometheus-net.DotNetRuntime.Tests/DotNetRuntimeStatsBuilderTests.cs index 4033768..3a81475 100644 --- a/src/prometheus-net.DotNetRuntime.Tests/DotNetRuntimeStatsBuilderTests.cs +++ b/src/prometheus-net.DotNetRuntime.Tests/DotNetRuntimeStatsBuilderTests.cs @@ -135,7 +135,7 @@ public void RegisterDefaultConsumers_Can_Register_Default_Consumers_For_All_Pars Assert.That(sp.GetService>, Is.Not.Null); Assert.That(sp.GetService>, Is.Not.Null); - Assert.That(sp.GetService>, Is.Not.Null); + Assert.That(sp.GetService>, Is.Not.Null); } [Test] diff --git a/src/prometheus-net.DotNetRuntime.Tests/EventListening/EventParserTypes.cs b/src/prometheus-net.DotNetRuntime.Tests/EventListening/EventParserTypes.cs index 0a010f5..b6292ec 100644 --- a/src/prometheus-net.DotNetRuntime.Tests/EventListening/EventParserTypes.cs +++ b/src/prometheus-net.DotNetRuntime.Tests/EventListening/EventParserTypes.cs @@ -57,6 +57,26 @@ public void When_Calling_GetEventParsers_Then_Returns_All_Event_Parsers_Defined_ typeof(ExceptionEventParser) })); } + +#if NETCOREAPP3_1 + + [Test] + public void When_Calling_GetEventInterfacesForCurrentRuntime_On_Net31_Then_Returns_Interfaces_For_Net31_Runtime_And_Below() + { + var interfaces = EventParserTypes.GetEventInterfacesForCurrentRuntime(typeof(VersionedEvents), EventLevel.LogAlways); + Assert.That(interfaces, Is.EquivalentTo(new [] { typeof(VersionedEvents.Events.Counters), typeof(VersionedEvents.Events.CountersV3_1) })); + } +#endif + +#if NET5_0 + + [Test] + public void When_Calling_GetEventInterfacesForCurrentRuntime_On_Net50_Then_Returns_Interfaces_For_Net50_Runtime_And_Below() + { + var interfaces = EventParserTypes.GetEventInterfacesForCurrentRuntime(typeof(VersionedEvents), EventLevel.LogAlways); + Assert.That(interfaces, Is.EquivalentTo(new [] { typeof(VersionedEvents.Events.Counters), typeof(VersionedEvents.Events.CountersV3_1), typeof(VersionedEvents.Events.CountersV5_0) })); + } +#endif public class AllEvents : AllEvents.Events.Verbose, AllEvents.Events.Info, AllEvents.Events.Warning, AllEvents.Events.Error, AllEvents.Events.Always, AllEvents.Events.Counters, AllEvents.Events.Critical, IEvents { @@ -69,6 +89,18 @@ public interface Error : IErrorEvents{} public interface Always : IAlwaysEvents{} public interface Critical : ICriticalEvents{} public interface Counters : ICounterEvents{} + public interface CountersV3_1 : ICounterEvents{} + public interface CountersV5_0 : ICounterEvents{} + } + } + + public class VersionedEvents : VersionedEvents.Events.Counters, VersionedEvents.Events.CountersV3_1, VersionedEvents.Events.CountersV5_0 + { + public static class Events + { + public interface Counters : ICounterEvents{} + public interface CountersV3_1 : ICounterEvents{} + public interface CountersV5_0 : ICounterEvents{} } } } diff --git a/src/prometheus-net.DotNetRuntime.Tests/IntegrationTests/Helpers.cs b/src/prometheus-net.DotNetRuntime.Tests/IntegrationTests/Helpers.cs new file mode 100644 index 0000000..43ef776 --- /dev/null +++ b/src/prometheus-net.DotNetRuntime.Tests/IntegrationTests/Helpers.cs @@ -0,0 +1,16 @@ +using System; +using System.Linq.Expressions; + +namespace Prometheus.DotNetRuntime.Tests.IntegrationTests +{ + public static class RuntimeEventHelper + { + public static void CompileMethods(Expression> toCompile, int times = 100) + { + for (int i = 0; i < 100; i++) + { + toCompile.Compile(); + } + } + } +} \ No newline at end of file diff --git a/src/prometheus-net.DotNetRuntime.Tests/IntegrationTests/JitCompilerTests.cs b/src/prometheus-net.DotNetRuntime.Tests/IntegrationTests/JitCompilerTests.cs index 56f1d5c..0fbd5ed 100644 --- a/src/prometheus-net.DotNetRuntime.Tests/IntegrationTests/JitCompilerTests.cs +++ b/src/prometheus-net.DotNetRuntime.Tests/IntegrationTests/JitCompilerTests.cs @@ -12,7 +12,7 @@ internal class Given_A_JitStatsCollector_That_Samples_Every_Jit_Event : Integrat { protected override DotNetRuntimeStatsBuilder.Builder ConfigureBuilder(DotNetRuntimeStatsBuilder.Builder toConfigure) { - return toConfigure.WithJitStats(SampleEvery.OneEvent); + return toConfigure.WithJitStats(CaptureLevel.Verbose, SampleEvery.OneEvent); } [Test] @@ -74,7 +74,7 @@ internal class Given_A_JitStatsCollector_That_Samples_Every_Fifth_Jit_Event : In { protected override DotNetRuntimeStatsBuilder.Builder ConfigureBuilder(DotNetRuntimeStatsBuilder.Builder toConfigure) { - return toConfigure.WithJitStats(SampleEvery.FiveEvents); + return toConfigure.WithJitStats(CaptureLevel.Verbose, SampleEvery.FiveEvents); } [Test] @@ -86,20 +86,49 @@ public void When_many_methods_are_jitted_then_their_compilation_is_measured() // act var sp = Stopwatch.StartNew(); - Compile100Methods(() => 1); + RuntimeEventHelper.CompileMethods(() => 1, 100); sp.Stop(); // assert Assert.That(() => MetricProducer.MethodsJittedTotal.Labels("true").Value, Is.GreaterThanOrEqualTo(methodsJitted + 20).After(100, 10)); Assert.That(MetricProducer.MethodsJittedSecondsTotal.Labels("true").Value, Is.GreaterThan(methodsJittedSeconds + sp.Elapsed.TotalSeconds).Within(0.1)); } + } + + internal class Given_Only_Counters_Are_Enabled_For_JitStats : IntegrationTestBase + { + protected override DotNetRuntimeStatsBuilder.Builder ConfigureBuilder(DotNetRuntimeStatsBuilder.Builder toConfigure) + { + return toConfigure.WithJitStats(CaptureLevel.Counters, SampleEvery.OneEvent); + } + +#if NET5_0 + + [Test] + public void When_Running_On_NET50_Then_Counts_Of_Methods_Are_Recorded() + { + // arrage + var methodsJittedPrevious = MetricProducer.MethodsJittedTotal.Value; + var bytesJittedPrevious = MetricProducer.BytesJitted.Value; + + // act + RuntimeEventHelper.CompileMethods(() => 1, 100); + + Assert.That(MetricProducer.BytesJitted, Is.Not.Null); + Assert.That(MetricProducer.MethodsJittedTotal, Is.Not.Null); + Assert.That(() => MetricProducer.BytesJitted.Value, Is.GreaterThan(bytesJittedPrevious).After(2_000, 10)); + Assert.That(() => MetricProducer.MethodsJittedTotal.Value, Is.GreaterThan(methodsJittedPrevious + 100).After(2_000, 10)); + } +#endif + +#if NETCOREAPP3_1 - private void Compile100Methods(Expression> toCompile) + [Test] + public void When_Running_On_NETCOREAPP31_Then_No_Metrics_Are_Available() { - for (int i = 0; i < 100; i++) - { - toCompile.Compile(); - } + Assert.That(MetricProducer.BytesJitted, Is.Null); + Assert.That(MetricProducer.MethodsJittedTotal, Is.Null); } +#endif } } \ No newline at end of file diff --git a/src/prometheus-net.DotNetRuntime/DotNetRuntimeStatsBuilder.cs b/src/prometheus-net.DotNetRuntime/DotNetRuntimeStatsBuilder.cs index 9667821..76c8f4b 100644 --- a/src/prometheus-net.DotNetRuntime/DotNetRuntimeStatsBuilder.cs +++ b/src/prometheus-net.DotNetRuntime/DotNetRuntimeStatsBuilder.cs @@ -28,6 +28,7 @@ public static Builder Default() .WithContentionStats() .WithThreadPoolStats() .WithGcStats() + .WithJitStats() .WithExceptionStats(); } @@ -131,15 +132,18 @@ public Builder WithContentionStats(CaptureLevel level = CaptureLevel.Counters, S /// Include metrics summarizing the volume of methods being compiled /// by the Just-In-Time compiler. /// + /// /// /// The sampling rate for JIT events. A lower sampling rate reduces memory use /// but reduces the accuracy of metrics produced (as a percentage of events are discarded). /// If your application achieves a high level of throughput (thousands of work items scheduled per second on /// the thread pool), it's recommend to reduce the sampling rate even further. /// - public Builder WithJitStats(SampleEvery sampleRate = SampleEvery.TenEvents) + public Builder WithJitStats(CaptureLevel captureLevel = CaptureLevel.Counters, SampleEvery sampleRate = SampleEvery.TenEvents) { - ListenerRegistrations.AddOrReplace(ListenerRegistration.Create(CaptureLevel.Verbose, sp => new JitEventParser(sampleRate))); + if (captureLevel != CaptureLevel.Counters) + ListenerRegistrations.AddOrReplace(ListenerRegistration.Create(CaptureLevel.Verbose, sp => new JitEventParser(sampleRate))); + _services.TryAddSingletonEnumerable(); return this; diff --git a/src/prometheus-net.DotNetRuntime/EventListening/EventParserTypes.cs b/src/prometheus-net.DotNetRuntime/EventListening/EventParserTypes.cs index 7e8ad55..7dfeedd 100644 --- a/src/prometheus-net.DotNetRuntime/EventListening/EventParserTypes.cs +++ b/src/prometheus-net.DotNetRuntime/EventListening/EventParserTypes.cs @@ -4,6 +4,8 @@ using System.Diagnostics.Tracing; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; namespace Prometheus.DotNetRuntime.EventListening { @@ -33,6 +35,12 @@ internal static IEnumerable GetEventInterfaces(Type t, EventLevel atLevelA .Where(t => GetEventLevel(t) <= atLevelAndBelow); } + internal static IEnumerable GetEventInterfacesForCurrentRuntime(Type t, EventLevel atLevelAndBelow) + { + return GetEventInterfaces(t, atLevelAndBelow) + .Where(AreEventsSupportedByRuntime); + } + internal static ImmutableHashSet GetLevelsFromParser(Type type) { return GetEventInterfaces(type) @@ -75,5 +83,43 @@ internal static IEnumerable GetEventParsers(Assembly fromAssembly) .GetTypes() .Where(x => x.IsClass && x.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEventParser<>))); } + + internal static Lazy CurrentRuntimeVerison = new Lazy(() => + { + var split = RuntimeInformation.FrameworkDescription.Split(' '); + + if (Version.TryParse(split[split.Length - 1], out var version)) + return new Version(version.Major, version.Minor); + + return null; + }); + + internal static bool AreEventsSupportedByRuntime(Type type) + { + var eventVer = GetVersionOfEvents(type); + + if (CurrentRuntimeVerison.Value == null) + // Assume if this is being run, it's on .net core 3.1+ + return eventVer == LowestSupportedVersion; + + return eventVer <= CurrentRuntimeVerison.Value; + } + + + private static readonly Version LowestSupportedVersion = new Version(3, 1); + private static readonly Regex VersionRegex = new Regex("V(?[0-9]+)_(?[0-9]+)", RegexOptions.Compiled); + private static Version GetVersionOfEvents(Type type) + { + if (!typeof(IEvents).IsAssignableFrom(type)) + throw new ArgumentException($"Type {type} does not implement {nameof(IEvents)}"); + + var match = VersionRegex.Match(type.Name); + + if (match == null || !match.Success) + // Defaults to 3.0 (haven't converted all existed interfaces into type interfaces) + return new Version(3, 0); + + return new Version(int.Parse(match.Groups["major"].Value), int.Parse(match.Groups["minor"].Value)); + } } } \ No newline at end of file diff --git a/src/prometheus-net.DotNetRuntime/EventListening/Parsers/RuntimeEventParser.cs b/src/prometheus-net.DotNetRuntime/EventListening/Parsers/RuntimeEventParser.cs index f7cf9de..0e89a4f 100644 --- a/src/prometheus-net.DotNetRuntime/EventListening/Parsers/RuntimeEventParser.cs +++ b/src/prometheus-net.DotNetRuntime/EventListening/Parsers/RuntimeEventParser.cs @@ -6,7 +6,9 @@ namespace Prometheus.DotNetRuntime.EventListening.Parsers { - public class RuntimeEventParser : EventCounterParserBase, RuntimeEventParser.Events.Counters + public class RuntimeEventParser : EventCounterParserBase, + RuntimeEventParser.Events.CountersV3_0, + RuntimeEventParser.Events.CountersV5_0 { #pragma warning disable CS0067 [CounterName("threadpool-thread-count")] @@ -65,7 +67,7 @@ public class RuntimeEventParser : EventCounterParserBase, Ru public static class Events { - public interface Counters : ICounterEvents + public interface CountersV3_0 : ICounterEvents { event Action ThreadPoolThreadCount; event Action ThreadPoolQueueLength; @@ -74,8 +76,6 @@ public interface Counters : ICounterEvents event Action ActiveTimerCount; event Action ExceptionCount; event Action NumAssembliesLoaded; - event Action IlBytesJitted; - event Action MethodsJittedCount; event Action AllocRate; event Action GcHeapSize; event Action Gen0GcCount; @@ -87,6 +87,12 @@ public interface Counters : ICounterEvents event Action Gen2Size; event Action LohSize; } + + public interface CountersV5_0 : ICounterEvents + { + event Action IlBytesJitted; + event Action MethodsJittedCount; + } } } } \ No newline at end of file diff --git a/src/prometheus-net.DotNetRuntime/ListenerRegistration.cs b/src/prometheus-net.DotNetRuntime/ListenerRegistration.cs index 7866fdd..a5b21f3 100644 --- a/src/prometheus-net.DotNetRuntime/ListenerRegistration.cs +++ b/src/prometheus-net.DotNetRuntime/ListenerRegistration.cs @@ -36,7 +36,7 @@ internal void RegisterServices(IServiceCollection services) services.AddSingleton(typeof(IEventListener), sp => sp.GetService(Type)); // Register each events interface exposed at the level specified - foreach (var i in EventParserTypes.GetEventInterfaces(Type, Level)) + foreach (var i in EventParserTypes.GetEventInterfacesForCurrentRuntime(Type, Level)) services.AddSingleton(i, sp => sp.GetService(Type)); } diff --git a/src/prometheus-net.DotNetRuntime/Metrics/Producers/ContentionMetricsProducer.cs b/src/prometheus-net.DotNetRuntime/Metrics/Producers/ContentionMetricsProducer.cs index 3850d98..65f852a 100644 --- a/src/prometheus-net.DotNetRuntime/Metrics/Producers/ContentionMetricsProducer.cs +++ b/src/prometheus-net.DotNetRuntime/Metrics/Producers/ContentionMetricsProducer.cs @@ -5,9 +5,9 @@ namespace Prometheus.DotNetRuntime.Metrics.Producers public class ContentionMetricsProducer : IMetricProducer { private readonly Consumes _contentionInfo; - private readonly Consumes _runtimeCounters; + private readonly Consumes _runtimeCounters; - public ContentionMetricsProducer(Consumes contentionInfo, Consumes runtimeCounters) + public ContentionMetricsProducer(Consumes contentionInfo, Consumes runtimeCounters) { _contentionInfo = contentionInfo; _runtimeCounters = runtimeCounters; diff --git a/src/prometheus-net.DotNetRuntime/Metrics/Producers/ExceptionMetricsProducer.cs b/src/prometheus-net.DotNetRuntime/Metrics/Producers/ExceptionMetricsProducer.cs index 4f2c791..6422a15 100644 --- a/src/prometheus-net.DotNetRuntime/Metrics/Producers/ExceptionMetricsProducer.cs +++ b/src/prometheus-net.DotNetRuntime/Metrics/Producers/ExceptionMetricsProducer.cs @@ -5,10 +5,10 @@ namespace Prometheus.DotNetRuntime.Metrics.Producers public class ExceptionMetricsProducer : IMetricProducer { private readonly Consumes _exceptionError; - private readonly Consumes _runtimeCounters; + private readonly Consumes _runtimeCounters; private const string LabelType = "type"; - public ExceptionMetricsProducer(Consumes exceptionError, Consumes runtimeCounters) + public ExceptionMetricsProducer(Consumes exceptionError, Consumes runtimeCounters) { _exceptionError = exceptionError; _runtimeCounters = runtimeCounters; diff --git a/src/prometheus-net.DotNetRuntime/Metrics/Producers/GcMetricsProducer.cs b/src/prometheus-net.DotNetRuntime/Metrics/Producers/GcMetricsProducer.cs index 09f892b..402ef7b 100644 --- a/src/prometheus-net.DotNetRuntime/Metrics/Producers/GcMetricsProducer.cs +++ b/src/prometheus-net.DotNetRuntime/Metrics/Producers/GcMetricsProducer.cs @@ -21,16 +21,16 @@ private const string private readonly Consumes _gcInfo; private readonly Consumes _gcVerbose; - private readonly Consumes _runtimeCounters; + private readonly Consumes _runtimeCounters; private readonly Ratio _gcCpuRatio = Ratio.ProcessTotalCpu(); private readonly Ratio _gcPauseRatio = Ratio.ProcessTime(); - private Options _options; + private readonly Options _options; public GcMetricsProducer( Options options, Consumes gcInfo, Consumes gcVerbose, - Consumes runtimeCounters) + Consumes runtimeCounters) { _options = options; _gcInfo = gcInfo; diff --git a/src/prometheus-net.DotNetRuntime/Metrics/Producers/JitMetricsProducer.cs b/src/prometheus-net.DotNetRuntime/Metrics/Producers/JitMetricsProducer.cs index 96a528c..8d4860d 100644 --- a/src/prometheus-net.DotNetRuntime/Metrics/Producers/JitMetricsProducer.cs +++ b/src/prometheus-net.DotNetRuntime/Metrics/Producers/JitMetricsProducer.cs @@ -10,36 +10,53 @@ public class JitMetricsProducer : IMetricProducer private const string LabelValueFalse = "false"; private readonly Consumes _jitVerbose; + private readonly Consumes _runtimeCounters; private readonly Ratio _jitCpuRatio = Ratio.ProcessTotalCpu(); - public JitMetricsProducer(Consumes jitVerbose) + public JitMetricsProducer(Consumes jitVerbose, Consumes runtimeCounters) { _jitVerbose = jitVerbose; + _runtimeCounters = runtimeCounters; } internal Counter MethodsJittedTotal { get; private set; } internal Counter MethodsJittedSecondsTotal { get; private set; } + internal Gauge BytesJitted { get; private set; } internal Gauge CpuRatio { get; private set; } public void RegisterMetrics(MetricFactory metrics) { - if (!_jitVerbose.Enabled) + if (!_jitVerbose.Enabled && !_runtimeCounters.Enabled) return; - MethodsJittedTotal = metrics.CreateCounter("dotnet_jit_method_total", "Total number of methods compiled by the JIT compiler", DynamicLabel); - MethodsJittedSecondsTotal = metrics.CreateCounter("dotnet_jit_method_seconds_total", "Total number of seconds spent in the JIT compiler", DynamicLabel); - _jitVerbose.Events.CompilationComplete += e => + if (_runtimeCounters.Enabled) { - MethodsJittedTotal.Labels(e.IsMethodDynamic.ToLabel()).Inc(); - MethodsJittedSecondsTotal.Labels(e.IsMethodDynamic.ToLabel()).Inc(e.CompilationDuration.TotalSeconds); - }; - - CpuRatio = metrics.CreateGauge("dotnet_jit_cpu_ratio", "The amount of total CPU time consumed spent JIT'ing"); + BytesJitted = metrics.CreateGauge("dotnet_jit_il_bytes", "Total bytes of IL compiled by the JIT compiler"); + _runtimeCounters.Events.IlBytesJitted += e => BytesJitted.Set(e.Mean); + } + + if (_jitVerbose.Enabled) + { + MethodsJittedTotal = metrics.CreateCounter("dotnet_jit_method_total", "Total number of methods compiled by the JIT compiler, broken down by compilation for dynamic code", DynamicLabel); + MethodsJittedSecondsTotal = metrics.CreateCounter("dotnet_jit_method_seconds_total", "Total number of seconds spent in the JIT compiler, broken down by compilation for dynamic code", DynamicLabel); + _jitVerbose.Events.CompilationComplete += e => + { + MethodsJittedTotal.Labels(e.IsMethodDynamic.ToLabel()).Inc(); + MethodsJittedSecondsTotal.Labels(e.IsMethodDynamic.ToLabel()).Inc(e.CompilationDuration.TotalSeconds); + }; + + CpuRatio = metrics.CreateGauge("dotnet_jit_cpu_ratio", "The amount of total CPU time consumed spent JIT'ing"); + } + else + { + MethodsJittedTotal = metrics.CreateCounter("dotnet_jit_method_total", "Total number of methods compiled by the JIT compiler"); + _runtimeCounters.Events.MethodsJittedCount += e => MethodsJittedTotal.Inc(e.Mean - MethodsJittedTotal.Value); + } } public void UpdateMetrics() { - CpuRatio.Set(_jitCpuRatio.CalculateConsumedRatio(MethodsJittedSecondsTotal)); + CpuRatio?.Set(_jitCpuRatio.CalculateConsumedRatio(MethodsJittedSecondsTotal)); } } } \ No newline at end of file diff --git a/src/prometheus-net.DotNetRuntime/Metrics/Producers/ThreadPoolMetricsProducer.cs b/src/prometheus-net.DotNetRuntime/Metrics/Producers/ThreadPoolMetricsProducer.cs index 5c15b70..bbc3e91 100644 --- a/src/prometheus-net.DotNetRuntime/Metrics/Producers/ThreadPoolMetricsProducer.cs +++ b/src/prometheus-net.DotNetRuntime/Metrics/Producers/ThreadPoolMetricsProducer.cs @@ -12,9 +12,9 @@ public class ThreadPoolMetricsProducer : IMetricProducer private readonly Dictionary _adjustmentReasonToLabel = LabelGenerator.MapEnumToLabelValues(); private readonly Options _options; private readonly Consumes _threadPoolInfo; - private readonly Consumes _runtimeCounters; + private readonly Consumes _runtimeCounters; - public ThreadPoolMetricsProducer(Options options, Consumes threadPoolInfo, Consumes runtimeCounters) + public ThreadPoolMetricsProducer(Options options, Consumes threadPoolInfo, Consumes runtimeCounters) { _options = options; _threadPoolInfo = threadPoolInfo; diff --git a/tools/DocsGenerator/DocsGenerator.csproj b/tools/DocsGenerator/DocsGenerator.csproj index 9e3c91b..b2935fc 100644 --- a/tools/DocsGenerator/DocsGenerator.csproj +++ b/tools/DocsGenerator/DocsGenerator.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + netcoreapp3.1;net5.0 9 diff --git a/tools/DocsGenerator/Program.cs b/tools/DocsGenerator/Program.cs index 5d7e110..05c8a5a 100644 --- a/tools/DocsGenerator/Program.cs +++ b/tools/DocsGenerator/Program.cs @@ -12,6 +12,7 @@ using Grynwald.MarkdownGenerator; using Prometheus; using Prometheus.DotNetRuntime; +using Prometheus.DotNetRuntime.EventListening; using Prometheus.DotNetRuntime.Metrics.Producers; namespace DocsGenerator @@ -20,6 +21,7 @@ class Program { static async Task Main(string[] args) { + // TODO output different path depending on runtime version var sources = new [] { SourceAndConfig.CreateFrom(b => b.WithThreadPoolStats(CaptureLevel.Counters, new ThreadPoolMetricsProducer.Options())), @@ -31,7 +33,9 @@ static async Task Main(string[] args) SourceAndConfig.CreateFrom(b => b.WithContentionStats(CaptureLevel.Informational, SampleEvery.OneEvent)), SourceAndConfig.CreateFrom(b => b.WithExceptionStats(CaptureLevel.Counters)), SourceAndConfig.CreateFrom(b => b.WithExceptionStats(CaptureLevel.Errors)), - new SourceAndConfig(new Source(typeof(DotNetRuntimeStatsBuilder.Builder).GetMethod(nameof(DotNetRuntimeStatsBuilder.Builder.WithJitStats)), CaptureLevel.Verbose), b => b.WithJitStats(SampleEvery.OneEvent)) + SourceAndConfig.CreateFrom(b => b.WithJitStats(CaptureLevel.Counters, SampleEvery.OneEvent)), + SourceAndConfig.CreateFrom(b => b.WithJitStats(CaptureLevel.Verbose, SampleEvery.OneEvent)), + SourceAndConfig.CreateFrom(b => b.WithExceptionStats(CaptureLevel.Errors)) }; var assemblyDocs = typeof(DotNetRuntimeStatsBuilder).Assembly.LoadXmlDocumentation(); @@ -53,8 +57,8 @@ MdSpan[] GetCells(Collector m) var document = new MdDocument(); var root = document.Root; - root.Add(new MdHeading("Metrics exposed", 1)); - root.Add(new MdParagraph(new MdRawMarkdownSpan($"A breakdown of all the metrics exposed by this library. Each subheading details the metrics produced by calling builder methods with the specified `{nameof(CaptureLevel)}`."))); + root.Add(new MdHeading(new MdRawMarkdownSpan($"`.net {EventParserTypes.CurrentRuntimeVerison.Value}` metrics"), 1)); + root.Add(new MdParagraph(new MdRawMarkdownSpan($"Each subheading details the metrics produced by calling builder methods with the specified `{nameof(CaptureLevel)}`."))); root.Add(new MdHeading("Default metrics", 2)); root.Add(new MdParagraph("Metrics that are included by default, regardless of what stats collectors are enabled.")); @@ -70,6 +74,9 @@ MdSpan[] GetCells(Collector m) for (var i = 0; i < methodAndSources.sources.Length; i++) { var s = methodAndSources.sources[i]; + if (allMetrics.SourceToMetrics[s].Count == 0) + continue; + root.Add(new MdHeading(new MdCodeSpan($"{nameof(CaptureLevel)}." + s.Level), 3)); var previousLevels = methodAndSources.sources.Take(i).ToArray(); @@ -87,7 +94,7 @@ MdSpan[] GetCells(Collector m) } - document.Save("../../../../../docs/metrics-exposed.md"); + document.Save($"../../../../../docs/metrics-exposed-{EventParserTypes.CurrentRuntimeVerison.Value}.md"); } private static GroupedMetrics GetAllMetrics(SourceAndConfig[] sources)