internal void ReportSpan(WavefrontSpan span)
 {
     // Reporter will flush it to Wavefront/proxy.
     try
     {
         reporter.Report(span);
     }
     catch (IOException e)
     {
         logger.LogWarning(0, e, "Error reporting span");
     }
 }
        internal void ReportWavefrontGeneratedData(WavefrontSpan span)
        {
            if (metricsRoot == null)
            {
                /*
                 * WavefrontSpanReporter not set, so no tracing spans will be reported as
                 * metrics/histograms.
                 */
                return;
            }

            var pointTagsDict = new Dictionary <string, string>
            {
                { Constants.ComponentTagKey, span.GetComponentTagValue() }
            };
            var  spanTags       = span.GetTagsAsMap();
            bool customTagMatch = false;

            if (redMetricsCustomTagKeys.Count > 0)
            {
                foreach (string customTagKey in redMetricsCustomTagKeys)
                {
                    if (spanTags.ContainsKey(customTagKey))
                    {
                        customTagMatch = true;
                        // Assuming at least one value exists
                        pointTagsDict[customTagKey] = spanTags[customTagKey].First();
                    }
                    else if (customTagKey == SpanKind.Key)
                    {
                        customTagMatch = true;
                        // Promote a default value for span.kind even if span doesn't have the tag
                        pointTagsDict[customTagKey] = Constants.NullTagValue;
                    }
                }
            }

            // Promote http.status_code
            if (spanTags.ContainsKey(HttpStatus.Key) && !pointTagsDict.ContainsKey(HttpStatus.Key))
            {
                customTagMatch = true;
                string httpStatusValue = spanTags[HttpStatus.Key].First();
                pointTagsDict[HttpStatus.Key] = httpStatusValue;
            }

            string application = span.GetSingleValuedTagValue(Constants.ApplicationTagKey) ??
                                 applicationTags.Application;
            string service = span.GetSingleValuedTagValue(Constants.ServiceTagKey) ??
                             applicationTags.Service;

            pointTagsDict[Constants.ApplicationTagKey] = application;
            pointTagsDict[Constants.ServiceTagKey]     = service;
            pointTagsDict[Constants.ClusterTagKey]     =
                span.GetSingleValuedTagValue(Constants.ClusterTagKey) ?? applicationTags.Cluster;
            pointTagsDict[Constants.ShardTagKey] =
                span.GetSingleValuedTagValue(Constants.ShardTagKey) ?? applicationTags.Shard;

            var pointTags = MetricTags.Concat(
                new MetricTags(OperationNameTag, span.GetOperationName()), pointTagsDict);

            // Propagate custom tags to ~component.heartbeat
            if (heartbeaterService != null && customTagMatch)
            {
                heartbeaterService.ReportCustomTags(pointTagsDict);
            }

            string metricNamePrefix = $"{application}.{service}.{span.GetOperationName()}";

            metricsRoot.Measure.Counter.Increment(new DeltaCounterOptions
                                                  .Builder(metricNamePrefix + InvocationSuffix).Tags(pointTags).Build());

            if (span.IsError())
            {
                metricsRoot.Measure.Counter.Increment(new DeltaCounterOptions
                                                      .Builder(metricNamePrefix + ErrorSuffix).Tags(pointTags).Build());
            }
            long spanDurationMicros = span.GetDurationMicros();

            // Convert duration from micros to millis and add to duration counter
            metricsRoot.Measure.Counter.Increment(new DeltaCounterOptions
                                                  .Builder(metricNamePrefix + TotalTimeSuffix).Tags(pointTags).Build(),
                                                  spanDurationMicros / 1000);
            // Update histogram with duration in micros
            if (span.IsError())
            {
                metricsRoot.Measure.Histogram.Update(
                    new WavefrontHistogramOptions.Builder(metricNamePrefix + DurationSuffix)
                    .Tags(MetricTags.Concat(pointTags, new MetricTags("error", "true")))
                    .Build(),
                    spanDurationMicros);
            }
            else
            {
                metricsRoot.Measure.Histogram.Update(
                    new WavefrontHistogramOptions.Builder(metricNamePrefix + DurationSuffix)
                    .Tags(pointTags)
                    .Build(),
                    spanDurationMicros);
            }
        }