Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
mic-max committed Oct 13, 2021
2 parents 3d15631 + 90d2906 commit 472eabb
Show file tree
Hide file tree
Showing 121 changed files with 4,005 additions and 678 deletions.
12 changes: 12 additions & 0 deletions OpenTelemetry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "customizing-the-sdk", "docs
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Tests.Stress", "test\OpenTelemetry.Tests.Stress\OpenTelemetry.Tests.Stress.csproj", "{2770158A-D220-414B-ABC6-179371323579}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus.Tests", "test\OpenTelemetry.Exporter.Prometheus.Tests\OpenTelemetry.Exporter.Prometheus.Tests.csproj", "{380EE686-91F1-45B3-AEEB-755F0E5B068F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs", "src\OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs\OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs.csproj", "{6E1A5FA3-E024-4972-9EDC-11E36C5A0D6F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -448,6 +452,14 @@ Global
{2770158A-D220-414B-ABC6-179371323579}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2770158A-D220-414B-ABC6-179371323579}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2770158A-D220-414B-ABC6-179371323579}.Release|Any CPU.Build.0 = Release|Any CPU
{380EE686-91F1-45B3-AEEB-755F0E5B068F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{380EE686-91F1-45B3-AEEB-755F0E5B068F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{380EE686-91F1-45B3-AEEB-755F0E5B068F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{380EE686-91F1-45B3-AEEB-755F0E5B068F}.Release|Any CPU.Build.0 = Release|Any CPU
{6E1A5FA3-E024-4972-9EDC-11E36C5A0D6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E1A5FA3-E024-4972-9EDC-11E36C5A0D6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E1A5FA3-E024-4972-9EDC-11E36C5A0D6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E1A5FA3-E024-4972-9EDC-11E36C5A0D6F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
10 changes: 5 additions & 5 deletions docs/metrics/customizing-the-sdk/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,22 @@ public class Program
public static void Main(string[] args)
{
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource(Meter1.Name)
.AddSource(Meter2.Name)
.AddMeter(Meter1.Name)
.AddMeter(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 })
.AddView(instrumentName: "MyHistogram", new HistogramConfiguration() { BucketBounds = new double[] { 10, 20 } })

// 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 })
.AddView(instrumentName: "MyCounterDrop", MetricStreamConfiguration.Drop)

// Advanced selection criteria and config via Func<Instrument, AggregationConfig>
// Advanced selection criteria and config via Func<Instrument, MetricStreamConfiguration>
.AddView((instrument) =>
{
if (instrument.Meter.Name.Equals("CompanyA.ProductB.Library2") &&
Expand Down
245 changes: 227 additions & 18 deletions docs/metrics/customizing-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,24 @@
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` holds all the configuration for metrics 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.
exposes various methods which configure the provider it is going to build. These
include methods like `AddMeter`, `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 show how to build a more useful provider.

```csharp
using OpenTelemetry;
Expand All @@ -36,9 +35,10 @@ using var meterProvider = Sdk.CreateMeterProviderBuilder().Build();

`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).
1. The list of `Meter`s from which instruments are created to report
measurements.
2. The list of instrumentations enabled via [Instrumentation
Library](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
Expand All @@ -52,11 +52,220 @@ using var meterProvider = Sdk.CreateMeterProviderBuilder().Build();

### Meter

// TODO
[`Meter`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#meter)
is used for creating
[`Instruments`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument),
which are then used to report
[Measurements](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#measurement).
The SDK follows an explicit opt-in model for listening to meters. i.e, by
default, it listens to no meters. Every meter which is used to create
instruments must be explicitly added to the meter provider.

`AddMeter` method on `MeterProviderBuilder` can be used to add a `Meter` to the
provider. The name of the `Meter` (case-insensitive) must be provided as an
argument to this method. `AddMeter` can be called multiple times to add more
than one meters. It also supports wild-card subscription model.

It is **not** possible to add meters *once* the provider is built by the
`Build()` method on the `MeterProviderBuilder`.

The snippet below shows how to add meters to the provider.

```csharp
using OpenTelemetry;
using OpenTelemetry.Metrics;

using var meterProvider = Sdk.CreateMeterProviderBuilder()
// The following enables instruments from Meter
// named "MyCompany.MyProduct.MyLibrary" only.
.AddMeter("MyCompany.MyProduct.MyLibrary")
// The following enables instruments from all Meters
// whose name starts with "AbcCompany.XyzProduct.".
.AddMeter("AbcCompany.XyzProduct.*")
.Build();
```

See [Program.cs](./Program.cs) for complete example.

**Note:** A common mistake while configuring `MeterProvider` is forgetting to
add the required `Meter`s to the provider. It is recommended to leverage the
wildcard subscription model where it makes sense. For example, if your
application is expecting to enable instruments from a number of libraries from a
company "Abc", the you can use `AddMeter("Abc.*")` to enable all meters whose
name starts with "Abc.".

### View

// TODO
A
[View](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view)
provides the ability to customize the metrics that are output by the SDK.
Following sections explains how to use this feature. Each section has two code
snippets. The first one uses an overload of `AddView` method that takes in the
name of the instrument as the first parameter. The `View` configuration is then
applied to the matching instrument name. The second code snippet shows how to
use an advanced selection criteria to achieve the same results. This requires
the user to provide a `Func<Instrument, MetricStreamConfiguration>` which offers
more flexibility in filtering the instruments to which the `View` should be
applied.

#### Rename an instrument

When SDK produces Metrics, the name of Metric is by default the name of the
instrument. View may be used to rename a metric to a different name. This is
particularly useful if there are conflicting instrument names, and you do not
own the instrument to create it with a different name.

```csharp
// Rename an instrument to new name.
.AddView(instrumentName: "MyCounter", name: "MyCounterRenamed")
```

```csharp
// Advanced selection criteria and config via Func<Instrument, MetricStreamConfiguration>
.AddView((instrument) =>
{
if (instrument.Meter.Name == "CompanyA.ProductB.LibraryC" &&
instrument.Name == "MyCounter")
{
return new MetricStreamConfiguration() { Name = "MyCounterRenamed" };
}

return null;
})
```

#### Drop an instrument

When using `AddMeter` to add a Meter to the provider, all the instruments from
that `Meter` gets subscribed. Views can be used to selectively drop an
instrument from a Meter. If the goal is to drop every instrument from a `Meter`,
then it is recommended to simply not add that `Meter` using `AddMeter`.

```csharp
// Drop the instrument "MyCounterDrop".
.AddView(instrumentName: "MyCounterDrop", MetricStreamConfiguration.Drop)
```

```csharp
// Advanced selection criteria and config via Func<Instrument, MetricStreamConfiguration>
.AddView((instrument) =>
{
if (instrument.Meter.Name == "CompanyA.ProductB.LibraryC" &&
instrument.Name == "MyCounterDrop")
{
return MetricStreamConfiguration.Drop;
}

return null;
})
```

#### Select specific tags

When recording a measurement from an instrument, all the tags that were provided
are reported as dimensions for the given metric. Views can be used to
selectively choose a subset of dimensions to report for a given metric. This is
useful when you have a metric for which only a few of the dimensions associated
with the metric are of interest to you.

```csharp
// Only choose "name" as the dimension for the metric "MyFruitCounter"
.AddView(
instrumentName: "MyFruitCounter",
metricStreamConfiguration: new MetricStreamConfiguration
{
TagKeys = new string[] { "name" },
})

...
// Only the dimension "name" is selected, "color" is dropped
MyFruitCounter.Add(1, new("name", "apple"), new("color", "red"));
MyFruitCounter.Add(2, new("name", "lemon"), new("color", "yellow"));
MyFruitCounter.Add(2, new("name", "apple"), new("color", "green"));
...

// If you provide an empty `string` array as `TagKeys` to the `MetricStreamConfiguration`
// the SDK will drop all the dimensions associated with the metric
.AddView(
instrumentName: "MyFruitCounter",
metricStreamConfiguration: new MetricStreamConfiguration
{
TagKeys = new string[] { },
})

...
// both "name" and "color" are dropped
MyFruitCounter.Add(1, new("name", "apple"), new("color", "red"));
MyFruitCounter.Add(2, new("name", "lemon"), new("color", "yellow"));
MyFruitCounter.Add(2, new("name", "apple"), new("color", "green"));
...
```

```csharp
// Advanced selection criteria and config via Func<Instrument, MetricStreamConfiguration>
.AddView((instrument) =>
{
if (instrument.Meter.Name == "CompanyA.ProductB.LibraryC" &&
instrument.Name == "MyFruitCounter")
{
return new MetricStreamConfiguration
{
TagKeys = new string[] { "name" },
};
}

return null;
})
```

#### Specify custom bounds for Histogram

By default, the bounds used for a Histogram are [`{ 0, 5, 10, 25, 50, 75, 100,
250, 500,
1000}`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#explicit-bucket-histogram-aggregation).
Views can be used to provide custom bounds for a Histogram. The measurements are
then aggregated using the custom bounds provided instead of the the default
bounds. This requires the use of `HistogramConfiguration`.

```csharp
// Change Histogram bounds to count measurements under the following buckets:
// (-inf, 10]
// (10, 20]
// (20, +inf)
.AddView(
instrumentName: "MyHistogram",
new HistogramConfiguration{ BucketBounds = new double[] { 10, 20 } })

// If you provide an empty `double` array as `BucketBounds` to the `HistogramConfiguration`,
// the SDK will only export the sum and count for the measurements.
// There are no buckets exported in this case.
.AddView(
instrumentName: "MyHistogram",
new HistogramConfiguration { BucketBounds = new double[] { } })
```

```csharp
// Advanced selection criteria and config via Func<Instrument, MetricStreamConfiguration>
.AddView((instrument) =>
{
if (instrument.Meter.Name == "CompanyA.ProductB.LibraryC" &&
instrument.Name == "MyHistogram")
{
// `HistogramConfiguration` is a child class of `MetricStreamConfiguration`
return new HistogramConfiguration
{
BucketBounds = new double[] { 10, 20 },
};
}

return null;
})
```

**NOTE:** The SDK currently does not support any changes to `Aggregation` type
for Views.

See [Program.cs](./Program.cs) for a complete example.

### Instrumentation

Expand Down
32 changes: 32 additions & 0 deletions docs/metrics/extending-the-sdk/MyExporterExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// <copyright file="MyExporterExtensions.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;
using OpenTelemetry.Metrics;

internal static class MyExporterExtensions
{
public static MeterProviderBuilder AddMyExporter(this MeterProviderBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

return builder.AddReader(new BaseExportingMetricReader(new MyExporter()));
}
}
14 changes: 13 additions & 1 deletion docs/metrics/extending-the-sdk/MyReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// </copyright>

using System;
using System.Text;
using OpenTelemetry;
using OpenTelemetry.Metrics;

Expand All @@ -29,7 +30,18 @@ public MyReader(string name = "MyReader")

protected override bool ProcessMetrics(Batch<Metric> metrics, int timeoutMilliseconds)
{
Console.WriteLine($"{this.name}.ProcessMetrics(metrics={metrics}, timeoutMilliseconds={timeoutMilliseconds})");
var sb = new StringBuilder();
foreach (var record in metrics)
{
if (sb.Length > 0)
{
sb.Append(", ");
}

sb.Append($"{record}");
}

Console.WriteLine($"{this.name}.ProcessMetrics(metrics=[{sb}], timeoutMilliseconds={timeoutMilliseconds})");
return true;
}

Expand Down
Loading

0 comments on commit 472eabb

Please sign in to comment.