Skip to content

Commit 9c0aba8

Browse files
[OTLP] Use packed format for metric histograms
Use packed format for metric histograms. Resolves #6538.
1 parent ac8d45e commit 9c0aba8

File tree

3 files changed

+82
-10
lines changed

3 files changed

+82
-10
lines changed

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ Notes](../../RELEASENOTES.md).
77

88
## Unreleased
99

10+
* Fixed an issue where OTLP export of some metrics payloads to a some OTLP
11+
receivers were rejected with an HTTP 400 response due to gRPC protocol errors.
12+
([#6567](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6567))
13+
1014
## 1.13.0
1115

1216
Released 2025-Oct-01

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,7 @@ private static int WriteMetric(byte[] buffer, int writePosition, Metric metric)
292292
writePosition = ProtobufSerializer.WriteDoubleWithTag(buffer, writePosition, ProtobufOtlpMetricFieldNumberConstants.HistogramDataPoint_Max, max);
293293
}
294294

295-
foreach (var histogramMeasurement in metricPoint.GetHistogramBuckets())
296-
{
297-
var bucketCount = (ulong)histogramMeasurement.BucketCount;
298-
writePosition = ProtobufSerializer.WriteFixed64WithTag(buffer, writePosition, ProtobufOtlpMetricFieldNumberConstants.HistogramDataPoint_Bucket_Counts, bucketCount);
299-
300-
if (histogramMeasurement.ExplicitBound != double.PositiveInfinity)
301-
{
302-
writePosition = ProtobufSerializer.WriteDoubleWithTag(buffer, writePosition, ProtobufOtlpMetricFieldNumberConstants.HistogramDataPoint_Explicit_Bounds, histogramMeasurement.ExplicitBound);
303-
}
304-
}
295+
writePosition = WriteHistogramBuckets(buffer, writePosition, metricPoint.GetHistogramBuckets());
305296

306297
writePosition = WriteDoubleExemplars(buffer, writePosition, ProtobufOtlpMetricFieldNumberConstants.HistogramDataPoint_Exemplars, in metricPoint);
307298

@@ -513,4 +504,77 @@ private static int WriteExemplar(byte[] buffer, int writePosition, in Exemplar e
513504
ProtobufSerializer.WriteReservedLength(buffer, exemplarLengthPosition, writePosition - (exemplarLengthPosition + ReserveSizeForLength));
514505
return writePosition;
515506
}
507+
508+
private static int WriteHistogramBuckets(byte[] buffer, int writePosition, HistogramBuckets buckets)
509+
{
510+
writePosition = WriteBucketCounts(buffer, writePosition, buckets.BucketCounts);
511+
512+
writePosition = WriteExplicitBounds(buffer, writePosition, buckets.ExplicitBounds!);
513+
514+
return writePosition;
515+
516+
static int WriteBucketCounts(byte[] buffer, int writePosition, HistogramBuckets.HistogramBucketValues[] values)
517+
{
518+
int length = values.Length;
519+
520+
writePosition = WritePackedLength(
521+
buffer,
522+
writePosition,
523+
length,
524+
ProtobufOtlpMetricFieldNumberConstants.HistogramDataPoint_Bucket_Counts);
525+
526+
for (int i = 0; i < length; i++)
527+
{
528+
writePosition = ProtobufSerializer.WriteFixed64LittleEndianFormat(
529+
buffer,
530+
writePosition,
531+
(ulong)values[i].SnapshotValue);
532+
}
533+
534+
return writePosition;
535+
}
536+
537+
static int WriteExplicitBounds(byte[] buffer, int writePosition, double[] values)
538+
{
539+
int length = 0;
540+
541+
for (int i = 0; i < values.Length; i++)
542+
{
543+
if (values[i] != double.PositiveInfinity)
544+
{
545+
length++;
546+
}
547+
}
548+
549+
if (length > 0)
550+
{
551+
writePosition = WritePackedLength(
552+
buffer,
553+
writePosition,
554+
length,
555+
ProtobufOtlpMetricFieldNumberConstants.HistogramDataPoint_Explicit_Bounds);
556+
557+
for (int i = 0; i < values.Length; i++)
558+
{
559+
var value = values[i];
560+
if (value != double.PositiveInfinity)
561+
{
562+
writePosition = ProtobufSerializer.WriteDouble(buffer, writePosition, value);
563+
}
564+
}
565+
}
566+
567+
return writePosition;
568+
}
569+
570+
static int WritePackedLength(byte[] buffer, int writePosition, int length, int fieldNumber)
571+
{
572+
return ProtobufSerializer.WriteTagAndLength(
573+
buffer,
574+
writePosition,
575+
length * 8,
576+
fieldNumber,
577+
ProtobufWireType.LEN);
578+
}
579+
}
516580
}

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufSerializer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ internal static int WriteDoubleWithTag(byte[] buffer, int writePosition, int fie
160160
return writePosition;
161161
}
162162

163+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
164+
internal static int WriteDouble(byte[] buffer, int writePosition, double value)
165+
=> WriteFixed64LittleEndianFormat(buffer, writePosition, (ulong)BitConverter.DoubleToInt64Bits(value));
166+
163167
/// <summary>
164168
/// Computes the number of bytes required to encode a 64-bit unsigned integer in Protocol Buffers' varint format.
165169
/// </summary>

0 commit comments

Comments
 (0)