-
Notifications
You must be signed in to change notification settings - Fork 282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Runtime instrumentation #207
Changes from 18 commits
0ce19d6
b37d5c8
70e56c0
10887a2
3033276
7c101bd
e5b550b
9a806fa
e55e8eb
0a0eb3b
93b5323
e01714e
cc270ca
fc8360a
28ad40e
7293304
542a3d5
9056811
da17698
69ec265
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
name: Pack OpenTelemetry.Contrib.Instrumentation.Runtime | ||
|
||
on: | ||
workflow_dispatch: | ||
inputs: | ||
logLevel: | ||
description: 'Log level' | ||
required: true | ||
default: 'warning' | ||
push: | ||
tags: | ||
- 'Instrumentation.Runtime-*' # trigger when we create a tag with prefix "Instrumentation.Runtime-" | ||
|
||
jobs: | ||
build-test-pack: | ||
runs-on: ${{ matrix.os }} | ||
env: | ||
PROJECT: OpenTelemetry.Contrib.Instrumentation.Runtime | ||
|
||
strategy: | ||
matrix: | ||
os: [windows-latest] | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
with: | ||
fetch-depth: 0 # fetching all | ||
|
||
- name: Install dependencies | ||
run: dotnet restore | ||
|
||
- name: dotnet build ${{env.PROJECT}} | ||
run: dotnet build src/${{env.PROJECT}} --configuration Release --no-restore -p:Deterministic=true | ||
|
||
- name: dotnet test ${{env.PROJECT}} | ||
run: dotnet test test/${{env.PROJECT}}.Tests | ||
|
||
- name: dotnet pack ${{env.PROJECT}} | ||
run: dotnet pack src/${{env.PROJECT}} --configuration Release --no-build | ||
|
||
- name: Publish Artifacts | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: ${{env.PROJECT}}-packages | ||
path: '**/${{env.PROJECT}}/bin/**/*.*nupkg' | ||
|
||
- name: Publish Nuget | ||
run: | | ||
nuget push **/${{env.PROJECT}}/bin/**/*.nupkg -Source https://api.nuget.org/v3/index.json -ApiKey ${{ secrets.NUGET_TOKEN }} -SymbolApiKey ${{ secrets.NUGET_TOKEN }} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// <copyright file="AssemblyInfo.cs" company="OpenTelemetry Authors"> | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// </copyright> | ||
|
||
using System.Runtime.CompilerServices; | ||
|
||
#if SIGNED | ||
[assembly: InternalsVisibleTo("OpenTelemetry.Contrib.Instrumentation.Runtime.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] | ||
#else | ||
[assembly: InternalsVisibleTo("OpenTelemetry.Contrib.Instrumentation.Runtime.Tests")] | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// <copyright file="MeterProviderBuilderExtensions.cs" company="OpenTelemetry Authors"> | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// </copyright> | ||
|
||
using System; | ||
using OpenTelemetry.Contrib.Instrumentation.Runtime; | ||
|
||
namespace OpenTelemetry.Metrics | ||
{ | ||
/// <summary> | ||
/// Extension methods to simplify registering of dependency instrumentation. | ||
/// </summary> | ||
public static class MeterProviderBuilderExtensions | ||
{ | ||
/// <summary> | ||
/// Enables runtime instrumentation. | ||
/// </summary> | ||
/// <param name="builder"><see cref="MeterProviderBuilder"/> being configured.</param> | ||
/// <param name="configure">Runtime metrics options.</param> | ||
/// <returns>The instance of <see cref="MeterProviderBuilder"/> to chain the calls.</returns> | ||
public static MeterProviderBuilder AddRuntimeMetrics( | ||
this MeterProviderBuilder builder, | ||
Action<RuntimeMetricsOptions> configure = null) | ||
{ | ||
if (builder == null) | ||
{ | ||
throw new ArgumentNullException(nameof(builder)); | ||
} | ||
|
||
var options = new RuntimeMetricsOptions(); | ||
configure?.Invoke(options); | ||
|
||
var instrumentation = new RuntimeMetrics(options); | ||
builder.AddMeter(RuntimeMetrics.InstrumentationName); | ||
return builder.AddInstrumentation(() => instrumentation); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFrameworks>netstandard2.0;net461;netcoreapp3.1;net6.0</TargetFrameworks> | ||
<Description>dotnet runtime instrumentation for OpenTelemetry .NET</Description> | ||
<PackageTags>$(PackageTags);OpenTelemetry;runtime</PackageTags> | ||
<MinVerTagPrefix>Instrumentation.Runtime-</MinVerTagPrefix> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="OpenTelemetry.Api" Version="1.2.0-rc2" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# DotNet Runtime Instrumentation for OpenTelemetry .NET | ||
|
||
[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Contrib.Instrumentation.Runtime.svg)](https://www.nuget.org/packages/OpenTelemetry.Contrib.Instrumentation.Runtime) | ||
[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Contrib.Instrumentation.Runtime.svg)](https://www.nuget.org/packages/OpenTelemetry.Contrib.Instrumentation.Runtime) | ||
|
||
This is an [Instrumentation | ||
Library](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library), | ||
which instruments [.NET Runtime](https://docs.microsoft.com/dotnet) and | ||
collect telemetry about runtime behavior. | ||
|
||
## Steps to enable OpenTelemetry.Contrib.Instrumentation.Runtime | ||
|
||
### Step 1: Install Package | ||
|
||
Add a reference to the | ||
[`OpenTelemetry.Contrib.Instrumentation.Runtime`](https://www.nuget.org/packages/OpenTelemetry.Contrib.Instrumentation.Runtime) | ||
package. Also, add any other instrumentations & exporters you will need. | ||
|
||
```shell | ||
dotnet add package OpenTelemetry.Contrib.Instrumentation.Runtime | ||
``` | ||
|
||
### Step 2: Enable Runtime Instrumentation at application startup | ||
|
||
Runtime instrumentation must be enabled at application startup. This is | ||
typically done in the `ConfigureServices` of your `Startup` class. The example | ||
below enables this instrumentation by using an extension method on | ||
`IServiceCollection`. This extension method requires adding the package | ||
[`OpenTelemetry.Extensions.Hosting`](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Extensions.Hosting/README.md) | ||
to the application. This ensures the instrumentation is disposed when the host | ||
is shutdown. | ||
|
||
Additionally, this examples sets up the OpenTelemetry Prometheus exporter, which | ||
requires adding the package | ||
[`OpenTelemetry.Exporter.Prometheus`](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.Prometheus/README.md) to | ||
the application. | ||
|
||
```csharp | ||
using Microsoft.Extensions.DependencyInjection; | ||
using OpenTelemetry.Metrics; | ||
|
||
public void ConfigureServices(IServiceCollection services) | ||
{ | ||
services.AddOpenTelemetryMetrics((builder) => builder | ||
.AddRuntimeMetrics() | ||
.AddPrometheusExporter() | ||
); | ||
} | ||
``` | ||
|
||
Or configure directly: | ||
|
||
```csharp | ||
using var meterProvider = Sdk.CreateMeterProviderBuilder() | ||
.AddRuntimeMetrics() | ||
.Build(); | ||
``` | ||
|
||
## Advanced configuration | ||
|
||
By default all available runtime metrics will be added. It's also possible to | ||
specify only the required metrics: | ||
|
||
```csharp | ||
using var meterProvider = Sdk.CreateMeterProviderBuilder() | ||
.AddRuntimeMetrics(options => options | ||
{ | ||
options.GcEnabled = true; | ||
options.ThreadingEnabled = true; | ||
options.PerformanceEnabled = true; | ||
}) | ||
.Build(); | ||
``` | ||
|
||
## Troubleshooting | ||
|
||
This component uses an | ||
[EventSource](https://docs.microsoft.com/dotnet/api/system.diagnostics.tracing.eventsource) | ||
with the name "OpenTelemetry-Instrumentation-Runtime" for its internal | ||
logging. Please refer to [SDK | ||
troubleshooting](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/src/OpenTelemetry#troubleshooting) for instructions on | ||
seeing these internal logs. | ||
|
||
## References | ||
|
||
* [Introduction to ASP.NET | ||
Core](https://docs.microsoft.com/aspnet/core/introduction-to-aspnet-core) | ||
* [OpenTelemetry Project](https://opentelemetry.io/) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// <copyright file="RuntimeMetrics.cs" company="OpenTelemetry Authors"> | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// </copyright> | ||
|
||
using System; | ||
using System.Diagnostics.Metrics; | ||
using System.Reflection; | ||
using System.Threading; | ||
|
||
namespace OpenTelemetry.Contrib.Instrumentation.Runtime | ||
{ | ||
/// <summary> | ||
/// .NET runtime instrumentation. | ||
/// </summary> | ||
internal class RuntimeMetrics : IDisposable | ||
{ | ||
internal static readonly AssemblyName AssemblyName = typeof(RuntimeMetrics).Assembly.GetName(); | ||
internal static readonly string InstrumentationName = AssemblyName.Name; | ||
internal static readonly string InstrumentationVersion = AssemblyName.Version.ToString(); | ||
|
||
private readonly Meter meter; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="RuntimeMetrics"/> class. | ||
/// </summary> | ||
/// <param name="options">The options to define the metrics.</param> | ||
public RuntimeMetrics(RuntimeMetricsOptions options) | ||
{ | ||
this.meter = new Meter(InstrumentationName, InstrumentationVersion); | ||
|
||
if (options.IsGcEnabled) | ||
{ | ||
this.meter.CreateObservableGauge($"{options.MetricPrefix}gc_heap_size", () => (double)(GC.GetTotalMemory(false) / 1_000_000), "MB", "GC Heap Size"); | ||
this.meter.CreateObservableGauge($"{options.MetricPrefix}gen_0-gc_count", () => GC.CollectionCount(0), description: "Gen 0 GC Count"); | ||
this.meter.CreateObservableGauge($"{options.MetricPrefix}gen_1-gc_count", () => GC.CollectionCount(1), description: "Gen 1 GC Count"); | ||
this.meter.CreateObservableGauge($"{options.MetricPrefix}gen_2-gc_count", () => GC.CollectionCount(2), description: "Gen 2 GC Count"); | ||
#if NETCOREAPP3_1_OR_GREATER | ||
this.meter.CreateObservableCounter($"{options.MetricPrefix}alloc_rate", () => GC.GetTotalAllocatedBytes(), "B", "Allocation Rate"); | ||
this.meter.CreateObservableCounter($"{options.MetricPrefix}gc_fragmentation", GetFragmentation, description: "GC Fragmentation"); | ||
#endif | ||
|
||
#if NET6_0_OR_GREATER | ||
this.meter.CreateObservableCounter($"{options.MetricPrefix}gc_committed", () => (double)(GC.GetGCMemoryInfo().TotalCommittedBytes / 1_000_000), "MB", description: "GC Committed Bytes"); | ||
#endif | ||
} | ||
|
||
#if NET6_0_OR_GREATER | ||
if (options.IsJitEnabled) | ||
{ | ||
this.meter.CreateObservableCounter($"{options.MetricPrefix}il_bytes_jitted", () => System.Runtime.JitInfo.GetCompiledILBytes(), "B", description: "IL Bytes Jitted"); | ||
this.meter.CreateObservableCounter($"{options.MetricPrefix}methods_jitted_count", () => System.Runtime.JitInfo.GetCompiledMethodCount(), description: "Number of Methods Jitted"); | ||
this.meter.CreateObservableGauge($"{options.MetricPrefix}time_in_jit", () => System.Runtime.JitInfo.GetCompilationTime().TotalMilliseconds, "ms", description: "Time spent in JIT"); | ||
} | ||
#endif | ||
|
||
#if NETCOREAPP3_1_OR_GREATER | ||
if (options.IsThreadingEnabled) | ||
{ | ||
this.meter.CreateObservableGauge($"{options.MetricPrefix}monitor_lock_contention_count", () => Monitor.LockContentionCount, description: "Monitor Lock Contention Count"); | ||
this.meter.CreateObservableCounter($"{options.MetricPrefix}threadpool_thread_count", () => ThreadPool.ThreadCount, description: "ThreadPool Thread Count"); | ||
this.meter.CreateObservableGauge($"{options.MetricPrefix}threadpool_completed_items_count", () => ThreadPool.CompletedWorkItemCount, description: "ThreadPool Completed Work Item Count"); | ||
this.meter.CreateObservableCounter($"{options.MetricPrefix}threadpool_queue_length", () => ThreadPool.PendingWorkItemCount, description: "ThreadPool Queue Length"); | ||
this.meter.CreateObservableCounter($"{options.MetricPrefix}active_timer_count", () => Timer.ActiveCount, description: "Number of Active Timers"); | ||
} | ||
#endif | ||
|
||
if (options.IsPerformanceEnabled) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps this should be named "Memory", or "Process Memory"? "Performance" seems very broad. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This "category" is used to provide the cpu-usage as well, which was removed for the 1st version. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed later that we were planning to prefix all these metrics with "process.runtime.dotnet." and the OT semantic conventions suggest this value should be named something like "process.memory.xyz". Maybe we should leave this instrument out of this Meter and put it with a different collection of non-runtime process information? Then the category would be gone and the name wouldn't matter. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I'm not sure I understood the question? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of the OpenTelemetry semantic conventions are still at very early stages. If I understand @noahfalk correctly, I think the intention is to put .NET runtime specific things under "process.runtime.dotnet.*", and put operating system specific things under a different place that doesn't have "runtime.dotnet" in the name. In other words, we need to ask ourselves - would this metric also apply to C++, if yes, it should not have "dotnet" in the name. |
||
{ | ||
this.meter.CreateObservableGauge($"{options.MetricPrefix}working_set", () => (double)(Environment.WorkingSet / 1_000_000), "MB", "Working Set"); | ||
} | ||
|
||
if (options.IsAssembliesEnabled) | ||
{ | ||
this.meter.CreateObservableCounter($"{options.MetricPrefix}assembly_count", () => AppDomain.CurrentDomain.GetAssemblies().Length, description: "Number of Assemblies Loaded"); | ||
} | ||
} | ||
|
||
/// <inheritdoc/> | ||
public void Dispose() | ||
{ | ||
this.meter?.Dispose(); | ||
} | ||
|
||
#if NETCOREAPP3_1_OR_GREATER | ||
private static double GetFragmentation() | ||
{ | ||
var gcInfo = GC.GetGCMemoryInfo(); | ||
return gcInfo.HeapSizeBytes != 0 ? gcInfo.FragmentedBytes * 100d / gcInfo.HeapSizeBytes : 0; | ||
} | ||
#endif | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest we use a single separator rather than a mixture of both "-" and "_".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would propose int64
process.runtime.dotnet.gc.heap
, and the unit isBytes
.For GC count, use
process.runtime.dotnet.gc.count
, and putgen
as dimension/attribute.Doesn't have to block this PR though.