示例#1
0
        internal MeterProviderBuilder AddReader(MetricReader reader)
        {
            if (this.MetricReaders.Count >= 1)
            {
                throw new InvalidOperationException("Only one Metricreader is allowed.");
            }

            this.MetricReaders.Add(reader);
            return(this);
        }
示例#2
0
            public bool MoveNext()
            {
                if (this.node != null)
                {
                    this.Current = this.node.Value;
                    this.node    = this.node.Next;
                    return(true);
                }

                return(false);
            }
示例#3
0
        public CompositeMetricReader AddReader(MetricReader reader)
        {
            Guard.Null(reader, nameof(reader));

            var node = new DoublyLinkedListNode(reader)
            {
                Previous = this.tail,
            };

            this.tail.Next = node;
            this.tail      = node;

            return(this);
        }
示例#4
0
        public CompositeMetricReader AddReader(MetricReader reader)
        {
            Guard.ThrowIfNull(reader);

            var node = new DoublyLinkedListNode(reader)
            {
                Previous = this.tail,
            };

            this.tail.Next = node;
            this.tail      = node;
            this.count++;

            return(this);
        }
            static bool TryFindExporter(MetricReader reader, out T exporter)
            {
                if (reader is BaseExportingMetricReader exportingMetricReader)
                {
                    exporter = exportingMetricReader.Exporter as T;
                    return(exporter != null);
                }

                if (reader is CompositeMetricReader compositeMetricReader)
                {
                    foreach (MetricReader childReader in compositeMetricReader)
                    {
                        if (TryFindExporter(childReader, out exporter))
                        {
                            return(true);
                        }
                    }
                }

                exporter = null;
                return(false);
            }
        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();
        internal MeterProviderSdk(
            Resource resource,
            IEnumerable <string> meterSources,
            List <MeterProviderBuilderBase.InstrumentationFactory> instrumentationFactories,
            List <Func <Instrument, MetricStreamConfiguration> > viewConfigs,
            int maxMetricStreams,
            int maxMetricPointsPerMetricStream,
            IEnumerable <MetricReader> readers)
        {
            this.Resource    = resource;
            this.viewConfigs = viewConfigs;

            foreach (var reader in readers)
            {
                Guard.Null(reader, nameof(reader));

                reader.SetParentProvider(this);
                reader.SetMaxMetricStreams(maxMetricStreams);
                reader.SetMaxMetricPointsPerMetricStream(maxMetricPointsPerMetricStream);

                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 });
                }
            }

            this.compositeMetricReader = this.reader as CompositeMetricReader;

            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;

            // We expect that all the readers to be added are provided before MeterProviderSdk is built.
            // If there are no readers added, we do not enable measurements for the instruments.
            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);
                    }

                    if (this.reader != null)
                    {
                        if (this.compositeMetricReader == null)
                        {
                            var metrics = this.reader.AddMetricsListWithViews(instrument, metricStreamConfigs);
                            if (metrics.Count > 0)
                            {
                                listener.EnableMeasurementEvents(instrument, metrics);
                            }
                        }
                        else
                        {
                            var metricsSuperList = this.compositeMetricReader.AddMetricsSuperListWithViews(instrument, metricStreamConfigs);
                            if (metricsSuperList.Any(metrics => metrics.Count > 0))
                            {
                                listener.EnableMeasurementEvents(instrument, metricsSuperList);
                            }
                        }
                    }
                };

                // 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;
                        }

                        if (this.reader != null)
                        {
                            if (this.compositeMetricReader == null)
                            {
                                var metric = this.reader.AddMetricWithNoViews(instrument);
                                if (metric != null)
                                {
                                    listener.EnableMeasurementEvents(instrument, metric);
                                }
                            }
                            else
                            {
                                var metrics = this.compositeMetricReader.AddMetricsWithNoViews(instrument);
                                if (metrics.Any(metric => metric != null))
                                {
                                    listener.EnableMeasurementEvents(instrument, metrics);
                                }
                            }
                        }
                    }
                    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();
        /// <summary>
        /// Adds a reader to the provider.
        /// </summary>
        /// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
        /// <param name="reader"><see cref="MetricReader"/>.</param>
        /// <returns><see cref="MeterProvider"/>.</returns>
        public static MeterProviderBuilder AddReader(this MeterProviderBuilder meterProviderBuilder, MetricReader reader)
        {
            if (meterProviderBuilder is MeterProviderBuilderBase meterProviderBuilderBase)
            {
                return(meterProviderBuilderBase.AddReader(reader));
            }

            return(meterProviderBuilder);
        }
 internal MeterProviderBuilder AddReader(MetricReader reader)
 {
     this.MetricReaders.Add(reader);
     return(this);
 }
示例#10
0
 public DoublyLinkedListNode(MetricReader value)
 {
     this.Value = value;
 }