diff --git a/.github/workflows/apicompatibility.yml b/.github/workflows/apicompatibility.yml
index 9ab30cf3734..70e7ada39eb 100644
--- a/.github/workflows/apicompatibility.yml
+++ b/.github/workflows/apicompatibility.yml
@@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # fetching all
- - uses: actions/setup-dotnet@v3.0.1
+ - uses: actions/setup-dotnet@v3.0.2
with:
dotnet-version: '7.0.x'
include-prerelease: true
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index e2e8dceef06..a855357cfa0 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # fetching all
- - uses: actions/setup-dotnet@v3.0.1
+ - uses: actions/setup-dotnet@v3.0.2
with:
dotnet-version: '7.0.x'
include-prerelease: true
diff --git a/.github/workflows/dotnet-format.yml b/.github/workflows/dotnet-format.yml
index d07b4e5ce93..37d7b78bffd 100644
--- a/.github/workflows/dotnet-format.yml
+++ b/.github/workflows/dotnet-format.yml
@@ -21,7 +21,7 @@ jobs:
uses: actions/checkout@v3
- name: Setup .NET Core 7.0
- uses: actions/setup-dotnet@v3.0.1
+ uses: actions/setup-dotnet@v3.0.2
with:
dotnet-version: '7.0.x'
include-prerelease: true
diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml
index 82e30af9ff9..afe2c61192a 100644
--- a/.github/workflows/linux-ci.yml
+++ b/.github/workflows/linux-ci.yml
@@ -23,11 +23,11 @@ jobs:
with:
fetch-depth: 0 # fetching all
- - uses: actions/setup-dotnet@v3.0.1
+ - uses: actions/setup-dotnet@v3.0.2
with:
dotnet-version: '6.0.x'
- - uses: actions/setup-dotnet@v3.0.1
+ - uses: actions/setup-dotnet@v3.0.2
with:
dotnet-version: '7.0.x'
include-prerelease: true
diff --git a/.github/workflows/publish-packages-1.0.yml b/.github/workflows/publish-packages-1.0.yml
index ca27a4311f2..8f6f423b58d 100644
--- a/.github/workflows/publish-packages-1.0.yml
+++ b/.github/workflows/publish-packages-1.0.yml
@@ -23,7 +23,7 @@ jobs:
fetch-depth: 0 # fetching all
ref: ${{ github.ref || 'main' }}
- - uses: actions/setup-dotnet@v3.0.1
+ - uses: actions/setup-dotnet@v3.0.2
with:
dotnet-version: '7.0.x'
include-prerelease: true
diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml
index bc0cad6336f..7ce30a222e6 100644
--- a/.github/workflows/windows-ci.yml
+++ b/.github/workflows/windows-ci.yml
@@ -26,7 +26,7 @@ jobs:
with:
fetch-depth: 0 # fetching all
- - uses: actions/setup-dotnet@v3.0.1
+ - uses: actions/setup-dotnet@v3.0.2
with:
dotnet-version: '7.0.x'
include-prerelease: true
diff --git a/build/Common.prod.props b/build/Common.prod.props
index 915644b7755..90119d36dab 100644
--- a/build/Common.prod.props
+++ b/build/Common.prod.props
@@ -13,11 +13,17 @@
-
+
-
-
+
+
+
diff --git a/build/PreBuild.ps1 b/build/PreBuild.ps1
index 847c4d52c70..64d51872e34 100644
--- a/build/PreBuild.ps1
+++ b/build/PreBuild.ps1
@@ -1,26 +1,31 @@
-param([string]$package, [string]$version)
+param(
+ [string]$package,
+ [string]$version,
+ [string]$workDir = ".\LastMajorVersionBinaries"
+)
-$workDir = "..\LastMajorVersionBinaries"
if (-Not (Test-Path $workDir))
{
- Write-Host "Working directory for previous package versions not found, creating..."
+ Write-Host "Working directory for compatibility check packages '$workDir' not found, creating..."
New-Item -Path $workDir -ItemType "directory" | Out-Null
}
if (Test-Path -Path "$workDir\$package.$version.zip")
{
- Write-Debug "Previous package version already downloaded"
+ Write-Debug "Previous package $package@$version already downloaded for compatibility check"
}
else
{
- Write-Host "Retrieving $package @$version for compatibility check"
+ Write-Host "Retrieving package $package@$version for compatibility check"
Invoke-WebRequest -Uri https://www.nuget.org/api/v2/package/$package/$version -Outfile "$workDir\$package.$version.zip"
}
+
if (Test-Path -Path "$workDir\$package\$version\lib")
{
- Write-Debug "Previous package version already extracted"
+ Write-Debug "Previous package $package@$version already extracted to '$workDir\$package\$version\lib'"
}
else
{
+ Write-Host "Extracting package $package@$version from '$workDir\$package.$version.zip' to '$workDir\$package\$version' for compatibility check"
Expand-Archive -LiteralPath "$workDir\$package.$version.zip" -DestinationPath "$workDir\$package\$version" -Force
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
index 3232e5d8a24..585c05c2e63 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
@@ -2,6 +2,16 @@
## Unreleased
+* OTLP histogram data points will now include `Min` and `Max` values when
+ they are present.
+ ([#2735](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2735))
+
+* Adds support for limiting the length and count of attributes exported from
+ the OTLP log exporter. These
+ [Attribute Limits](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#attribute-limits)
+ are configured via the environment variables defined in the specification.
+ ([#3684](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3684))
+
## 1.4.0-beta.1
Released 2022-Sep-29
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/LogRecordExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/LogRecordExtensions.cs
index 126530af082..e674b30f7ad 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/LogRecordExtensions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/LogRecordExtensions.cs
@@ -18,8 +18,8 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Google.Protobuf;
-using Google.Protobuf.Collections;
using Microsoft.Extensions.Logging;
+using OpenTelemetry.Configuration;
using OpenTelemetry.Internal;
using OpenTelemetry.Logs;
using OpenTelemetry.Trace;
@@ -75,6 +75,11 @@ internal static OtlpLogs.LogRecord ToOtlpLog(this LogRecord logRecord)
SeverityText = LogLevels[(int)logRecord.LogLevel],
};
+ var attributeValueLengthLimit = SdkConfiguration.Instance.AttributeValueLengthLimit;
+ var attributeCountLimit = SdkConfiguration.Instance.AttributeCountLimit ?? int.MaxValue;
+
+ // First add the generic attributes like category, eventid and exception, so they are less likely being dropped because of AttributeCountLimit
+
if (!string.IsNullOrEmpty(logRecord.CategoryName))
{
// TODO:
@@ -82,7 +87,24 @@ internal static OtlpLogs.LogRecord ToOtlpLog(this LogRecord logRecord)
// if it makes it to log data model.
// https://github.com/open-telemetry/opentelemetry-specification/issues/2398
// 2. Confirm if this name for attribute is good.
- otlpLogRecord.Attributes.AddStringAttribute("dotnet.ilogger.category", logRecord.CategoryName);
+ otlpLogRecord.AddStringAttribute("dotnet.ilogger.category", logRecord.CategoryName, attributeValueLengthLimit, attributeCountLimit);
+ }
+
+ if (logRecord.EventId.Id != default)
+ {
+ otlpLogRecord.AddIntAttribute(nameof(logRecord.EventId.Id), logRecord.EventId.Id, attributeValueLengthLimit, attributeCountLimit);
+ }
+
+ if (!string.IsNullOrEmpty(logRecord.EventId.Name))
+ {
+ otlpLogRecord.AddStringAttribute(nameof(logRecord.EventId.Name), logRecord.EventId.Name, attributeValueLengthLimit, attributeCountLimit);
+ }
+
+ if (logRecord.Exception != null)
+ {
+ otlpLogRecord.AddStringAttribute(SemanticConventions.AttributeExceptionType, logRecord.Exception.GetType().Name, attributeValueLengthLimit, attributeCountLimit);
+ otlpLogRecord.AddStringAttribute(SemanticConventions.AttributeExceptionMessage, logRecord.Exception.Message, attributeValueLengthLimit, attributeCountLimit);
+ otlpLogRecord.AddStringAttribute(SemanticConventions.AttributeExceptionStacktrace, logRecord.Exception.ToInvariantString(), attributeValueLengthLimit, attributeCountLimit);
}
bool bodyPopulatedFromFormattedMessage = false;
@@ -103,30 +125,13 @@ internal static OtlpLogs.LogRecord ToOtlpLog(this LogRecord logRecord)
{
otlpLogRecord.Body = new OtlpCommon.AnyValue { StringValue = stateValue.Value as string };
}
- else if (OtlpKeyValueTransformer.Instance.TryTransformTag(stateValue, out var result))
+ else if (OtlpKeyValueTransformer.Instance.TryTransformTag(stateValue, out var result, attributeValueLengthLimit))
{
- otlpLogRecord.Attributes.Add(result);
+ otlpLogRecord.AddAttribute(result, attributeCountLimit);
}
}
}
- if (logRecord.EventId.Id != default)
- {
- otlpLogRecord.Attributes.AddIntAttribute(nameof(logRecord.EventId.Id), logRecord.EventId.Id);
- }
-
- if (!string.IsNullOrEmpty(logRecord.EventId.Name))
- {
- otlpLogRecord.Attributes.AddStringAttribute(nameof(logRecord.EventId.Name), logRecord.EventId.Name);
- }
-
- if (logRecord.Exception != null)
- {
- otlpLogRecord.Attributes.AddStringAttribute(SemanticConventions.AttributeExceptionType, logRecord.Exception.GetType().Name);
- otlpLogRecord.Attributes.AddStringAttribute(SemanticConventions.AttributeExceptionMessage, logRecord.Exception.Message);
- otlpLogRecord.Attributes.AddStringAttribute(SemanticConventions.AttributeExceptionStacktrace, logRecord.Exception.ToInvariantString());
- }
-
if (logRecord.TraceId != default && logRecord.SpanId != default)
{
byte[] traceIdBytes = new byte[16];
@@ -149,9 +154,9 @@ void ProcessScope(LogRecordScope scope, OtlpLogs.LogRecord otlpLog)
foreach (var scopeItem in scope)
{
var scopeItemWithDepthInfo = new KeyValuePair($"[Scope.{scopeDepth}]:{scopeItem.Key}", scopeItem.Value);
- if (OtlpKeyValueTransformer.Instance.TryTransformTag(scopeItemWithDepthInfo, out var result))
+ if (OtlpKeyValueTransformer.Instance.TryTransformTag(scopeItemWithDepthInfo, out var result, attributeValueLengthLimit))
{
- otlpLog.Attributes.Add(result);
+ otlpLog.AddAttribute(result, attributeCountLimit);
}
}
}
@@ -164,22 +169,37 @@ void ProcessScope(LogRecordScope scope, OtlpLogs.LogRecord otlpLog)
return otlpLogRecord;
}
- private static void AddStringAttribute(this RepeatedField repeatedField, string key, string value)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void AddAttribute(this OtlpLogs.LogRecord logRecord, OtlpCommon.KeyValue attribute, int maxAttributeCount)
{
- repeatedField.Add(new OtlpCommon.KeyValue
+ if (logRecord.Attributes.Count < maxAttributeCount)
+ {
+ logRecord.Attributes.Add(attribute);
+ }
+ else
{
- Key = key,
- Value = new OtlpCommon.AnyValue { StringValue = value },
- });
+ logRecord.DroppedAttributesCount++;
+ }
}
- private static void AddIntAttribute(this RepeatedField repeatedField, string key, int value)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void AddStringAttribute(this OtlpLogs.LogRecord logRecord, string key, string value, int? maxValueLength, int maxAttributeCount)
{
- repeatedField.Add(new OtlpCommon.KeyValue
+ var attributeItem = new KeyValuePair(key, value);
+ if (OtlpKeyValueTransformer.Instance.TryTransformTag(attributeItem, out var result, maxValueLength))
{
- Key = key,
- Value = new OtlpCommon.AnyValue { IntValue = value },
- });
+ logRecord.AddAttribute(result, maxAttributeCount);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void AddIntAttribute(this OtlpLogs.LogRecord logRecord, string key, int value, int? maxValueLength, int maxAttributeCount)
+ {
+ var attributeItem = new KeyValuePair(key, value);
+ if (OtlpKeyValueTransformer.Instance.TryTransformTag(attributeItem, out var result, maxValueLength))
+ {
+ logRecord.AddAttribute(result, maxAttributeCount);
+ }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs
index 4933871e7b2..50ad352ddfc 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs
@@ -257,6 +257,12 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric)
dataPoint.Count = (ulong)metricPoint.GetHistogramCount();
dataPoint.Sum = metricPoint.GetHistogramSum();
+ if (metricPoint.HasMinMax())
+ {
+ dataPoint.Min = metricPoint.GetHistogramMin();
+ dataPoint.Max = metricPoint.GetHistogramMax();
+ }
+
foreach (var histogramMeasurement in metricPoint.GetHistogramBuckets())
{
dataPoint.BucketCounts.Add((ulong)histogramMeasurement.BucketCount);
diff --git a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt
index bf3ab6eedd6..eedd81fc086 100644
--- a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt
+++ b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt
@@ -10,6 +10,13 @@ OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void
OpenTelemetry.Logs.LogRecord.TraceId.set -> void
OpenTelemetry.Logs.LogRecord.TraceState.set -> void
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
+OpenTelemetry.Metrics.HistogramConfiguration
+OpenTelemetry.Metrics.HistogramConfiguration.HistogramConfiguration() -> void
+OpenTelemetry.Metrics.HistogramConfiguration.RecordMinMax.get -> bool
+OpenTelemetry.Metrics.HistogramConfiguration.RecordMinMax.set -> void
+OpenTelemetry.Metrics.MetricPoint.GetHistogramMax() -> double
+OpenTelemetry.Metrics.MetricPoint.GetHistogramMin() -> double
+OpenTelemetry.Metrics.MetricPoint.HasMinMax() -> bool
static Microsoft.Extensions.DependencyInjection.MeterProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
static Microsoft.Extensions.DependencyInjection.MeterProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder) -> OpenTelemetry.Metrics.MeterProviderBuilder!
diff --git a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt
index bf3ab6eedd6..eedd81fc086 100644
--- a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt
+++ b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt
@@ -10,6 +10,13 @@ OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void
OpenTelemetry.Logs.LogRecord.TraceId.set -> void
OpenTelemetry.Logs.LogRecord.TraceState.set -> void
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
+OpenTelemetry.Metrics.HistogramConfiguration
+OpenTelemetry.Metrics.HistogramConfiguration.HistogramConfiguration() -> void
+OpenTelemetry.Metrics.HistogramConfiguration.RecordMinMax.get -> bool
+OpenTelemetry.Metrics.HistogramConfiguration.RecordMinMax.set -> void
+OpenTelemetry.Metrics.MetricPoint.GetHistogramMax() -> double
+OpenTelemetry.Metrics.MetricPoint.GetHistogramMin() -> double
+OpenTelemetry.Metrics.MetricPoint.HasMinMax() -> bool
static Microsoft.Extensions.DependencyInjection.MeterProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
static Microsoft.Extensions.DependencyInjection.MeterProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder) -> OpenTelemetry.Metrics.MeterProviderBuilder!
diff --git a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt
index bf3ab6eedd6..eedd81fc086 100644
--- a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt
@@ -10,6 +10,13 @@ OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void
OpenTelemetry.Logs.LogRecord.TraceId.set -> void
OpenTelemetry.Logs.LogRecord.TraceState.set -> void
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
+OpenTelemetry.Metrics.HistogramConfiguration
+OpenTelemetry.Metrics.HistogramConfiguration.HistogramConfiguration() -> void
+OpenTelemetry.Metrics.HistogramConfiguration.RecordMinMax.get -> bool
+OpenTelemetry.Metrics.HistogramConfiguration.RecordMinMax.set -> void
+OpenTelemetry.Metrics.MetricPoint.GetHistogramMax() -> double
+OpenTelemetry.Metrics.MetricPoint.GetHistogramMin() -> double
+OpenTelemetry.Metrics.MetricPoint.HasMinMax() -> bool
static Microsoft.Extensions.DependencyInjection.MeterProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
static Microsoft.Extensions.DependencyInjection.MeterProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder) -> OpenTelemetry.Metrics.MeterProviderBuilder!
diff --git a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt
index bf3ab6eedd6..eedd81fc086 100644
--- a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt
+++ b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt
@@ -10,6 +10,13 @@ OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void
OpenTelemetry.Logs.LogRecord.TraceId.set -> void
OpenTelemetry.Logs.LogRecord.TraceState.set -> void
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
+OpenTelemetry.Metrics.HistogramConfiguration
+OpenTelemetry.Metrics.HistogramConfiguration.HistogramConfiguration() -> void
+OpenTelemetry.Metrics.HistogramConfiguration.RecordMinMax.get -> bool
+OpenTelemetry.Metrics.HistogramConfiguration.RecordMinMax.set -> void
+OpenTelemetry.Metrics.MetricPoint.GetHistogramMax() -> double
+OpenTelemetry.Metrics.MetricPoint.GetHistogramMin() -> double
+OpenTelemetry.Metrics.MetricPoint.HasMinMax() -> bool
static Microsoft.Extensions.DependencyInjection.MeterProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
static Microsoft.Extensions.DependencyInjection.MeterProviderBuilderServiceCollectionExtensions.ConfigureOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder) -> OpenTelemetry.Metrics.MeterProviderBuilder!
diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md
index 5ea2486a319..2c6f3092d3a 100644
--- a/src/OpenTelemetry/CHANGELOG.md
+++ b/src/OpenTelemetry/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+* Make recording of `Min` and `Max` for histograms configurable, enabled by
+ default.
+ ([#2735](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2735))
+
* Changed default bucket boundaries for Explicit Bucket Histogram from [0, 5,
10, 25, 50, 75, 100, 250, 500, 1000] to [0, 5, 10, 25, 50, 75, 100, 250, 500,
750, 1000, 2500, 5000, 7500, 10000].
diff --git a/src/OpenTelemetry/Metrics/AggregationType.cs b/src/OpenTelemetry/Metrics/AggregationType.cs
index bd87c9e5b4f..eff60b2d91d 100644
--- a/src/OpenTelemetry/Metrics/AggregationType.cs
+++ b/src/OpenTelemetry/Metrics/AggregationType.cs
@@ -54,13 +54,23 @@ internal enum AggregationType
DoubleGauge = 5,
///
- /// Histogram.
+ /// Histogram with sum, count, buckets.
///
Histogram = 6,
///
- /// Histogram with sum, count only.
+ /// Histogram with sum, count, min, max, buckets.
///
- HistogramSumCount = 7,
+ HistogramMinMax = 7,
+
+ ///
+ /// Histogram with sum, count.
+ ///
+ HistogramSumCount = 8,
+
+ ///
+ /// Histogram with sum, count, min, max.
+ ///
+ HistogramSumCountMinMax = 9,
}
}
diff --git a/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs b/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs
index 58bfc245b10..8be618748fc 100644
--- a/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs
+++ b/src/OpenTelemetry/Metrics/ExplicitBucketHistogramConfiguration.cs
@@ -21,7 +21,7 @@ namespace OpenTelemetry.Metrics
///
/// Stores configuration for a histogram metric stream with explicit bucket boundaries.
///
- public class ExplicitBucketHistogramConfiguration : MetricStreamConfiguration
+ public class ExplicitBucketHistogramConfiguration : HistogramConfiguration
{
///
/// Gets or sets the optional boundaries of the histogram metric stream.
diff --git a/src/OpenTelemetry/Metrics/HistogramBuckets.cs b/src/OpenTelemetry/Metrics/HistogramBuckets.cs
index 2a2149b31b0..c0160e534ee 100644
--- a/src/OpenTelemetry/Metrics/HistogramBuckets.cs
+++ b/src/OpenTelemetry/Metrics/HistogramBuckets.cs
@@ -31,13 +31,17 @@ public class HistogramBuckets
internal readonly double[] ExplicitBounds;
internal readonly long[] RunningBucketCounts;
-
internal readonly long[] SnapshotBucketCounts;
internal double RunningSum;
-
internal double SnapshotSum;
+ internal double RunningMin = double.PositiveInfinity;
+ internal double SnapshotMin;
+
+ internal double RunningMax = double.NegativeInfinity;
+ internal double SnapshotMax;
+
internal int IsCriticalSectionOccupied = 0;
private readonly BucketLookupNode bucketLookupTreeRoot;
diff --git a/src/OpenTelemetry/Metrics/HistogramConfiguration.cs b/src/OpenTelemetry/Metrics/HistogramConfiguration.cs
new file mode 100644
index 00000000000..37980098937
--- /dev/null
+++ b/src/OpenTelemetry/Metrics/HistogramConfiguration.cs
@@ -0,0 +1,26 @@
+//
+// 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.
+//
+
+namespace OpenTelemetry.Metrics;
+
+public class HistogramConfiguration : MetricStreamConfiguration
+{
+ ///
+ /// Gets or sets a value indicating whether Min, Max
+ /// should be collected.
+ ///
+ public bool RecordMinMax { get; set; } = true;
+}
diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs
index c40e0f82ac7..6f5771121b7 100644
--- a/src/OpenTelemetry/Metrics/Metric.cs
+++ b/src/OpenTelemetry/Metrics/Metric.cs
@@ -34,7 +34,8 @@ internal Metric(
AggregationTemporality temporality,
int maxMetricPointsPerMetricStream,
double[] histogramBounds = null,
- string[] tagKeysInteresting = null)
+ string[] tagKeysInteresting = null,
+ bool histogramRecordMinMax = true)
{
this.InstrumentIdentity = instrumentIdentity;
@@ -118,15 +119,9 @@ internal Metric(
{
this.MetricType = MetricType.Histogram;
- if (histogramBounds != null
- && histogramBounds.Length == 0)
- {
- aggType = AggregationType.HistogramSumCount;
- }
- else
- {
- aggType = AggregationType.Histogram;
- }
+ aggType = histogramBounds != null && histogramBounds.Length == 0
+ ? (histogramRecordMinMax ? AggregationType.HistogramSumCountMinMax : AggregationType.HistogramSumCount)
+ : (histogramRecordMinMax ? AggregationType.HistogramMinMax : AggregationType.Histogram);
}
else
{
diff --git a/src/OpenTelemetry/Metrics/MetricPoint.cs b/src/OpenTelemetry/Metrics/MetricPoint.cs
index 55abb65b9c5..50dc46d1135 100644
--- a/src/OpenTelemetry/Metrics/MetricPoint.cs
+++ b/src/OpenTelemetry/Metrics/MetricPoint.cs
@@ -58,11 +58,13 @@ internal MetricPoint(
this.deltaLastValue = default;
this.MetricPointStatus = MetricPointStatus.NoCollectPending;
- if (this.aggType == AggregationType.Histogram)
+ if (this.aggType == AggregationType.Histogram ||
+ this.aggType == AggregationType.HistogramMinMax)
{
this.histogramBuckets = new HistogramBuckets(histogramExplicitBounds);
}
- else if (this.aggType == AggregationType.HistogramSumCount)
+ else if (this.aggType == AggregationType.HistogramSumCount ||
+ this.aggType == AggregationType.HistogramSumCountMinMax)
{
this.histogramBuckets = new HistogramBuckets(null);
}
@@ -187,7 +189,10 @@ public readonly double GetGaugeLastValueDouble()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly long GetHistogramCount()
{
- if (this.aggType != AggregationType.Histogram && this.aggType != AggregationType.HistogramSumCount)
+ if (this.aggType != AggregationType.Histogram &&
+ this.aggType != AggregationType.HistogramSumCount &&
+ this.aggType != AggregationType.HistogramMinMax &&
+ this.aggType != AggregationType.HistogramSumCountMinMax)
{
this.ThrowNotSupportedMetricTypeException(nameof(this.GetHistogramCount));
}
@@ -205,7 +210,10 @@ public readonly long GetHistogramCount()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly double GetHistogramSum()
{
- if (this.aggType != AggregationType.Histogram && this.aggType != AggregationType.HistogramSumCount)
+ if (this.aggType != AggregationType.Histogram &&
+ this.aggType != AggregationType.HistogramSumCount &&
+ this.aggType != AggregationType.HistogramMinMax &&
+ this.aggType != AggregationType.HistogramSumCountMinMax)
{
this.ThrowNotSupportedMetricTypeException(nameof(this.GetHistogramSum));
}
@@ -223,7 +231,10 @@ public readonly double GetHistogramSum()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly HistogramBuckets GetHistogramBuckets()
{
- if (this.aggType != AggregationType.Histogram && this.aggType != AggregationType.HistogramSumCount)
+ if (this.aggType != AggregationType.Histogram &&
+ this.aggType != AggregationType.HistogramSumCount &&
+ this.aggType != AggregationType.HistogramMinMax &&
+ this.aggType != AggregationType.HistogramSumCountMinMax)
{
this.ThrowNotSupportedMetricTypeException(nameof(this.GetHistogramBuckets));
}
@@ -231,6 +242,47 @@ public readonly HistogramBuckets GetHistogramBuckets()
return this.histogramBuckets;
}
+ ///
+ /// Gets the minimum value of the histogram associated with the metric
+ /// point if present. Check by using .
+ ///
+ ///
+ /// Applies to metric type.
+ ///
+ /// A histogram's maximum value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public double GetHistogramMin()
+ {
+ return this.histogramBuckets.SnapshotMin;
+ }
+
+ ///
+ /// Gets the maximum value of the histogram associated with the metric
+ /// point if present. Check by using .
+ ///
+ ///
+ /// Applies to metric type.
+ ///
+ /// A histogram's maximum value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public double GetHistogramMax()
+ {
+ return this.histogramBuckets.SnapshotMax;
+ }
+
+ ///
+ /// Gets whether the histogram has a valid Min and Max value.
+ /// Should be used before
+ /// and to
+ /// ensure a valid value is returned.
+ ///
+ /// A minimum and maximum value exist.
+ public bool HasMinMax()
+ {
+ return this.aggType == AggregationType.HistogramMinMax ||
+ this.aggType == AggregationType.HistogramSumCountMinMax;
+ }
+
internal readonly MetricPoint Copy()
{
MetricPoint copy = this;
@@ -261,7 +313,9 @@ internal void Update(long number)
}
case AggregationType.Histogram:
+ case AggregationType.HistogramMinMax:
case AggregationType.HistogramSumCount:
+ case AggregationType.HistogramSumCountMinMax:
{
this.Update((double)number);
@@ -374,6 +428,63 @@ internal void Update(double number)
sw.SpinOnce();
}
+ break;
+ }
+
+ case AggregationType.HistogramMinMax:
+ {
+ int i = this.histogramBuckets.FindBucketIndex(number);
+
+ var sw = default(SpinWait);
+ while (true)
+ {
+ if (Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 1) == 0)
+ {
+ // Lock acquired
+ unchecked
+ {
+ this.runningValue.AsLong++;
+ this.histogramBuckets.RunningSum += number;
+ this.histogramBuckets.RunningBucketCounts[i]++;
+ this.histogramBuckets.RunningMin = Math.Min(this.histogramBuckets.RunningMin, number);
+ this.histogramBuckets.RunningMax = Math.Max(this.histogramBuckets.RunningMax, number);
+ }
+
+ // Release lock
+ Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 0);
+ break;
+ }
+
+ sw.SpinOnce();
+ }
+
+ break;
+ }
+
+ case AggregationType.HistogramSumCountMinMax:
+ {
+ var sw = default(SpinWait);
+ while (true)
+ {
+ if (Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 1) == 0)
+ {
+ // Lock acquired
+ unchecked
+ {
+ this.runningValue.AsLong++;
+ this.histogramBuckets.RunningSum += number;
+ this.histogramBuckets.RunningMin = Math.Min(this.histogramBuckets.RunningMin, number);
+ this.histogramBuckets.RunningMax = Math.Max(this.histogramBuckets.RunningMax, number);
+ }
+
+ // Release lock
+ Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 0);
+ break;
+ }
+
+ sw.SpinOnce();
+ }
+
break;
}
}
@@ -501,6 +612,7 @@ internal void TakeSnapshot(bool outputDelta)
// Lock acquired
this.snapshotValue.AsLong = this.runningValue.AsLong;
this.histogramBuckets.SnapshotSum = this.histogramBuckets.RunningSum;
+
if (outputDelta)
{
this.runningValue.AsLong = 0;
@@ -539,10 +651,88 @@ internal void TakeSnapshot(bool outputDelta)
// Lock acquired
this.snapshotValue.AsLong = this.runningValue.AsLong;
this.histogramBuckets.SnapshotSum = this.histogramBuckets.RunningSum;
+
+ if (outputDelta)
+ {
+ this.runningValue.AsLong = 0;
+ this.histogramBuckets.RunningSum = 0;
+ }
+
+ this.MetricPointStatus = MetricPointStatus.NoCollectPending;
+
+ // Release lock
+ Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 0);
+ break;
+ }
+
+ sw.SpinOnce();
+ }
+
+ break;
+ }
+
+ case AggregationType.HistogramMinMax:
+ {
+ var sw = default(SpinWait);
+ while (true)
+ {
+ if (Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 1) == 0)
+ {
+ // Lock acquired
+ this.snapshotValue.AsLong = this.runningValue.AsLong;
+ this.histogramBuckets.SnapshotSum = this.histogramBuckets.RunningSum;
+ this.histogramBuckets.SnapshotMin = this.histogramBuckets.RunningMin;
+ this.histogramBuckets.SnapshotMax = this.histogramBuckets.RunningMax;
+
+ if (outputDelta)
+ {
+ this.runningValue.AsLong = 0;
+ this.histogramBuckets.RunningSum = 0;
+ this.histogramBuckets.RunningMin = double.PositiveInfinity;
+ this.histogramBuckets.RunningMax = double.NegativeInfinity;
+ }
+
+ for (int i = 0; i < this.histogramBuckets.RunningBucketCounts.Length; i++)
+ {
+ this.histogramBuckets.SnapshotBucketCounts[i] = this.histogramBuckets.RunningBucketCounts[i];
+ if (outputDelta)
+ {
+ this.histogramBuckets.RunningBucketCounts[i] = 0;
+ }
+ }
+
+ this.MetricPointStatus = MetricPointStatus.NoCollectPending;
+
+ // Release lock
+ Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 0);
+ break;
+ }
+
+ sw.SpinOnce();
+ }
+
+ break;
+ }
+
+ case AggregationType.HistogramSumCountMinMax:
+ {
+ var sw = default(SpinWait);
+ while (true)
+ {
+ if (Interlocked.Exchange(ref this.histogramBuckets.IsCriticalSectionOccupied, 1) == 0)
+ {
+ // Lock acquired
+ this.snapshotValue.AsLong = this.runningValue.AsLong;
+ this.histogramBuckets.SnapshotSum = this.histogramBuckets.RunningSum;
+ this.histogramBuckets.SnapshotMin = this.histogramBuckets.RunningMin;
+ this.histogramBuckets.SnapshotMax = this.histogramBuckets.RunningMax;
+
if (outputDelta)
{
this.runningValue.AsLong = 0;
this.histogramBuckets.RunningSum = 0;
+ this.histogramBuckets.RunningMin = double.PositiveInfinity;
+ this.histogramBuckets.RunningMax = double.NegativeInfinity;
}
this.MetricPointStatus = MetricPointStatus.NoCollectPending;
diff --git a/src/OpenTelemetry/Metrics/MetricReaderExt.cs b/src/OpenTelemetry/Metrics/MetricReaderExt.cs
index d2863d55d6e..4aa6769f346 100644
--- a/src/OpenTelemetry/Metrics/MetricReaderExt.cs
+++ b/src/OpenTelemetry/Metrics/MetricReaderExt.cs
@@ -156,8 +156,7 @@ internal List AddMetricsListWithViews(Instrument instrument, List metricIdentity1.Equals(metricIdentity2);
public static bool operator !=(MetricStreamIdentity metricIdentity1, MetricStreamIdentity metricIdentity2) => !metricIdentity1.Equals(metricIdentity2);
@@ -99,6 +103,7 @@ public bool Equals(MetricStreamIdentity other)
&& this.Unit == other.Unit
&& this.Description == other.Description
&& this.ViewId == other.ViewId
+ && this.HistogramRecordMinMax == other.HistogramRecordMinMax
&& StringArrayComparer.Equals(this.TagKeys, other.TagKeys)
&& HistogramBoundsEqual(this.HistogramBucketBounds, other.HistogramBucketBounds);
}
diff --git a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs
index 0664dc18c00..c4b8fdf944a 100644
--- a/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs
+++ b/test/OpenTelemetry.Tests/Metrics/MetricTestData.cs
@@ -49,5 +49,22 @@ public static IEnumerable