Skip to content

Commit

Permalink
Merge branch 'feature/resource-attribute-tags' of https://github.com/…
Browse files Browse the repository at this point in the history
…robertcoltheart/opentelemetry-dotnet into feature/resource-attribute-tags
  • Loading branch information
robertcoltheart committed Mar 6, 2024
2 parents 5ddf850 + f6f10ba commit 0a930da
Show file tree
Hide file tree
Showing 25 changed files with 787 additions and 523 deletions.
20 changes: 15 additions & 5 deletions docs/logs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ Here is the rule of thumb:
Minutes - Console Application](./getting-started-console/README.md) tutorial
to learn more.

:heavy_check_mark: You should use dot-separated
[UpperCamelCase](https://en.wikipedia.org/wiki/Camel_case) as the log category
name, which makes it convenient to [filter logs](#log-filtering). A common
practice is to use fully qualified class name, and if further categorization is
desired, append a subcategory name. Refer to the [.NET official
document](https://learn.microsoft.com/dotnet/core/extensions/logging#log-category)
to learn more.

```csharp
loggerFactory.CreateLogger<MyClass>(); // this is equivalent to CreateLogger("MyProduct.MyLibrary.MyClass")
loggerFactory.CreateLogger("MyProduct.MyLibrary.MyClass"); // use the fully qualified class name
loggerFactory.CreateLogger("MyProduct.MyLibrary.MyClass.DatabaseOperations"); // append a subcategory name
loggerFactory.CreateLogger("MyProduct.MyLibrary.MyClass.FileOperations"); // append another subcategory name
```

:stop_sign: You should avoid creating loggers too frequently. Although loggers
are not super expensive, they still come with CPU and memory cost, and are meant
to be reused throughout the application. Refer to the [logging performance
Expand Down Expand Up @@ -186,11 +201,6 @@ instances if they are created by you.
API invocation associated with the logger factory could become no-op (i.e. no
logs will be emitted).

:heavy_check_mark: You should use the fully qualified class name as the log
category name. Refer to the [.NET official
document](https://learn.microsoft.com/dotnet/core/extensions/logging#log-category)
to learn more.

## Log Correlation

In OpenTelemetry, logs are automatically correlated to
Expand Down
54 changes: 17 additions & 37 deletions docs/metrics/customizing-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,58 +412,38 @@ exemplars.

#### ExemplarFilter

`ExemplarFilter` determines which measurements are eligible to become an
Exemplar. i.e. `ExemplarFilter` determines which measurements are offered to
`ExemplarReservoir`, which makes the final decision about whether the offered
measurement gets stored as an exemplar. They can be used to control the noise
and overhead associated with Exemplar collection.
`ExemplarFilter` determines which measurements are offered to the configured
`ExemplarReservoir`, which makes the final decision about whether or not the
offered measurement gets recorded as an `Exemplar`. Generally `ExemplarFilter`
is a mechanism to control the overhead associated with `Exemplar` offering.

OpenTelemetry SDK comes with the following Filters:
OpenTelemetry SDK comes with the following `ExemplarFilters` (defined on
`ExemplarFilterType`):

* `AlwaysOnExemplarFilter` - makes all measurements eligible for being an Exemplar.
* `AlwaysOffExemplarFilter` - makes no measurements eligible for being an
Exemplar. Using this is as good as turning off Exemplar feature, and is the current
* `AlwaysOff`: Makes no measurements eligible for becoming an `Exemplar`. Using
this is as good as turning off the `Exemplar` feature and is the current
default.
* `TraceBasedExemplarFilter` - makes those measurements eligible for being an
Exemplar, which are recorded in the context of a sampled parent `Activity`
(span).
* `AlwaysOn`: Makes all measurements eligible for becoming an `Exemplar`.
* `TraceBased`: Makes those measurements eligible for becoming an `Exemplar`
which are recorded in the context of a sampled `Activity` (span).

`SetExemplarFilter` method on `MeterProviderBuilder` can be used to set the
desired `ExemplarFilter`.

The snippet below shows how to set `ExemplarFilter`.
The `SetExemplarFilter` extension method on `MeterProviderBuilder` can be used
to set the desired `ExemplarFilterType` and enable `Exemplar` collection:

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

using var meterProvider = Sdk.CreateMeterProviderBuilder()
// rest of config not shown
.SetExemplarFilter(new TraceBasedExemplarFilter())
.SetExemplarFilter(ExemplarFilterType.TraceBased)
.Build();
```

> [!NOTE]
> As of today, there is no separate toggle for enable/disable Exemplar feature.
Exemplars can be disabled by setting filter as `AlwaysOffExemplarFilter`, which
is also the default (i.e Exemplar feature is disabled by default). Users can
enable the feature by setting filter to anything other than
`AlwaysOffExemplarFilter`. For example: `.SetExemplarFilter(new TraceBasedExemplarFilter())`.

If the built-in `ExemplarFilter`s are not meeting the needs, one may author
custom `ExemplarFilter` as shown
[here](../extending-the-sdk/README.md#exemplarfilter). A custom filter, which
eliminates all un-interesting measurements from becoming Exemplar is a
recommended way to control performance overhead associated with collecting
Exemplars. See
[benchmark](../../../test/Benchmarks/Metrics/ExemplarBenchmarks.cs) to see how
much impact can `ExemplarFilter` have on performance.

#### ExemplarReservoir

`ExemplarReservoir` receives the measurements sampled in by the `ExemplarFilter`
and is responsible for storing Exemplars. `ExemplarReservoir` ultimately decides
which measurements get stored as exemplars. The following are the default
`ExemplarReservoir` receives the measurements sampled by the `ExemplarFilter`
and is responsible for recording `Exemplar`s. The following are the default
reservoirs:

* `AlignedHistogramBucketExemplarReservoir` is the default reservoir used for
Expand All @@ -479,7 +459,7 @@ size (currently defaulting to 1) determines the maximum number of exemplars
stored.

> [!NOTE]
> Currently there is no ability to change or configure Reservoir.
> Currently there is no ability to change or configure `ExemplarReservoir`.
### Instrumentation

Expand Down
39 changes: 1 addition & 38 deletions docs/metrics/extending-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,44 +74,7 @@ Not supported.

## ExemplarFilter

OpenTelemetry .NET SDK has provided the following built-in `ExemplarFilter`s:

* [AlwaysOnExemplarFilter](../../../src/OpenTelemetry/Metrics/Exemplar/AlwaysOnExemplarFilter.cs)
* [AlwaysOffExemplarFilter](../../../src/OpenTelemetry/Metrics/Exemplar/AlwaysOffExemplarFilter.cs)
* [TraceBasedExemplarFilter](../../../src/OpenTelemetry/Metrics/Exemplar/TraceBasedExemplarFilter.cs)

Custom exemplar filters can be implemented to achieve filtering based on other criterion:

* `ExemplarFilter` should derive from `OpenTelemetry.ExemplarFilter` (which
belongs to the [OpenTelemetry](../../../src/OpenTelemetry/README.md) package)
and implement the `ShouldSample` method.

One example is a filter, which filters all measurements of value lower
than given threshold is given below. Such a filter prevents any measurements
below the given threshold from ever becoming a `Exemplar`. Such filters could
also incorporate the `TraceBasedExemplarFilter` condition as well, as storing
exemplars for non-sampled traces may be undesired.

```csharp
public sealed class HighValueFilter : ExemplarFilter
{
private readonly double maxValue;

public HighValueFilter(double maxValue)
{
this.maxValue = maxValue;
}
public override bool ShouldSample(long value, ReadOnlySpan<KeyValuePair<string, object>> tags)
{
return Activity.Current?.Recorded && value > this.maxValue;
}

public override bool ShouldSample(double value, ReadOnlySpan<KeyValuePair<string, object>> tags)
{
return Activity.Current?.Recorded && value > this.maxValue;
}
}
```
Not supported.

## ExemplarReservoir

Expand Down
2 changes: 1 addition & 1 deletion examples/AspNetCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
builder
.AddMeter(Instrumentation.MeterName)
#if EXPOSE_EXPERIMENTAL_FEATURES
.SetExemplarFilter(new TraceBasedExemplarFilter())
.SetExemplarFilter(ExemplarFilterType.TraceBased)
#endif
.AddRuntimeInstrumentation()
.AddHttpClientInstrumentation()
Expand Down
5 changes: 5 additions & 0 deletions src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
as it is mandated by the specification.
([#5316](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5268))

* **Experimental (pre-release builds only):** Add support in
`OtlpMetricExporter` for emitting exemplars supplied on Counters, Gauges, and
ExponentialHistograms.
([#5397](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5397))

## 1.7.0

Released 2023-Dec-08
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric)
AddAttributes(metricPoint.Tags, dataPoint.Attributes);

dataPoint.AsInt = metricPoint.GetSumLong();

if (metricPoint.TryGetExemplars(out var exemplars))
{
foreach (ref readonly var exemplar in exemplars)
{
dataPoint.Exemplars.Add(
ToOtlpExemplar(exemplar.LongValue, in exemplar));
}
}

sum.DataPoints.Add(dataPoint);
}

Expand Down Expand Up @@ -185,6 +195,16 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric)
AddAttributes(metricPoint.Tags, dataPoint.Attributes);

dataPoint.AsDouble = metricPoint.GetSumDouble();

if (metricPoint.TryGetExemplars(out var exemplars))
{
foreach (ref readonly var exemplar in exemplars)
{
dataPoint.Exemplars.Add(
ToOtlpExemplar(exemplar.DoubleValue, in exemplar));
}
}

sum.DataPoints.Add(dataPoint);
}

Expand All @@ -206,6 +226,16 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric)
AddAttributes(metricPoint.Tags, dataPoint.Attributes);

dataPoint.AsInt = metricPoint.GetGaugeLastValueLong();

if (metricPoint.TryGetExemplars(out var exemplars))
{
foreach (ref readonly var exemplar in exemplars)
{
dataPoint.Exemplars.Add(
ToOtlpExemplar(exemplar.LongValue, in exemplar));
}
}

gauge.DataPoints.Add(dataPoint);
}

Expand All @@ -227,6 +257,16 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric)
AddAttributes(metricPoint.Tags, dataPoint.Attributes);

dataPoint.AsDouble = metricPoint.GetGaugeLastValueDouble();

if (metricPoint.TryGetExemplars(out var exemplars))
{
foreach (ref readonly var exemplar in exemplars)
{
dataPoint.Exemplars.Add(
ToOtlpExemplar(exemplar.DoubleValue, in exemplar));
}
}

gauge.DataPoints.Add(dataPoint);
}

Expand Down Expand Up @@ -320,7 +360,14 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric)
dataPoint.Positive.BucketCounts.Add((ulong)bucketCount);
}

// TODO: exemplars.
if (metricPoint.TryGetExemplars(out var exemplars))
{
foreach (ref readonly var exemplar in exemplars)
{
dataPoint.Exemplars.Add(
ToOtlpExemplar(exemplar.DoubleValue, in exemplar));
}
}

histogram.DataPoints.Add(dataPoint);
}
Expand All @@ -333,29 +380,7 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric)
return otlpMetric;
}

private static void AddAttributes(ReadOnlyTagCollection tags, RepeatedField<OtlpCommon.KeyValue> attributes)
{
foreach (var tag in tags)
{
if (OtlpKeyValueTransformer.Instance.TryTransformTag(tag, out var result))
{
attributes.Add(result);
}
}
}

private static void AddScopeAttributes(IEnumerable<KeyValuePair<string, object>> meterTags, RepeatedField<OtlpCommon.KeyValue> attributes)
{
foreach (var tag in meterTags)
{
if (OtlpKeyValueTransformer.Instance.TryTransformTag(tag, out var result))
{
attributes.Add(result);
}
}
}

private static OtlpMetrics.Exemplar ToOtlpExemplar<T>(T value, in Metrics.Exemplar exemplar)
internal static OtlpMetrics.Exemplar ToOtlpExemplar<T>(T value, in Metrics.Exemplar exemplar)
where T : struct
{
var otlpExemplar = new OtlpMetrics.Exemplar
Expand Down Expand Up @@ -399,4 +424,26 @@ private static OtlpMetrics.Exemplar ToOtlpExemplar<T>(T value, in Metrics.Exempl

return otlpExemplar;
}

private static void AddAttributes(ReadOnlyTagCollection tags, RepeatedField<OtlpCommon.KeyValue> attributes)
{
foreach (var tag in tags)
{
if (OtlpKeyValueTransformer.Instance.TryTransformTag(tag, out var result))
{
attributes.Add(result);
}
}
}

private static void AddScopeAttributes(IEnumerable<KeyValuePair<string, object>> meterTags, RepeatedField<OtlpCommon.KeyValue> attributes)
{
foreach (var tag in meterTags)
{
if (OtlpKeyValueTransformer.Instance.TryTransformTag(tag, out var result))
{
attributes.Add(result);
}
}
}
}
Loading

0 comments on commit 0a930da

Please sign in to comment.