public MeterForwarder(IMeter meter)
        {
            _meter = meter;

            _listener = new MeterListener();
            _listener.InstrumentPublished = InstrumentPublished;
            _listener.SetMeasurementEventCallback <long>(MeasurementCallback);
            _listener.Start();
        }
 private void InstrumentPublished(Instrument instrument, MeterListener listener)
 {
     if (instrument.Meter.Name == MetricTracker.MeterName && instrument.Name == MetricTracker.Names.Operations)
     {
         // For logging meters, only track the operation duration histogram for now.
         // That's currently the way the IMeter.ValueRecorder API is used, it's passing the service and assuming
         // that the meter is always the request duration. This seems incorrect, but we'll retain for consistency for now.
         listener.EnableMeasurementEvents(instrument);
     }
 }
        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();
        internal MeterProviderSdk(
            Resource resource,
            IEnumerable <string> meterSources,
            List <MeterProviderBuilderSdk.InstrumentationFactory> instrumentationFactories,
            MetricProcessor[] metricProcessors)
        {
            this.Resource = resource;

            // TODO: Replace with single CompositeProcessor.
            this.metricProcessors.AddRange(metricProcessors);

            foreach (var processor in this.metricProcessors)
            {
                processor.SetGetMetricFunction(this.Collect);
                processor.SetParentProvider(this);
            }

            if (instrumentationFactories.Any())
            {
                foreach (var instrumentationFactory in instrumentationFactories)
                {
                    this.instrumentations.Add(instrumentationFactory.Factory());
                }
            }

            // Setup Listener
            var meterSourcesToSubscribe = new Dictionary <string, bool>(StringComparer.OrdinalIgnoreCase);

            foreach (var name in meterSources)
            {
                meterSourcesToSubscribe[name] = true;
            }

            this.listener = new MeterListener()
            {
                InstrumentPublished = (instrument, listener) =>
                {
                    if (meterSourcesToSubscribe.ContainsKey(instrument.Meter.Name))
                    {
                        var aggregatorStore = new AggregatorStore(instrument);

                        // Lock to prevent new instrument (aggregatorstore)
                        // from being added while Collect is going on.
                        lock (this.collectLock)
                        {
                            this.AggregatorStores.TryAdd(aggregatorStore, true);
                            listener.EnableMeasurementEvents(instrument, aggregatorStore);
                        }
                    }
                },
                MeasurementsCompleted = (instrument, state) => this.MeasurementsCompleted(instrument, state),
            };

            // Everything double
            this.listener.SetMeasurementEventCallback <double>((i, m, l, c) => this.MeasurementRecorded(i, m, l, c));
            this.listener.SetMeasurementEventCallback <float>((i, m, l, c) => this.MeasurementRecorded(i, (double)m, l, c));

            // Everything long
            this.listener.SetMeasurementEventCallback <long>((i, m, l, c) => this.MeasurementRecorded(i, m, l, c));
            this.listener.SetMeasurementEventCallback <int>((i, m, l, c) => this.MeasurementRecorded(i, (long)m, l, c));
            this.listener.SetMeasurementEventCallback <short>((i, m, l, c) => this.MeasurementRecorded(i, (long)m, l, c));
            this.listener.SetMeasurementEventCallback <byte>((i, m, l, c) => this.MeasurementRecorded(i, (long)m, l, c));

            this.listener.Start();
        }