void IPacket.ReadFields(PacketDefinition definition, SerializedPacket packet)
        {
            switch (definition.Version)
            {
            case 1:
                int rawSampleType;
                packet.GetField("metricSampleType", out rawSampleType);
                m_MetricSampleType = (MetricSampleType)rawSampleType;
                break;

            default:
                throw new GibraltarPacketVersionException(definition.Version);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Indicates whether two samples are required to calculate a metric value or not.
        /// </summary>
        /// <remarks>
        /// Many sample types require multiple samples to determine an output value because they work with
        /// the change between two points.
        /// </remarks>
        internal static bool SampledMetricTypeRequiresMultipleSamples(MetricSampleType metricSampleType)
        {
            bool multipleRequired;

            //based purely on the counter type, according to Microsoft documentation
            switch (metricSampleType)
            {
            case MetricSampleType.RawFraction:
            case MetricSampleType.RawCount:
                //these just require one sample
                multipleRequired = false;
                break;

            default:
                //everything else requires more than one sample
                multipleRequired = true;
                break;
            }

            return(multipleRequired);
        }
        /// <summary>
        /// Generate a display caption for the supplied sample metric type
        /// </summary>
        /// <param name="metricSampleType">The sample metric type to make a caption for</param>
        /// <returns>An end-user display caption</returns>
        public static string SampledMetricTypeCaption(MetricSampleType metricSampleType)
        {
            string returnVal;

            switch (metricSampleType)
            {
            case MetricSampleType.TotalCount:
                returnVal = "Count of Items";
                break;

            case MetricSampleType.TotalFraction:
                returnVal = "Percentage";
                break;

            case MetricSampleType.IncrementalCount:
                returnVal = "Count of Items";
                break;

            case MetricSampleType.IncrementalFraction:
                returnVal = "Percentage";
                break;

            case MetricSampleType.RawCount:
                returnVal = "Count of Items";
                break;

            case MetricSampleType.RawFraction:
                returnVal = "Percentage";
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(metricSampleType));
            }

            return(returnVal);
        }
        /// <summary>
        /// Compute the counter value for this sample compared with the provided baseline sample (if any)
        /// </summary>
        /// <remarks>
        /// A baseline sample is required when the current metric requires multiple samples to determine results.
        /// The baseline sample must be for a date and time prior to this sample for correct results.
        /// </remarks>
        /// <param name="baselineSample">The previous baseline sample to calculate a difference for</param>
        /// <returns>The calculated counter value</returns>
        public override double ComputeValue(SampledMetricSample baselineSample)
        {
            if ((baselineSample == null) && (RequiresMultipleSamples))
            {
                throw new ArgumentNullException(nameof(baselineSample), "A baseline metric sample is required and none was provided.");
            }

            if ((baselineSample != null) && (baselineSample.Timestamp > Timestamp))
            {
                throw new ArgumentOutOfRangeException(nameof(baselineSample), baselineSample.Timestamp, "The baseline sample must be for a date & time before this sample to be valid for comparison.");
            }

            //Now lets do some math!  The math we have to do depends on the sampled metric type.
            MetricSampleType metricType = Metric.Definition.MetricSampleType;

            //First, eliminate the values that don't need math at all
            if (RequiresMultipleSamples == false)
            {
                return Value;
            }

            //and now we're down to stuff that requires math.
            double calculatedResult;
            CustomSampledMetricSamplePacket baselineSamplePacket = (CustomSampledMetricSamplePacket) baselineSample.Packet;

            if (metricType == MetricSampleType.TotalCount)
            {
                //here we want to calculate the difference between the start and end of our sampled period, ignoring interim samples.
                calculatedResult = Packet.RawValue - baselineSamplePacket.RawValue;
            }
            else if (metricType == MetricSampleType.TotalFraction)
            {
                double valueDelta = Packet.RawValue - baselineSamplePacket.RawValue;
                double baseDelta = Packet.BaseValue - baselineSamplePacket.BaseValue;

                //Protect from a divide by zero case.
                if ((baseDelta == 0) && (valueDelta != 0))
                {
                    throw new DivideByZeroException(string.Format(CultureInfo.InvariantCulture, "The baseline delta is zero however the value delta is not, indicating a data collection problem in the original data.  Value delta: {0}", valueDelta));
                }

                calculatedResult = valueDelta / baseDelta;
            }
            else if (metricType == MetricSampleType.IncrementalCount) 
            {
                //The new value is just the total at the end, so we just get the value property which knows enough to sum things.
                calculatedResult = Value;
            }
            else if (metricType == MetricSampleType.IncrementalFraction)
            {
                double value = Value;
                double baseValue = BaseValue;

                //Protect from a divide by zero case.
                if ((baseValue == 0) && (value != 0))
                {
                    throw new DivideByZeroException(string.Format(CultureInfo.InvariantCulture, "The baseline value is zero however the value is not, indicating a data collection problem in the original data.  Value: {0}", value));
                }

                calculatedResult = value / baseValue;
            }
            else 
            {
                // This is dumb, but FxCop doesn't seem to notice that the duplicate casts are in non-overlapping code paths.
                // So to make it happy, moving the cast outside these last two if's (now nested instead of chained).
                // Note: This will throw an exception if it fails to cast, before we check the MetricSampleType enum.
                CustomSampledMetricSample customSample = (CustomSampledMetricSample)baselineSample;
                if (metricType == MetricSampleType.RawCount)
                {
                    //we need to do a weighted average of the values in the range
                    //now life gets more fun - we have to do a weighted average of everything in between the baseline sample and this sample.
                    CustomSampledMetricSample[] samples = SampleRange(customSample);
                    calculatedResult = CalculateWeightedAverageValue(samples);
                }
                else if (metricType == MetricSampleType.RawFraction)
                {
                    //we do a weighted average of the values in the range, then divide
                    CustomSampledMetricSample[] samples = SampleRange(customSample);
                    double value = CalculateWeightedAverageValue(samples);
                    double baseValue = CalculateWeightedAverageBaseValue(samples);

                    //Protect from a divide by zero case.
                    if ((baseValue == 0) && (value != 0))
                    {
                        throw new DivideByZeroException(string.Format(CultureInfo.InvariantCulture, "The baseline value is zero however the value is not, indicating a data collection problem in the original data.  Value: {0}", value));
                    }

                    calculatedResult = value / baseValue;
                }
                else
                {
                    //oh hell.  We probably should have used a switch statement, but I didn't. Why?  Perhaps someone will 
                    //call that out in code review, but I think it was because of how much code is in each of these cases.
                    throw new ArgumentOutOfRangeException();
                }
            }

            return calculatedResult;
        }
 /// <summary>
 /// Create a new custom sampled metric definition packet from the provided information
 /// </summary>
 /// <remarks>Definition packets are the lightweight internals used for persistence.</remarks>
 /// <param name="metricTypeName">The unique metric type</param>
 /// <param name="categoryName">The name of the category with which this definition is associated.</param>
 /// <param name="counterName">The name of the definition within the category.</param>
 /// <param name="metricSampleType">The specific unit representation of the data being captured for this metric</param>
 /// <param name="unitCaption">The display caption for the calculated values captured under this metric.</param>
 /// <param name="description">A description of what is tracked by this metric, suitable for end-user display.</param>
 public CustomSampledMetricDefinitionPacket(string metricTypeName, string categoryName, string counterName, MetricSampleType metricSampleType, string unitCaption, string description)
     : base(metricTypeName, categoryName, counterName, unitCaption, description)
 {
     m_MetricSampleType = metricSampleType;
 }
 /// <summary>
 /// Create a new custom sampled metric definition packet from the provided information
 /// </summary>
 /// <remarks>Definition packets are the lightweight internals used for persistence.</remarks>
 /// <param name="metricTypeName">The unique metric type</param>
 /// <param name="categoryName">The name of the category with which this definition is associated.</param>
 /// <param name="counterName">The name of the definition within the category.</param>
 /// <param name="metricSampleType">The specific unit representation of the data being captured for this metric</param>
 public CustomSampledMetricDefinitionPacket(string metricTypeName, string categoryName, string counterName, MetricSampleType metricSampleType)
     : base(metricTypeName, categoryName, counterName)
 {
     m_MetricSampleType = metricSampleType;
 }
Ejemplo n.º 7
0
 /// <summary>
 /// Create a new metric definition for the active log.
 /// </summary>
 /// <remarks>At any one time there should only be one metric definition with a given combination of
 /// metric type, category, and counter name.  These values together are used to correlate metrics
 /// between sessions.  The metric definition will automatically be added to the provided collection.</remarks>
 /// <param name="metricTypeName">The unique metric type</param>
 /// <param name="categoryName">The name of the category with which this definition is associated.</param>
 /// <param name="counterName">The name of the definition within the category.</param>
 /// <param name="metricSampleType">The type of data captured for each metric under this definition.</param>
 /// <param name="unitCaption">The display caption for the calculated values captured under this metric.</param>
 /// <param name="description">A description of what is tracked by this metric, suitable for end-user display.</param>
 public CustomSampledMetricDefinition(string metricTypeName, string categoryName, string counterName, MetricSampleType metricSampleType, string unitCaption, string description)
     : base(Log.Metrics, new CustomSampledMetricDefinitionPacket(metricTypeName, categoryName, counterName, metricSampleType, unitCaption, description))
 {
     m_RequiresMultipleSamples = SampledMetricTypeRequiresMultipleSamples(metricSampleType);
 }
Ejemplo n.º 8
0
 /// <summary>Creates a new metric definition from the provided information, or returns an existing matching definition if found.</summary>
 /// <remarks>If the metric definition doesn't exist, it will be created.
 /// If the metric definition does exist, but is not a Custom Sampled Metric (or a derived class) an exception will be thrown.
 /// Definitions are looked up and added to the active logging metrics collection (Log.Metrics)</remarks>
 /// <param name="metricTypeName">The unique metric type</param>
 /// <param name="categoryName">The name of the category with which this definition is associated.</param>
 /// <param name="counterName">The name of the definition within the category.</param>
 /// <param name="metricSampleType">The type of data captured for each metric under this definition.</param>
 /// <param name="unitCaption">The display caption for the calculated values captured under this metric.</param>
 /// <param name="description">A description of what is tracked by this metric, suitable for end-user display.</param>
 public static CustomSampledMetricDefinition AddOrGet(string metricTypeName, string categoryName, string counterName, MetricSampleType metricSampleType, string unitCaption, string description)
 {
     //just forward into our call that requires the definition to be specified
     return(AddOrGet(Log.Metrics, metricTypeName, categoryName, counterName, metricSampleType, unitCaption, description));
 }
Ejemplo n.º 9
0
        /// <summary>Creates a new metric definition from the provided information, or returns an existing matching definition if found.</summary>
        /// <remarks>If the metric definition doesn't exist, it will be created.
        /// If the metric definition does exist, but is not a Custom Sampled Metric (or a derived class) an exception will be thrown.
        /// Definitions are looked up and added to the provided definitions dictionary.</remarks>
        /// <param name="definitions">The definitions dictionary this definition is a part of</param>
        /// <param name="metricTypeName">The unique metric type</param>
        /// <param name="categoryName">The name of the category with which this definition is associated.</param>
        /// <param name="counterName">The name of the definition within the category.</param>
        /// <param name="metricSampleType">The type of data captured for each metric under this definition.</param>
        /// <param name="unitCaption">The display caption for the calculated values captured under this metric.</param>
        /// <param name="description">A description of what is tracked by this metric, suitable for end-user display.</param>
        public static CustomSampledMetricDefinition AddOrGet(MetricDefinitionCollection definitions, string metricTypeName, string categoryName, string counterName, MetricSampleType metricSampleType, string unitCaption, string description)
        {
            //we must have a definitions collection, or we have a problem
            if (definitions == null)
            {
                throw new ArgumentNullException(nameof(definitions));
            }

            //we need to find the definition, adding it if necessary
            string            definitionKey = GetKey(metricTypeName, categoryName, counterName);
            IMetricDefinition definition;

            //we need the try and create to be atomic in a multi-threaded environment
            lock (definitions.Lock)
            {
                if (definitions.TryGetValue(definitionKey, out definition))
                {
                    //if the metric definition exists, but is of the wrong type we have a problem.
                    if ((definition is CustomSampledMetricDefinition) == false)
                    {
                        throw new ArgumentException("A metric already exists with the provided type, category, and counter name but it is not compatible with being a custom sampled metric.  Please use a different counter name.", nameof(counterName));
                    }
                }
                else
                {
                    //we didn't find one, make a new one
                    definition = new CustomSampledMetricDefinition(definitions, metricTypeName, categoryName, counterName,
                                                                   metricSampleType, unitCaption, description);
                }
            }

            return((CustomSampledMetricDefinition)definition);
        }
Ejemplo n.º 10
0
 /// <summary>
 /// Create a new metric definition.
 /// </summary>
 /// <remarks>At any one time there should only be one metric definition with a given combination of
 /// metric type, category, and counter name.  These values together are used to correlate metrics
 /// between sessions.  The metric definition will automatically be added to the provided collection.</remarks>
 /// <param name="definitions">The definitions dictionary this definition is a part of</param>
 /// <param name="metricTypeName">The unique metric type</param>
 /// <param name="categoryName">The name of the category with which this definition is associated.</param>
 /// <param name="counterName">The name of the definition within the category.</param>
 /// <param name="metricSampleType">The type of data captured for each metric under this definition.</param>
 public CustomSampledMetricDefinition(MetricDefinitionCollection definitions, string metricTypeName, string categoryName, string counterName, MetricSampleType metricSampleType)
     : base(definitions, new CustomSampledMetricDefinitionPacket(metricTypeName, categoryName, counterName, metricSampleType))
 {
     m_RequiresMultipleSamples = SampledMetricTypeRequiresMultipleSamples(metricSampleType);
 }
        /// <summary>Creates a new metric instance from the provided definition information, or returns any existing instance if found.</summary>
        /// <remarks>If the metric definition doesn't exist, it will be created.  If the metric doesn't exist, it will be created.
        /// If the metric definition does exist, but is not a Custom Sampled Metric (or a derived class) an exception will be thrown.</remarks>
        /// <param name="definitions">The definitions dictionary this definition is a part of</param>
        /// <param name="metricTypeName">The unique metric type</param>
        /// <param name="categoryName">The name of the category with which this definition is associated.</param>
        /// <param name="counterName">The name of the definition within the category.</param>
        /// <param name="metricSampleType">The type of data captured for each metric under this definition.</param>
        /// <param name="instanceName">The unique name of this instance within the metric's collection.</param>
        public static CustomSampledMetric AddOrGet(MetricDefinitionCollection definitions, string metricTypeName, string categoryName, string counterName, MetricSampleType metricSampleType, string instanceName)
        {
            //we must have a definitions collection, or we have a problem
            if (definitions == null)
            {
                throw new ArgumentNullException(nameof(definitions));
            }

            //we need to find the definition, adding it if necessary
            string            definitionKey = MetricDefinition.GetKey(metricTypeName, categoryName, counterName);
            IMetricDefinition definition;

            //Establish a lock on the definitions collection so our lookup & create are atomic.
            lock (definitions.Lock)
            {
                if (definitions.TryGetValue(definitionKey, out definition))
                {
                    //if the metric definition exists, but is of the wrong type we have a problem.
                    if ((definition is CustomSampledMetricDefinition) == false)
                    {
                        throw new ArgumentException("A metric already exists with the provided type, category, and counter name but it is not compatible with being a custom sampled metric.  Please use a different counter name.", nameof(counterName));
                    }
                }
                else
                {
                    //we didn't find one, make a new one
                    definition =
                        new CustomSampledMetricDefinition(definitions, metricTypeName, categoryName, counterName,
                                                          metricSampleType);
                }
            }

            //now we have our definition, proceed to create a new metric if it doesn't exist
            string  metricKey = MetricDefinition.GetKey(metricTypeName, categoryName, counterName, instanceName);
            IMetric metric;

            //see if we can get the metric already.  If not, we'll create it
            lock (((MetricCollection)definition.Metrics).Lock)
            {
                if (definition.Metrics.TryGetValue(metricKey, out metric) == false)
                {
                    metric = new CustomSampledMetric((CustomSampledMetricDefinition)definition, instanceName);
                }
            }

            return((CustomSampledMetric)metric);
        }
 /// <summary>Creates a new metric instance from the provided definition information, or returns any existing instance if found.</summary>
 /// <remarks>If the metric definition doesn't exist, it will be created.  If the metric doesn't exist, it will be created.
 /// If the metric definition does exist, but is not a Custom Sampled Metric (or a derived class) an exception will be thrown.
 /// Definitions are looked up and added to the active logging metrics collection (Log.Metrics)</remarks>
 /// <param name="metricTypeName">The unique metric type</param>
 /// <param name="categoryName">The name of the category with which this definition is associated.</param>
 /// <param name="counterName">The name of the definition within the category.</param>
 /// <param name="metricSampleType">The type of data captured for each metric under this definition.</param>
 /// <param name="instanceName">The unique name of this instance within the metric's collection.</param>
 public static CustomSampledMetric AddOrGet(string metricTypeName, string categoryName, string counterName, MetricSampleType metricSampleType, string instanceName)
 {
     //just forward into our call that requires the definition to be specified
     return(AddOrGet(Log.Metrics, metricTypeName, categoryName, counterName, metricSampleType, instanceName));
 }