Skip to content

Commit

Permalink
[To metric branch] Exemplar - add filtered tags (#4202)
Browse files Browse the repository at this point in the history
  • Loading branch information
cijothomas authored Feb 17, 2023
1 parent 0ae30f0 commit e6efd4d
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 26 deletions.
15 changes: 15 additions & 0 deletions src/OpenTelemetry.Exporter.Console/ConsoleMetricExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,21 @@ public override ExportResult Export(in Batch<Metric> batch)
exemplarString.Append(exemplar.TraceId);
exemplarString.Append(" SpanId: ");
exemplarString.Append(exemplar.SpanId);

if (exemplar.FilteredTags != null && exemplar.FilteredTags.Count > 0)
{
exemplarString.Append(" Filtered Tags : ");

foreach (var tag in exemplar.FilteredTags)
{
if (ConsoleTagTransformer.Instance.TryTransformTag(tag, out var result))
{
exemplarString.Append(result);
exemplarString.Append(' ');
}
}
}

exemplarString.AppendLine();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,26 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric)
byte[] spanIdBytes = new byte[8];
examplar.SpanId?.CopyTo(spanIdBytes);

dataPoint.Exemplars.Add(new OtlpMetrics.Exemplar()
var otlpExemplar = new OtlpMetrics.Exemplar
{
TimeUnixNano = (ulong)examplar.Timestamp.ToUnixTimeNanoseconds(),
TraceId = UnsafeByteOperations.UnsafeWrap(traceIdBytes),
SpanId = UnsafeByteOperations.UnsafeWrap(spanIdBytes),
AsDouble = examplar.DoubleValue,
});
};

if (examplar.FilteredTags != null)
{
foreach (var tag in examplar.FilteredTags)
{
if (OtlpKeyValueTransformer.Instance.TryTransformTag(tag, out var result))
{
otlpExemplar.FilteredAttributes.Add(result);
}
}
}

