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