Add view (configuration only) (#2396)

This commit is contained in:
Cijo Thomas 2021-09-27 20:47:08 -07:00 committed by GitHub
parent 400e463555
commit 2c70ca6da1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 360 additions and 6 deletions

View File

@ -220,6 +220,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "extending-the-sdk", "docs\m
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StressTestMetrics", "test\StressTestMetrics\StressTestMetrics.csproj", "{A885DBE2-4B82-432C-A77B-19844D7BBC96}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "customizing-the-sdk", "docs\metrics\customizing-the-sdk\customizing-the-sdk.csproj", "{81234AFA-B4E7-4D0D-AB97-FD559C78EDA2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -434,14 +436,18 @@ Global
{EA60B549-F712-4ABE-8E44-FCA83B78C06E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA60B549-F712-4ABE-8E44-FCA83B78C06E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA60B549-F712-4ABE-8E44-FCA83B78C06E}.Release|Any CPU.Build.0 = Release|Any CPU
{A885DBE2-4B82-432C-A77B-19844D7BBC96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A885DBE2-4B82-432C-A77B-19844D7BBC96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A885DBE2-4B82-432C-A77B-19844D7BBC96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A885DBE2-4B82-432C-A77B-19844D7BBC96}.Release|Any CPU.Build.0 = Release|Any CPU
{1F9D7748-D099-4E25-97F5-9C969D6FF969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F9D7748-D099-4E25-97F5-9C969D6FF969}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F9D7748-D099-4E25-97F5-9C969D6FF969}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F9D7748-D099-4E25-97F5-9C969D6FF969}.Release|Any CPU.Build.0 = Release|Any CPU
{A885DBE2-4B82-432C-A77B-19844D7BBC96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A885DBE2-4B82-432C-A77B-19844D7BBC96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A885DBE2-4B82-432C-A77B-19844D7BBC96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A885DBE2-4B82-432C-A77B-19844D7BBC96}.Release|Any CPU.Build.0 = Release|Any CPU
{81234AFA-B4E7-4D0D-AB97-FD559C78EDA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81234AFA-B4E7-4D0D-AB97-FD559C78EDA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81234AFA-B4E7-4D0D-AB97-FD559C78EDA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81234AFA-B4E7-4D0D-AB97-FD559C78EDA2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -475,8 +481,9 @@ Global
{64E3D8BB-93AB-4571-93F7-ED8D64DFFD06} = {5B7FB835-3FFF-4BC2-99C5-A5B5FAE3C818}
{E7F491CC-C37E-4A56-9CA7-8F77F59E0614} = {3277B1C0-BDFE-4460-9B0D-D9A661FB48DB}
{EA60B549-F712-4ABE-8E44-FCA83B78C06E} = {3277B1C0-BDFE-4460-9B0D-D9A661FB48DB}
{A885DBE2-4B82-432C-A77B-19844D7BBC96} = {0169B149-FB8B-46F4-9EF7-8A0E69F8FAAF}
{1F9D7748-D099-4E25-97F5-9C969D6FF969} = {3277B1C0-BDFE-4460-9B0D-D9A661FB48DB}
{A885DBE2-4B82-432C-A77B-19844D7BBC96} = {0169B149-FB8B-46F4-9EF7-8A0E69F8FAAF}
{81234AFA-B4E7-4D0D-AB97-FD559C78EDA2} = {3277B1C0-BDFE-4460-9B0D-D9A661FB48DB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521}

View File

@ -0,0 +1,99 @@
// <copyright file="Program.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 OpenTelemetry;
using OpenTelemetry.Metrics;
public class Program
{
private static readonly Meter Meter1 = new Meter("CompanyA.ProductA.Library1", "1.0");
private static readonly Meter Meter2 = new Meter("CompanyA.ProductB.Library2", "1.0");
public static void Main(string[] args)
{
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource(Meter1.Name)
.AddSource(Meter2.Name)
// Rename an instrument to new name.
.AddView(instrumentName: "MyCounter", name: "MyCounterRenamed")
// Change Histogram bounds
.AddView(instrumentName: "MyHistogram", new HistogramConfiguration() { BucketBounds = new double[] { 10, 20 }, Aggregation = Aggregation.LastValue })
// For the instrument "MyCounterCustomTags", aggregate with only the keys "tag1", "tag2".
.AddView(instrumentName: "MyCounterCustomTags", new MetricStreamConfiguration() { TagKeys = new string[] { "tag1", "tag2" } })
// Drop the instrument "MyCounterDrop".
.AddView(instrumentName: "MyCounterDrop", new MetricStreamConfiguration() { Aggregation = Aggregation.Drop })
// Advanced selection criteria and config via Func<Instrument, AggregationConfig>
.AddView((instrument) =>
{
if (instrument.Meter.Name.Equals("CompanyA.ProductB.Library2") &&
instrument.GetType().Name.Contains("Histogram"))
{
return new HistogramConfiguration() { BucketBounds = new double[] { 10, 20 } };
}
return null;
})
// An instrument which does not match any views
// gets processed with default behavior. (SDK default)
// Uncommenting the following line will
// turn off the above default. i.e any
// instrument which does not match any views
// gets dropped.
// .AddView(instrumentName: "*", new DropAggregationConfig())
.AddConsoleExporter()
.Build();
var random = new Random();
var counter = Meter1.CreateCounter<long>("MyCounter");
for (int i = 0; i < 20000; i++)
{
counter.Add(1, new("tag1", "value1"), new("tag2", "value2"));
}
var histogram = Meter1.CreateHistogram<long>("MyHistogram");
for (int i = 0; i < 20000; i++)
{
histogram.Record(random.Next(1, 1000), new("tag1", "value1"), new("tag2", "value2"));
}
var counterCustomTags = Meter1.CreateCounter<long>("MyCounterCustomTags");
for (int i = 0; i < 20000; i++)
{
counterCustomTags.Add(1, new("tag1", "value1"), new("tag2", "value2"), new("tag3", "value4"));
}
var counterDrop = Meter1.CreateCounter<long>("MyCounterDrop");
for (int i = 0; i < 20000; i++)
{
counterDrop.Add(1, new("tag1", "value1"), new("tag2", "value2"));
}
var histogram2 = Meter2.CreateHistogram<long>("MyHistogram2");
for (int i = 0; i < 20000; i++)
{
histogram2.Record(random.Next(1, 1000), new("tag1", "value1"), new("tag2", "value2"));
}
}
}

View File

@ -0,0 +1,71 @@
# Customizing OpenTelemetry .NET SDK for Metrics
## MeterProvider
As shown in the [getting-started](../getting-started/README.md) doc, a valid
[`MeterProvider`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#meterprovider)
must be configured and built to collect metrics with OpenTelemetry .NET Sdk.
`MeterProvider` holds all the configuration for tracing like metricreaders,
views, etc. Naturally, almost all the customizations must be done on the
`MeterProvider`.
## Building a MeterProvider
Building a `MeterProvider` is done using `MeterProviderBuilder` which must be
obtained by calling `Sdk.CreateMeterProviderBuilder()`. `MeterProviderBuilder`
exposes various methods which configures the provider it is going to build. These
includes methods like `AddSource`, `AddView` etc, and are explained in
subsequent sections of this document. Once configuration is done, calling
`Build()` on the `MeterProviderBuilder` builds the `MeterProvider` instance.
Once built, changes to its configuration is not allowed. In most cases,
a single `MeterProvider` is created at the application startup,
and is disposed when application shuts down.
The snippet below shows how to build a basic `MeterProvider`. This will create
a provider with default configuration, and is not particularly useful. The
subsequent sections shows how to build a more useful provider.
```csharp
using OpenTelemetry;
using OpenTelemetry.Metrics;
using var meterProvider = Sdk.CreateMeterProviderBuilder().Build();
```
## MeterProvider configuration
`MeterProvider` holds the metrics configuration, which includes the following:
1. The list of `Meter`s from which measurements are collected.
2. The list of instrumentations enabled via
[InstrumentationLibrary](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library).
3. The list of
[MetricReaders](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#metricreader),
including exporting readers which exports metrics to
[Exporters](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#metricexporter)
4. The
[Resource](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/sdk.md)
associated with the metrics.
5. The list of
[Views](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view)
to be used.
### Meter
// TODO
### View
// TODO
### Instrumentation
// TODO
### MetricReader
// TODO
### Resource
// TODO

View File

@ -0,0 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,42 @@
// <copyright file="HistogramConfiguration.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;
namespace OpenTelemetry.Metrics
{
public class HistogramConfiguration : MetricStreamConfiguration
{
private Aggregation aggregation = Aggregation.Histogram;
public double[] BucketBounds { get; set; }
public override Aggregation Aggregation
{
get => this.aggregation;
set
{
if (value != Aggregation.Histogram)
{
throw new ArgumentException($"Aggregation must be Histogram.");
}
this.aggregation = value;
}
}
}
}

View File

@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using OpenTelemetry.Resources;
namespace OpenTelemetry.Metrics
@ -27,6 +28,7 @@ namespace OpenTelemetry.Metrics
{
private readonly List<InstrumentationFactory> instrumentationFactories = new List<InstrumentationFactory>();
private readonly List<string> meterSources = new List<string>();
private readonly List<Func<Instrument, MetricStreamConfiguration>> viewConfigs = new List<Func<Instrument, MetricStreamConfiguration>>();
private ResourceBuilder resourceBuilder = ResourceBuilder.CreateDefault();
protected MeterProviderBuilderBase()
@ -84,6 +86,24 @@ namespace OpenTelemetry.Metrics
return this;
}
internal MeterProviderBuilder AddView(string instrumentName, string name)
{
// TODO: Actually implement view.
return this;
}
internal MeterProviderBuilder AddView(string instrumentName, MetricStreamConfiguration aggregationConfig)
{
// TODO: Actually implement view.
return this;
}
internal MeterProviderBuilder AddView(Func<Instrument, MetricStreamConfiguration> viewConfig)
{
// TODO: Actually implement view.
return this;
}
internal MeterProviderBuilder SetResourceBuilder(ResourceBuilder resourceBuilder)
{
this.resourceBuilder = resourceBuilder ?? throw new ArgumentNullException(nameof(resourceBuilder));
@ -100,6 +120,7 @@ namespace OpenTelemetry.Metrics
this.resourceBuilder.Build(),
this.meterSources,
this.instrumentationFactories,
this.viewConfigs,
this.MetricReaders.ToArray());
}

View File

@ -15,6 +15,7 @@
// </copyright>
using System;
using System.Diagnostics.Metrics;
using OpenTelemetry.Resources;
namespace OpenTelemetry.Metrics
@ -40,6 +41,62 @@ namespace OpenTelemetry.Metrics
return meterProviderBuilder;
}
/// <summary>
/// Add metric view, which can be used to customize the Metrics outputted
/// from the SDK. The views are applied in the order they are added.
/// </summary>
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
/// <param name="instrumentName">Name of the instrument, to be used as part of Instrument selection criteria.</param>
/// <param name="name">Name of the view. This will be used as name of resulting metrics stream.</param>
/// <returns><see cref="MeterProvider"/>.</returns>
/// <remarks>See View specification here : https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view.</remarks>
public static MeterProviderBuilder AddView(this MeterProviderBuilder meterProviderBuilder, string instrumentName, string name)
{
if (meterProviderBuilder is MeterProviderBuilderBase meterProviderBuilderBase)
{
return meterProviderBuilderBase.AddView(instrumentName, name);
}
return meterProviderBuilder;
}
/// <summary>
/// Add metric view, which can be used to customize the Metrics outputted
/// from the SDK. The views are applied in the order they are added.
/// </summary>
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
/// <param name="instrumentName">Name of the instrument, to be used as part of Instrument selection criteria.</param>
/// <param name="aggregationConfig">Aggregation configuration used to produce metrics stream.</param>
/// <returns><see cref="MeterProvider"/>.</returns>
/// <remarks>See View specification here : https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view.</remarks>
public static MeterProviderBuilder AddView(this MeterProviderBuilder meterProviderBuilder, string instrumentName, MetricStreamConfiguration aggregationConfig)
{
if (meterProviderBuilder is MeterProviderBuilderBase meterProviderBuilderBase)
{
return meterProviderBuilderBase.AddView(instrumentName, aggregationConfig);
}
return meterProviderBuilder;
}
/// <summary>
/// Add metric view, which can be used to customize the Metrics outputted
/// from the SDK. The views are applied in the order they are added.
/// </summary>
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
/// <param name="viewConfig">Function to configure aggregation based on the instrument.</param>
/// <returns><see cref="MeterProvider"/>.</returns>
/// <remarks>See View specification here : https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view.</remarks>
public static MeterProviderBuilder AddView(this MeterProviderBuilder meterProviderBuilder, Func<Instrument, MetricStreamConfiguration> viewConfig)
{
if (meterProviderBuilder is MeterProviderBuilderBase meterProviderBuilderBase)
{
return meterProviderBuilderBase.AddView(viewConfig);
}
return meterProviderBuilder;
}
/// <summary>
/// Sets the <see cref="ResourceBuilder"/> from which the Resource associated with
/// this provider is built from. Overwrites currently set ResourceBuilder.

View File

@ -29,6 +29,7 @@ namespace OpenTelemetry.Metrics
internal int ShutdownCount;
private readonly Metric[] metrics;
private readonly List<object> instrumentations = new List<object>();
private readonly List<Func<Instrument, MetricStreamConfiguration>> viewConfigs;
private readonly object collectLock = new object();
private readonly object instrumentCreationLock = new object();
private readonly Dictionary<string, bool> metricStreamNames = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
@ -39,10 +40,12 @@ namespace OpenTelemetry.Metrics
internal MeterProviderSdk(
Resource resource,
IEnumerable<string> meterSources,
List<MeterProviderBuilderSdk.InstrumentationFactory> instrumentationFactories,
List<MeterProviderBuilderBase.InstrumentationFactory> instrumentationFactories,
List<Func<Instrument, MetricStreamConfiguration>> viewConfigs,
IEnumerable<MetricReader> readers)
{
this.Resource = resource;
this.viewConfigs = viewConfigs;
this.metrics = new Metric[MaxMetrics];
AggregationTemporality temporality = AggregationTemporality.Cumulative;
@ -97,6 +100,8 @@ namespace OpenTelemetry.Metrics
{
lock (this.instrumentCreationLock)
{
// TODO: This is where view config will be looked up
// and zero, one or more Metric streams will be created.
if (this.metricStreamNames.ContainsKey(instrument.Name))
{
// log and ignore this instrument.

View File

@ -0,0 +1,46 @@
// <copyright file="MetricStreamConfiguration.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>
namespace OpenTelemetry.Metrics
{
// TODO: can be optimized like MetricType
public enum Aggregation
{
#pragma warning disable SA1602 // Enumeration items should be documented
Default,
None,
Sum,
LastValue,
Histogram,
Drop = None,
#pragma warning restore SA1602 // Enumeration items should be documented
}
public class MetricStreamConfiguration
{
public string Name { get; set; }
public string Description { get; set; }
public string Unit { get; set; }
public string[] TagKeys { get; set; }
public virtual Aggregation Aggregation { get; set; }
// TODO: MetricPoints caps can be configured here
}
}