dataPoint.Exemplars.Add(otlpExemplar);
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/OpenTelemetry/Metrics/AggregatorStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ private void UpdateLong(long value, ReadOnlySpan<KeyValuePair<string, object>> t
// TODO: can special case built-in filters to be bit faster.
if (this.exemplarFilter.ShouldSample(value, tags))
{
this.metricPoints[index].UpdateWithExemplar(value);
this.metricPoints[index].UpdateWithExemplar(value, tags: default);
}
else
{
Expand Down Expand Up @@ -354,7 +354,7 @@ private void UpdateLongCustomTags(long value, ReadOnlySpan<KeyValuePair<string,
// TODO: can special case built-in filters to be bit faster.
if (this.exemplarFilter.ShouldSample(value, tags))
{
this.metricPoints[index].UpdateWithExemplar(value);
this.metricPoints[index].UpdateWithExemplar(value, tags);
}
else
{
Expand Down Expand Up @@ -385,7 +385,7 @@ private void UpdateDouble(double value, ReadOnlySpan<KeyValuePair<string, object
// TODO: can special case built-in filters to be bit faster.
if (this.exemplarFilter.ShouldSample(value, tags))
{
this.metricPoints[index].UpdateWithExemplar(value);
this.metricPoints[index].UpdateWithExemplar(value, tags: default);
}
else
{
Expand Down Expand Up @@ -416,7 +416,7 @@ private void UpdateDoubleCustomTags(double value, ReadOnlySpan<KeyValuePair<stri
// TODO: can special case built-in filters to be bit faster.
if (this.exemplarFilter.ShouldSample(value, tags))
{
this.metricPoints[index].UpdateWithExemplar(value);
this.metricPoints[index].UpdateWithExemplar(value, tags);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,62 @@ public AlignedHistogramBucketExemplarReservoir(int length)
this.snapshotExemplars = new Exemplar[length + 1];
}

public void OfferAtBoundary(int index, double value)
public void OfferAtBoundary(int index, double value, ReadOnlySpan<KeyValuePair<string, object>> tags)
{
var exemplar = default(Exemplar);
ref var exemplar = ref this.runningExemplars[index];
exemplar.Timestamp = DateTime.UtcNow;
exemplar.DoubleValue = value;
exemplar.TraceId = Activity.Current?.TraceId;
exemplar.SpanId = Activity.Current?.SpanId;
this.runningExemplars[index] = exemplar;

if (tags == default)
{
// default tag is used to indicate
// the special case where all tags provided at measurement
// recording time are stored.
// In this case, Exemplars does not have to store any tags.
// In other words, FilteredTags will be empty.
return;
}

if (exemplar.FilteredTags == null)
{
exemplar.FilteredTags = new List<KeyValuePair<string, object>>(tags.Length);
}
else
{
// Keep the list, but clear contents.
exemplar.FilteredTags.Clear();
}

// Though only those tags that are filtered need to be
// stored, finding filtered list from the full tag list
// is expensive. So all the tags are stored in hot path (this).
// During snapshot, the filtered list is calculated.
// TODO: Evaluate alternative approaches based on perf.
foreach (var tag in tags)
{
exemplar.FilteredTags.Add(tag);
}
}

public Exemplar[] Collect()
{
return this.snapshotExemplars;
}

public void SnapShot(bool reset)
public void SnapShot(ReadOnlyTagCollection actualTags, bool reset)
{
for (int i = 0; i < this.runningExemplars.Length; i++)
{
this.snapshotExemplars[i] = this.runningExemplars[i];
if (this.runningExemplars[i].FilteredTags != null)
{
// TODO: Better data structure to avoid this Linq.
// This is doing filtered = alltags - storedtags.
this.snapshotExemplars[i].FilteredTags = this.runningExemplars[i].FilteredTags.Except(actualTags.KeyAndValues.ToList()).ToList();
}

if (reset)
{
this.runningExemplars[i].Timestamp = default;
Expand Down
5 changes: 5 additions & 0 deletions src/OpenTelemetry/Metrics/Exemplar/Exemplar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,10 @@ public struct Exemplar
/// Gets the double value.
/// </summary>
public double DoubleValue { get; internal set; }

/// <summary>
/// Gets the FilteredTags (i.e any tags that were dropped during aggregation).
/// </summary>
public List<KeyValuePair<string, object>> FilteredTags { get; internal set; }
}
}
24 changes: 12 additions & 12 deletions src/OpenTelemetry/Metrics/MetricPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ internal void Update(long number)
this.MetricPointStatus = MetricPointStatus.CollectPending;
}

internal void UpdateWithExemplar(long number)
internal void UpdateWithExemplar(long number, ReadOnlySpan<KeyValuePair<string, object>> tags)
{
switch (this.aggType)
{
Expand Down Expand Up @@ -384,13 +384,13 @@ internal void UpdateWithExemplar(long number)

case AggregationType.HistogramWithBuckets:
{
this.UpdateHistogramWithBuckets((double)number, true);
this.UpdateHistogramWithBuckets((double)number, tags, true);
break;
}

case AggregationType.HistogramWithMinMaxBuckets:
{
this.UpdateHistogramWithBucketsAndMinMax((double)number, true);
this.UpdateHistogramWithBucketsAndMinMax((double)number, tags, true);
break;
}
}
Expand Down Expand Up @@ -488,7 +488,7 @@ internal void Update(double number)
this.MetricPointStatus = MetricPointStatus.CollectPending;
}

internal void UpdateWithExemplar(double number)
internal void UpdateWithExemplar(double number, ReadOnlySpan<KeyValuePair<string, object>> tags)
{
switch (this.aggType)
{
Expand Down Expand Up @@ -542,13 +542,13 @@ internal void UpdateWithExemplar(double number)

case AggregationType.HistogramWithBuckets:
{
this.UpdateHistogramWithBuckets(number, true);
this.UpdateHistogramWithBuckets(number, tags, true);
break;
}

case AggregationType.HistogramWithMinMaxBuckets:
{
this.UpdateHistogramWithBucketsAndMinMax(number, true);
this.UpdateHistogramWithBucketsAndMinMax(number, tags, true);
break;
}
}
Expand Down Expand Up @@ -692,7 +692,7 @@ internal void TakeSnapshot(bool outputDelta)
}
}

this.histogramBuckets.ExemplarReservoir?.SnapShot(outputDelta);
this.histogramBuckets.ExemplarReservoir?.SnapShot(this.Tags, outputDelta);

this.MetricPointStatus = MetricPointStatus.NoCollectPending;

Expand Down Expand Up @@ -767,7 +767,7 @@ internal void TakeSnapshot(bool outputDelta)
}
}

this.histogramBuckets.ExemplarReservoir?.SnapShot(outputDelta);
this.histogramBuckets.ExemplarReservoir?.SnapShot(this.Tags, outputDelta);
this.MetricPointStatus = MetricPointStatus.NoCollectPending;

// Release lock
Expand Down Expand Up @@ -865,7 +865,7 @@ private void UpdateHistogramWithMinMax(double number)
}
}

private void UpdateHistogramWithBuckets(double number, bool reportExemplar = false)
private void UpdateHistogramWithBuckets(double number, ReadOnlySpan<KeyValuePair<string, object>> tags = default, bool reportExemplar = false)
{
int i = this.histogramBuckets.FindBucketIndex(number);

Expand All @@ -882,7 +882,7 @@ private void UpdateHistogramWithBuckets(double number, bool reportExemplar = fal
this.histogramBuckets.RunningBucketCounts[i]++;
if (reportExemplar)
{
this.histogramBuckets.ExemplarReservoir.OfferAtBoundary(i, number);
this.histogramBuckets.ExemplarReservoir.OfferAtBoundary(i, number, tags);
}
}

Expand All @@ -895,7 +895,7 @@ private void UpdateHistogramWithBuckets(double number, bool reportExemplar = fal
}
}

private void UpdateHistogramWithBucketsAndMinMax(double number, bool reportExemplar = false)
private void UpdateHistogramWithBucketsAndMinMax(double number, ReadOnlySpan<KeyValuePair<string, object>> tags = default, bool reportExemplar = false)
{
int i = this.histogramBuckets.FindBucketIndex(number);

Expand All @@ -912,7 +912,7 @@ private void UpdateHistogramWithBucketsAndMinMax(double number, bool reportExemp
this.histogramBuckets.RunningBucketCounts[i]++;
if (reportExemplar)
{
this.histogramBuckets.ExemplarReservoir.OfferAtBoundary(i, number);
this.histogramBuckets.ExemplarReservoir.OfferAtBoundary(i, number, tags);
}

this.histogramBuckets.RunningMin = Math.Min(this.histogramBuckets.RunningMin, number);
Expand Down
8 changes: 4 additions & 4 deletions src/OpenTelemetry/ReadOnlyTagCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ namespace OpenTelemetry
// prevent accidental boxing.
public readonly struct ReadOnlyTagCollection
{
private readonly KeyValuePair<string, object>[] keyAndValues;
internal readonly KeyValuePair<string, object>[] KeyAndValues;

internal ReadOnlyTagCollection(KeyValuePair<string, object>[]? keyAndValues)
{
this.keyAndValues = keyAndValues ?? Array.Empty<KeyValuePair<string, object>>();
this.KeyAndValues = keyAndValues ?? Array.Empty<KeyValuePair<string, object>>();
}

/// <summary>
/// Gets the number of tags in the collection.
/// </summary>
public int Count => this.keyAndValues.Length;
public int Count => this.KeyAndValues.Length;

/// <summary>
/// Returns an enumerator that iterates through the tags.
Expand Down Expand Up @@ -78,7 +78,7 @@ public bool MoveNext()

if (index < this.source.Count)
{
this.Current = this.source.keyAndValues[index];
this.Current = this.source.KeyAndValues[index];

this.index++;
return true;
Expand Down

0 comments on commit e6efd4d

Please sign in to comment.