internal AggregatorStore( AggregationType aggType, AggregationTemporality temporality, double[] histogramBounds, string[] tagKeysInteresting = null) { this.metricPoints = new MetricPoint[MaxMetricPoints]; this.aggType = aggType; this.temporality = temporality; this.outputDelta = temporality == AggregationTemporality.Delta ? true : false; this.histogramBounds = histogramBounds; this.startTimeExclusive = DateTimeOffset.UtcNow; if (tagKeysInteresting == null) { this.updateLongCallback = this.UpdateLong; this.updateDoubleCallback = this.UpdateDouble; } else { this.updateLongCallback = this.UpdateLongCustomTags; this.updateDoubleCallback = this.UpdateDoubleCustomTags; var hs = new HashSet <string>(StringComparer.Ordinal); foreach (var key in tagKeysInteresting) { hs.Add(key); } this.tagKeysInteresting = hs; this.tagsKeysInterestingCount = hs.Count; } }
public void ExportOnlyWhenPointChanged(AggregationTemporality temporality) { using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{temporality}"); var exportedItems = new List <Metric>(); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddReader( new BaseExportingMetricReader(new InMemoryExporter <Metric>(exportedItems)) { Temporality = temporality, }) .Build(); var counter = meter.CreateCounter <long>("meter"); counter.Add(10, new KeyValuePair <string, object>("tag1", "value1")); meterProvider.ForceFlush(); Assert.Single(exportedItems); exportedItems.Clear(); meterProvider.ForceFlush(); if (temporality == AggregationTemporality.Cumulative) { Assert.Single(exportedItems); } else { Assert.Empty(exportedItems); } }
internal AggregatorStore( string name, AggregationType aggType, AggregationTemporality temporality, int maxMetricPoints, double[] histogramBounds, string[] tagKeysInteresting = null) { this.name = name; this.maxMetricPoints = maxMetricPoints; this.metricPointCapHitMessage = $"Maximum MetricPoints limit reached for this Metric stream. Configured limit: {this.maxMetricPoints}"; this.metricPoints = new MetricPoint[maxMetricPoints]; this.currentMetricPointBatch = new int[maxMetricPoints]; this.aggType = aggType; this.temporality = temporality; this.outputDelta = temporality == AggregationTemporality.Delta ? true : false; this.histogramBounds = histogramBounds; this.startTimeExclusive = DateTimeOffset.UtcNow; if (tagKeysInteresting == null) { this.updateLongCallback = this.UpdateLong; this.updateDoubleCallback = this.UpdateDouble; } else { this.updateLongCallback = this.UpdateLongCustomTags; this.updateDoubleCallback = this.UpdateDoubleCustomTags; var hs = new HashSet <string>(tagKeysInteresting, StringComparer.Ordinal); this.tagKeysInteresting = hs; this.tagsKeysInterestingCount = hs.Count; } }
public void TestMetricPointCap(AggregationTemporality temporality) { var metricItems = new List <Metric>(); int metricPointCount = 0; var metricExporter = new TestExporter <Metric>(ProcessExport); void ProcessExport(Batch <Metric> batch) { foreach (var metric in batch) { foreach (ref var metricPoint in metric.GetMetricPoints()) { metricPointCount++; } } } var metricReader = new BaseExportingMetricReader(metricExporter) { PreferredAggregationTemporality = temporality, }; using var meter = new Meter("TestPointCapMeter"); var counterLong = meter.CreateCounter <long>("mycounterCapTest"); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter("TestPointCapMeter") .AddReader(metricReader) .Build(); // Make one Add with no tags. // as currently we reserve 0th index // for no tag point! // This may be changed later. counterLong.Add(10); for (int i = 0; i < AggregatorStore.MaxMetricPoints + 1; i++) { counterLong.Add(10, new KeyValuePair <string, object>("key", "value" + i)); } metricReader.Collect(); Assert.Equal(AggregatorStore.MaxMetricPoints, metricPointCount); metricPointCount = 0; metricReader.Collect(); Assert.Equal(AggregatorStore.MaxMetricPoints, metricPointCount); // These updates would be dropped. counterLong.Add(10, new KeyValuePair <string, object>("key", "valueA")); counterLong.Add(10, new KeyValuePair <string, object>("key", "valueB")); counterLong.Add(10, new KeyValuePair <string, object>("key", "valueC")); metricPointCount = 0; metricReader.Collect(); Assert.Equal(AggregatorStore.MaxMetricPoints, metricPointCount); }
public void StreamNamesDuplicatesAreNotAllowedTest(AggregationTemporality temporality) { var metricItems = new List <Metric>(); int metricCount = 0; var metricExporter = new TestExporter <Metric>(ProcessExport); void ProcessExport(Batch <Metric> batch) { foreach (var metric in batch) { metricCount++; } } var metricReader = new BaseExportingMetricReader(metricExporter) { PreferredAggregationTemporality = temporality, }; using var meter1 = new Meter("TestDuplicateMetricName1"); using var meter2 = new Meter("TestDuplicateMetricName2"); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter("TestDuplicateMetricName1") .AddMeter("TestDuplicateMetricName2") .AddReader(metricReader) .Build(); // Expecting one metric stream. var counterLong = meter1.CreateCounter <long>("name1"); counterLong.Add(10); metricReader.Collect(); Assert.Equal(1, metricCount); // The following will be ignored as // metric of same name exists. // Metric stream will remain one. var anotherCounterSameName = meter1.CreateCounter <long>("name1"); anotherCounterSameName.Add(10); metricCount = 0; metricReader.Collect(); Assert.Equal(1, metricCount); // The following will also be ignored // as the name is same. // (the Meter name is not part of stream name) var anotherCounterSameNameDiffMeter = meter2.CreateCounter <long>("name1"); anotherCounterSameNameDiffMeter.Add(10); metricCount = 0; metricReader.Collect(); Assert.Equal(1, metricCount); }
public void TestInstrumentDisposal(AggregationTemporality temporality) { var metricItems = new List <Metric>(); var metricExporter = new InMemoryExporter <Metric>(metricItems); var metricReader = new BaseExportingMetricReader(metricExporter) { PreferredAggregationTemporality = temporality, }; var meter1 = new Meter($"{Utils.GetCurrentMethodName()}.{temporality}.1"); var meter2 = new Meter($"{Utils.GetCurrentMethodName()}.{temporality}.2"); var counter1 = meter1.CreateCounter <long>("counterFromMeter1"); var counter2 = meter2.CreateCounter <long>("counterFromMeter2"); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter1.Name) .AddMeter(meter2.Name) .AddReader(metricReader) .Build(); counter1.Add(10, new KeyValuePair <string, object>("key", "value")); counter2.Add(10, new KeyValuePair <string, object>("key", "value")); metricReader.Collect(); Assert.Equal(2, metricItems.Count); metricItems.Clear(); counter1.Add(10, new KeyValuePair <string, object>("key", "value")); counter2.Add(10, new KeyValuePair <string, object>("key", "value")); meter1.Dispose(); metricReader.Collect(); Assert.Equal(2, metricItems.Count); metricItems.Clear(); counter1.Add(10, new KeyValuePair <string, object>("key", "value")); counter2.Add(10, new KeyValuePair <string, object>("key", "value")); metricReader.Collect(); Assert.Single(metricItems); metricItems.Clear(); counter1.Add(10, new KeyValuePair <string, object>("key", "value")); counter2.Add(10, new KeyValuePair <string, object>("key", "value")); meter2.Dispose(); metricReader.Collect(); Assert.Single(metricItems); metricItems.Clear(); counter1.Add(10, new KeyValuePair <string, object>("key", "value")); counter2.Add(10, new KeyValuePair <string, object>("key", "value")); metricReader.Collect(); Assert.Empty(metricItems); }
public void DuplicateInstrumentNamesFromDifferentMetersAreAllowed(AggregationTemporality temporality, bool hasView) { var metricItems = new List <Metric>(); var metricExporter = new InMemoryExporter <Metric>(metricItems); var metricReader = new BaseExportingMetricReader(metricExporter) { PreferredAggregationTemporality = temporality, }; using var meter1 = new Meter($"{Utils.GetCurrentMethodName()}.1.{temporality}"); using var meter2 = new Meter($"{Utils.GetCurrentMethodName()}.2.{temporality}"); var meterProviderBuilder = Sdk.CreateMeterProviderBuilder() .AddMeter(meter1.Name) .AddMeter(meter2.Name) .AddReader(metricReader); if (hasView) { meterProviderBuilder.AddView("name1", new MetricStreamConfiguration() { Description = "description" }); } using var meterProvider = meterProviderBuilder.Build(); // Expecting one metric stream. var counterLong = meter1.CreateCounter <long>("name1"); counterLong.Add(10); metricReader.Collect(); Assert.Single(metricItems); // The following will not be ignored // as it is the same metric name but different meter. var anotherCounterSameNameDiffMeter = meter2.CreateCounter <long>("name1"); anotherCounterSameNameDiffMeter.Add(10); counterLong.Add(10); metricItems.Clear(); metricReader.Collect(); Assert.Equal(2, metricItems.Count); }
public void DuplicateInstrumentNamesFromSameMeterAreNotAllowed(AggregationTemporality temporality, bool hasView) { var metricItems = new List <Metric>(); var metricExporter = new InMemoryExporter <Metric>(metricItems); var metricReader = new BaseExportingMetricReader(metricExporter) { PreferredAggregationTemporality = temporality, }; using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{temporality}"); var meterProviderBuilder = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddReader(metricReader); if (hasView) { meterProviderBuilder.AddView("name1", new MetricStreamConfiguration() { Description = "description" }); } using var meterProvider = meterProviderBuilder.Build(); // Expecting one metric stream. var counterLong = meter.CreateCounter <long>("name1"); counterLong.Add(10); metricReader.Collect(); Assert.Single(metricItems); // The following will be ignored as // metric of same name exists. // Metric stream will remain one. var anotherCounterSameName = meter.CreateCounter <long>("name1"); anotherCounterSameName.Add(10); counterLong.Add(10); metricItems.Clear(); metricReader.Collect(); Assert.Single(metricItems); }
public void TestMetricPointCap(AggregationTemporality temporality) { var exportedItems = new List <Metric>(); int MetricPointCount() { var count = 0; foreach (var metric in exportedItems) { foreach (ref readonly var metricPoint in metric.GetMetricPoints()) { count++; } } return(count); } using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{temporality}"); var counterLong = meter.CreateCounter <long>("mycounterCapTest"); using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddReader(new BaseExportingMetricReader(new InMemoryExporter <Metric>(exportedItems)) { Temporality = temporality, }) .Build(); // Make one Add with no tags. // as currently we reserve 0th index // for no tag point! // This may be changed later. counterLong.Add(10); for (int i = 0; i < MeterProviderBuilderBase.MaxMetricPointsPerMetricDefault + 1; i++) { counterLong.Add(10, new KeyValuePair <string, object>("key", "value" + i)); } meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Equal(MeterProviderBuilderBase.MaxMetricPointsPerMetricDefault, MetricPointCount()); exportedItems.Clear(); counterLong.Add(10); for (int i = 0; i < MeterProviderBuilderBase.MaxMetricPointsPerMetricDefault + 1; i++) { counterLong.Add(10, new KeyValuePair <string, object>("key", "value" + i)); } meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Equal(MeterProviderBuilderBase.MaxMetricPointsPerMetricDefault, MetricPointCount()); counterLong.Add(10); for (int i = 0; i < MeterProviderBuilderBase.MaxMetricPointsPerMetricDefault + 1; i++) { counterLong.Add(10, new KeyValuePair <string, object>("key", "value" + i)); } // These updates would be dropped. counterLong.Add(10, new KeyValuePair <string, object>("key", "valueA")); counterLong.Add(10, new KeyValuePair <string, object>("key", "valueB")); counterLong.Add(10, new KeyValuePair <string, object>("key", "valueC")); exportedItems.Clear(); meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Equal(MeterProviderBuilderBase.MaxMetricPointsPerMetricDefault, MetricPointCount()); }
public void TestHistogramToOltpMetric(string name, string description, string unit, long?longValue, double?doubleValue, AggregationTemporality aggregationTemporality, params object[] keysValues) { var metrics = new List <Metric>(); var metricReader = new BaseExportingMetricReader(new InMemoryExporter <Metric>(metrics)); metricReader.Temporality = aggregationTemporality; using var meter = new Meter(Utils.GetCurrentMethodName()); using var provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddReader(metricReader) .Build(); var attributes = ToAttributes(keysValues).ToArray(); if (longValue.HasValue) { var histogram = meter.CreateHistogram <long>(name, unit, description); histogram.Record(longValue.Value, attributes); } else { var histogram = meter.CreateHistogram <double>(name, unit, description); histogram.Record(doubleValue.Value, attributes); } provider.ForceFlush(); var batch = new Batch <Metric>(metrics.ToArray(), metrics.Count); var request = new OtlpCollector.ExportMetricsServiceRequest(); request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch); var resourceMetric = request.ResourceMetrics.Single(); var instrumentationLibraryMetrics = resourceMetric.InstrumentationLibraryMetrics.Single(); var actual = instrumentationLibraryMetrics.Metrics.Single(); Assert.Equal(name, actual.Name); Assert.Equal(description ?? string.Empty, actual.Description); Assert.Equal(unit ?? string.Empty, actual.Unit); Assert.Equal(OtlpMetrics.Metric.DataOneofCase.Histogram, actual.DataCase); Assert.Null(actual.Gauge); Assert.Null(actual.Sum); Assert.NotNull(actual.Histogram); Assert.Null(actual.ExponentialHistogram); Assert.Null(actual.Summary); var otlpAggregationTemporality = aggregationTemporality == AggregationTemporality.Cumulative ? OtlpMetrics.AggregationTemporality.Cumulative : OtlpMetrics.AggregationTemporality.Delta; Assert.Equal(otlpAggregationTemporality, actual.Histogram.AggregationTemporality); Assert.Single(actual.Histogram.DataPoints); var dataPoint = actual.Histogram.DataPoints.First(); Assert.True(dataPoint.StartTimeUnixNano > 0); Assert.True(dataPoint.TimeUnixNano > 0); Assert.Equal(1UL, dataPoint.Count); if (longValue.HasValue) { Assert.Equal((double)longValue, dataPoint.Sum); } else { Assert.Equal(doubleValue, dataPoint.Sum); } int bucketIndex; for (bucketIndex = 0; bucketIndex < dataPoint.ExplicitBounds.Count; ++bucketIndex) { if (dataPoint.Sum <= dataPoint.ExplicitBounds[bucketIndex]) { break; } Assert.Equal(0UL, dataPoint.BucketCounts[bucketIndex]); } Assert.Equal(1UL, dataPoint.BucketCounts[bucketIndex]); if (attributes.Length > 0) { OtlpTestHelpers.AssertOtlpAttributes(attributes, dataPoint.Attributes); } else { Assert.Empty(dataPoint.Attributes); } Assert.Empty(dataPoint.Exemplars); #pragma warning disable CS0612 // Type or member is obsolete Assert.Null(actual.IntGauge); Assert.Null(actual.IntSum); Assert.Null(actual.IntHistogram); Assert.Empty(dataPoint.Labels); #pragma warning restore CS0612 // Type or member is obsolete }
public void SdkSupportsMultipleReaders(AggregationTemporality aggregationTemporality, bool hasViews) { var exportedItems1 = new List <Metric>(); using var deltaExporter1 = new InMemoryExporter <Metric>(exportedItems1); using var deltaReader1 = new BaseExportingMetricReader(deltaExporter1) { Temporality = AggregationTemporality.Delta, }; var exportedItems2 = new List <Metric>(); using var deltaExporter2 = new InMemoryExporter <Metric>(exportedItems2); using var deltaReader2 = new BaseExportingMetricReader(deltaExporter2) { Temporality = aggregationTemporality, }; using var meter = new Meter($"{Utils.GetCurrentMethodName()}.{aggregationTemporality}.{hasViews}"); var counter = meter.CreateCounter <long>("counter"); int index = 0; var values = new long[] { 100, 200, 300, 400 }; long GetValue() => values[index++]; var gauge = meter.CreateObservableGauge("gauge", () => GetValue()); var meterProviderBuilder = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) .AddReader(deltaReader1) .AddReader(deltaReader2); if (hasViews) { meterProviderBuilder.AddView("counter", "renamedCounter"); } using var meterProvider = meterProviderBuilder.Build(); counter.Add(10, new KeyValuePair <string, object>("key", "value")); meterProvider.ForceFlush(); Assert.Equal(2, exportedItems1.Count); Assert.Equal(2, exportedItems2.Count); // Check value exported for Counter this.AssertLongSumValueForMetric(exportedItems1[0], 10); this.AssertLongSumValueForMetric(exportedItems2[0], 10); // Check value exported for Gauge this.AssertLongSumValueForMetric(exportedItems1[1], 100); this.AssertLongSumValueForMetric(exportedItems2[1], 200); exportedItems1.Clear(); exportedItems2.Clear(); counter.Add(15, new KeyValuePair <string, object>("key", "value")); meterProvider.ForceFlush(); Assert.Equal(2, exportedItems1.Count); Assert.Equal(2, exportedItems2.Count); // Check value exported for Counter this.AssertLongSumValueForMetric(exportedItems1[0], 15); if (aggregationTemporality == AggregationTemporality.Delta) { this.AssertLongSumValueForMetric(exportedItems2[0], 15); } else { this.AssertLongSumValueForMetric(exportedItems2[0], 25); } // Check value exported for Gauge this.AssertLongSumValueForMetric(exportedItems1[1], 300); this.AssertLongSumValueForMetric(exportedItems2[1], 400); }
public AggregationTemporalityAttribute(AggregationTemporality supported, AggregationTemporality preferred) { this.supportedAggregationTemporality = supported; this.preferredAggregationTemporality = preferred; }
internal Metric( Instrument instrument, AggregationTemporality temporality, string metricName, string metricDescription, double[] histogramBounds = null, string[] tagKeysInteresting = null) { this.Name = metricName; this.Description = metricDescription ?? string.Empty; this.Unit = instrument.Unit ?? string.Empty; this.Meter = instrument.Meter; AggregationType aggType = default; if (instrument.GetType() == typeof(ObservableCounter <long>) || instrument.GetType() == typeof(ObservableCounter <int>) || instrument.GetType() == typeof(ObservableCounter <short>) || instrument.GetType() == typeof(ObservableCounter <byte>)) { aggType = AggregationType.LongSumIncomingCumulative; this.MetricType = MetricType.LongSum; } else if (instrument.GetType() == typeof(Counter <long>) || instrument.GetType() == typeof(Counter <int>) || instrument.GetType() == typeof(Counter <short>) || instrument.GetType() == typeof(Counter <byte>)) { aggType = AggregationType.LongSumIncomingDelta; this.MetricType = MetricType.LongSum; } else if (instrument.GetType() == typeof(Counter <double>) || instrument.GetType() == typeof(Counter <float>)) { aggType = AggregationType.DoubleSumIncomingDelta; this.MetricType = MetricType.DoubleSum; } else if (instrument.GetType() == typeof(ObservableCounter <double>) || instrument.GetType() == typeof(ObservableCounter <float>)) { aggType = AggregationType.DoubleSumIncomingCumulative; this.MetricType = MetricType.DoubleSum; } else if (instrument.GetType() == typeof(ObservableGauge <double>) || instrument.GetType() == typeof(ObservableGauge <float>)) { aggType = AggregationType.DoubleGauge; this.MetricType = MetricType.DoubleGauge; } else if (instrument.GetType() == typeof(ObservableGauge <long>) || instrument.GetType() == typeof(ObservableGauge <int>) || instrument.GetType() == typeof(ObservableGauge <short>) || instrument.GetType() == typeof(ObservableGauge <byte>)) { aggType = AggregationType.LongGauge; this.MetricType = MetricType.LongGauge; } else if (instrument.GetType() == typeof(Histogram <long>) || instrument.GetType() == typeof(Histogram <int>) || instrument.GetType() == typeof(Histogram <short>) || instrument.GetType() == typeof(Histogram <byte>) || instrument.GetType() == typeof(Histogram <float>) || instrument.GetType() == typeof(Histogram <double>)) { this.MetricType = MetricType.Histogram; if (histogramBounds != null && histogramBounds.Length == 0) { aggType = AggregationType.HistogramSumCount; } else { aggType = AggregationType.Histogram; } } else { // TODO: Log and assign some invalid Enum. } this.aggStore = new AggregatorStore(aggType, temporality, histogramBounds ?? DefaultHistogramBounds, tagKeysInteresting); this.Temporality = temporality; this.InstrumentDisposed = false; }
internal Metric( MetricStreamIdentity instrumentIdentity, AggregationTemporality temporality, int maxMetricPointsPerMetricStream, double[] histogramBounds = null, string[] tagKeysInteresting = null) { this.InstrumentIdentity = instrumentIdentity; AggregationType aggType; if (instrumentIdentity.InstrumentType == typeof(ObservableCounter <long>) || instrumentIdentity.InstrumentType == typeof(ObservableCounter <int>) || instrumentIdentity.InstrumentType == typeof(ObservableCounter <short>) || instrumentIdentity.InstrumentType == typeof(ObservableCounter <byte>)) { aggType = AggregationType.LongSumIncomingCumulative; this.MetricType = MetricType.LongSum; } else if (instrumentIdentity.InstrumentType == typeof(Counter <long>) || instrumentIdentity.InstrumentType == typeof(Counter <int>) || instrumentIdentity.InstrumentType == typeof(Counter <short>) || instrumentIdentity.InstrumentType == typeof(Counter <byte>)) { aggType = AggregationType.LongSumIncomingDelta; this.MetricType = MetricType.LongSum; } else if (instrumentIdentity.InstrumentType == typeof(Counter <double>) || instrumentIdentity.InstrumentType == typeof(Counter <float>)) { aggType = AggregationType.DoubleSumIncomingDelta; this.MetricType = MetricType.DoubleSum; } else if (instrumentIdentity.InstrumentType == typeof(ObservableCounter <double>) || instrumentIdentity.InstrumentType == typeof(ObservableCounter <float>)) { aggType = AggregationType.DoubleSumIncomingCumulative; this.MetricType = MetricType.DoubleSum; } else if (instrumentIdentity.InstrumentType == typeof(ObservableGauge <double>) || instrumentIdentity.InstrumentType == typeof(ObservableGauge <float>)) { aggType = AggregationType.DoubleGauge; this.MetricType = MetricType.DoubleGauge; } else if (instrumentIdentity.InstrumentType == typeof(ObservableGauge <long>) || instrumentIdentity.InstrumentType == typeof(ObservableGauge <int>) || instrumentIdentity.InstrumentType == typeof(ObservableGauge <short>) || instrumentIdentity.InstrumentType == typeof(ObservableGauge <byte>)) { aggType = AggregationType.LongGauge; this.MetricType = MetricType.LongGauge; } else if (instrumentIdentity.InstrumentType == typeof(Histogram <long>) || instrumentIdentity.InstrumentType == typeof(Histogram <int>) || instrumentIdentity.InstrumentType == typeof(Histogram <short>) || instrumentIdentity.InstrumentType == typeof(Histogram <byte>) || instrumentIdentity.InstrumentType == typeof(Histogram <float>) || instrumentIdentity.InstrumentType == typeof(Histogram <double>)) { this.MetricType = MetricType.Histogram; if (histogramBounds != null && histogramBounds.Length == 0) { aggType = AggregationType.HistogramSumCount; } else { aggType = AggregationType.Histogram; } } else { throw new NotSupportedException($"Unsupported Instrument Type: {instrumentIdentity.InstrumentType.FullName}"); } this.aggStore = new AggregatorStore(instrumentIdentity.InstrumentName, aggType, temporality, maxMetricPointsPerMetricStream, histogramBounds ?? DefaultHistogramBounds, tagKeysInteresting); this.Temporality = temporality; this.InstrumentDisposed = false; }
public AggregationTemporalityAttribute(AggregationTemporality temporality) { this.temporality = temporality; }
internal MeterProviderSdk( Resource resource, IEnumerable <string> meterSources, List <MeterProviderBuilderBase.InstrumentationFactory> instrumentationFactories, List <Func <Instrument, MetricStreamConfiguration> > viewConfigs, IEnumerable <MetricReader> readers) { this.Resource = resource; this.viewConfigs = viewConfigs; this.metrics = new Metric[MaxMetrics]; this.metricsCurrentBatch = new Metric[MaxMetrics]; AggregationTemporality temporality = AggregationTemporality.Cumulative; foreach (var reader in readers) { Guard.Null(reader, nameof(reader)); reader.SetParentProvider(this); // TODO: Actually support multiple readers. // Currently the last reader's temporality wins. temporality = reader.PreferredAggregationTemporality; if (this.reader == null) { this.reader = reader; } else if (this.reader is CompositeMetricReader compositeReader) { compositeReader.AddReader(reader); } else { this.reader = new CompositeMetricReader(new[] { this.reader, reader }); } } if (instrumentationFactories.Any()) { foreach (var instrumentationFactory in instrumentationFactories) { this.instrumentations.Add(instrumentationFactory.Factory()); } } // Setup Listener Func <Instrument, bool> shouldListenTo = instrument => false; if (meterSources.Any(s => s.Contains('*'))) { var regex = GetWildcardRegex(meterSources); shouldListenTo = instrument => regex.IsMatch(instrument.Meter.Name); } else if (meterSources.Any()) { var meterSourcesToSubscribe = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (var meterSource in meterSources) { meterSourcesToSubscribe.Add(meterSource); } shouldListenTo = instrument => meterSourcesToSubscribe.Contains(instrument.Meter.Name); } this.listener = new MeterListener(); var viewConfigCount = this.viewConfigs.Count; if (viewConfigCount > 0) { this.listener.InstrumentPublished = (instrument, listener) => { if (!shouldListenTo(instrument)) { OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(instrument.Name, instrument.Meter.Name, "Instrument belongs to a Meter not subscribed by the provider.", "Use AddMeter to add the Meter to the provider."); return; } // Creating list with initial capacity as the maximum // possible size, to avoid any array resize/copy internally. // There may be excess space wasted, but it'll eligible for // GC right after this method. var metricStreamConfigs = new List <MetricStreamConfiguration>(viewConfigCount); foreach (var viewConfig in this.viewConfigs) { var metricStreamConfig = viewConfig(instrument); if (metricStreamConfig != null) { metricStreamConfigs.Add(metricStreamConfig); } } if (metricStreamConfigs.Count == 0) { // No views matched. Add null // which will apply defaults. // Users can turn off this default // by adding a view like below as the last view. // .AddView(instrumentName: "*", MetricStreamConfiguration.Drop) metricStreamConfigs.Add(null); } var maxCountMetricsToBeCreated = metricStreamConfigs.Count; // Create list with initial capacity as the max metric count. // Due to duplicate/max limit, we may not end up using them // all, and that memory is wasted until Meter disposed. // TODO: Revisit to see if we need to do metrics.TrimExcess() var metrics = new List <Metric>(maxCountMetricsToBeCreated); lock (this.instrumentCreationLock) { for (int i = 0; i < maxCountMetricsToBeCreated; i++) { var metricStreamConfig = metricStreamConfigs[i]; var meterName = instrument.Meter.Name; var metricName = metricStreamConfig?.Name ?? instrument.Name; var metricStreamName = $"{meterName}.{metricName}"; if (!MeterProviderBuilderSdk.IsValidInstrumentName(metricName)) { OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored( metricName, instrument.Meter.Name, "Metric name is invalid.", "The name must comply with the OpenTelemetry specification."); continue; } if (this.metricStreamNames.Contains(metricStreamName)) { // TODO: Log that instrument is ignored // as the resulting Metric name is conflicting // with existing name. continue; } if (metricStreamConfig?.Aggregation == Aggregation.Drop) { // TODO: Log that instrument is ignored // as user explicitly asked to drop it // with View. continue; } var index = ++this.metricIndex; if (index >= MaxMetrics) { // TODO: Log that instrument is ignored // as max number of Metrics have reached. } else { Metric metric; var metricDescription = metricStreamConfig?.Description ?? instrument.Description; string[] tagKeysInteresting = metricStreamConfig?.TagKeys; double[] histogramBucketBounds = (metricStreamConfig is ExplicitBucketHistogramConfiguration histogramConfig && histogramConfig.Boundaries != null) ? histogramConfig.Boundaries : null; metric = new Metric(instrument, temporality, metricName, metricDescription, histogramBucketBounds, tagKeysInteresting); this.metrics[index] = metric; metrics.Add(metric); this.metricStreamNames.Add(metricStreamName); } } if (metrics.Count > 0) { listener.EnableMeasurementEvents(instrument, metrics); } } }; // Everything double this.listener.SetMeasurementEventCallback <double>(this.MeasurementRecordedDouble); this.listener.SetMeasurementEventCallback <float>((instrument, value, tags, state) => this.MeasurementRecordedDouble(instrument, value, tags, state)); // Everything long this.listener.SetMeasurementEventCallback <long>(this.MeasurementRecordedLong); this.listener.SetMeasurementEventCallback <int>((instrument, value, tags, state) => this.MeasurementRecordedLong(instrument, value, tags, state)); this.listener.SetMeasurementEventCallback <short>((instrument, value, tags, state) => this.MeasurementRecordedLong(instrument, value, tags, state)); this.listener.SetMeasurementEventCallback <byte>((instrument, value, tags, state) => this.MeasurementRecordedLong(instrument, value, tags, state)); this.listener.MeasurementsCompleted = (instrument, state) => this.MeasurementsCompleted(instrument, state); } else { this.listener.InstrumentPublished = (instrument, listener) => { if (!shouldListenTo(instrument)) { OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(instrument.Name, instrument.Meter.Name, "Instrument belongs to a Meter not subscribed by the provider.", "Use AddMeter to add the Meter to the provider."); return; } try { if (!MeterProviderBuilderSdk.IsValidInstrumentName(instrument.Name)) { OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored( instrument.Name, instrument.Meter.Name, "Instrument name is invalid.", "The name must comply with the OpenTelemetry specification"); return; } var meterName = instrument.Meter.Name; var metricName = instrument.Name; var metricStreamName = $"{meterName}.{metricName}"; Metric metric = null; lock (this.instrumentCreationLock) { if (this.metricStreamNames.Contains(metricStreamName)) { OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricName, instrument.Meter.Name, "Metric name conflicting with existing name.", "Either change the name of the instrument or change name using View."); return; } var index = ++this.metricIndex; if (index >= MaxMetrics) { OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(metricName, instrument.Meter.Name, "Maximum allowed Metrics for the provider exceeded.", "Use views to drop unused instruments. Or configure Provider to allow higher limit."); return; } else { metric = new Metric(instrument, temporality, metricName, instrument.Description); this.metrics[index] = metric; this.metricStreamNames.Add(metricStreamName); } } listener.EnableMeasurementEvents(instrument, metric); } catch (Exception) { OpenTelemetrySdkEventSource.Log.MetricInstrumentIgnored(instrument.Name, instrument.Meter.Name, "SDK internal error occurred.", "Contact SDK owners."); } }; // Everything double this.listener.SetMeasurementEventCallback <double>(this.MeasurementRecordedDoubleSingleStream); this.listener.SetMeasurementEventCallback <float>((instrument, value, tags, state) => this.MeasurementRecordedDoubleSingleStream(instrument, value, tags, state)); // Everything long this.listener.SetMeasurementEventCallback <long>(this.MeasurementRecordedLongSingleStream); this.listener.SetMeasurementEventCallback <int>((instrument, value, tags, state) => this.MeasurementRecordedLongSingleStream(instrument, value, tags, state)); this.listener.SetMeasurementEventCallback <short>((instrument, value, tags, state) => this.MeasurementRecordedLongSingleStream(instrument, value, tags, state)); this.listener.SetMeasurementEventCallback <byte>((instrument, value, tags, state) => this.MeasurementRecordedLongSingleStream(instrument, value, tags, state)); this.listener.MeasurementsCompleted = (instrument, state) => this.MeasurementsCompletedSingleStream(instrument, state); } this.listener.Start();
public AggregationTemporalityAttribute(AggregationTemporality supported) : this(supported, supported) { }