Beispiel #1
0
        private MetricValueCollection OnCalculateValues(MetricSampleInterval interval, int intervals, DateTimeOffset startDateTime, DateTimeOffset endDateTime)
        {
            EnsureSorted();

            MetricValueCollection newMetricValueCollection = new MetricValueCollection("Log Messages", "The number of log messages recorded per interval.", interval, intervals, "Messages");

            //And prep for our process loop
            DateTimeOffset windowStartDateTime = startDateTime;
            DateTimeOffset windowEndDateTime   = CalculateOffset(windowStartDateTime, interval, intervals);


            //because we're doing a simple count by time interval, it's easiest to iterate
            //through all of the intervals and then count the matches within.
            int curSampleIndex = 0;

            //roll through the samples to find our first sample index we care about so we can count from there.
            for (curSampleIndex = 0; curSampleIndex < Count; curSampleIndex++)
            {
                if (this[curSampleIndex].Timestamp >= startDateTime)
                {
                    //bingo!  this is the first one we want
                    break;
                }
            }

            while (windowEndDateTime <= endDateTime)
            {
                //count how many messages we go through to get to our target.
                int curMessageCount = 0;

                while (curSampleIndex < Count)
                {
                    if (this[curSampleIndex].Timestamp >= windowEndDateTime)
                    {
                        //this is the first one in the next interval (or later), so don't count it and stop looping.
                        break;
                    }
                    else
                    {
                        //this bad boy is in our range, count it
                        curMessageCount++;
                    }

                    //and make damn sure we advance our index by one to not get into an infinite loop
                    curSampleIndex++;
                }

                //record off this interval
                new MetricValue(newMetricValueCollection, windowStartDateTime, curMessageCount);

                //and prep for the next loop
                windowStartDateTime = windowEndDateTime;
                windowEndDateTime   = CalculateOffset(windowStartDateTime, interval, intervals);
            }

            return(newMetricValueCollection);
        }
        /// <summary>Create a new metric value for the specified metric value set.</summary>
        /// <remarks>The new metric value is automatically added to the provided metric value set.</remarks>
        /// <param name="metricValueCollection">The metric value set this value is part of.</param>
        /// <param name="timeStamp">The unique date and time of this value sample.</param>
        /// <param name="value">The calculated value.</param>
        public MetricValue(MetricValueCollection metricValueCollection, DateTimeOffset timeStamp, double value)
        {
            m_MetricValueCollection = metricValueCollection;
            m_TimeStamp             = timeStamp;
            m_Value = value;

            //get the unique sequence number from the metric
            m_Sequence = metricValueCollection.GetSampleSequence();

            //and add ourself to the metric value set
            m_MetricValueCollection.Add(this);
        }
        private MetricValueCollection OnCalculateValuesShortest(DateTimeOffset startDateTime, DateTimeOffset endDateTime, IEventMetricValueDefinition trendValue)
        {
            //there are two possibilities for unit caption:  Either use the caption from the trend definition or, if that's null, we'll just do Count.
            string unitCaption = null;

            if (trendValue != null)
            {
                unitCaption = trendValue.UnitCaption;
            }

            if (string.IsNullOrEmpty(unitCaption))
            {
                unitCaption = "Events";
            }

            MetricValueCollection newMetricValueCollection;

            if (trendValue == null)
            {
                //initialize with our metric object for default caption/description behavior
                newMetricValueCollection = new MetricValueCollection(this, MetricSampleInterval.Shortest, 0, unitCaption);
            }
            else
            {
                //initialize with our trend for trend-specific captions and descriptions
                newMetricValueCollection = new MetricValueCollection((EventMetricValueDefinition)trendValue, MetricSampleInterval.Shortest, 0, unitCaption);
            }

            double lastValue = 0.0;

            //First, we need to find the value for the start date & time and the sample index into the collection.
            for (int curSampleIndex = 0; curSampleIndex < Samples.Count; curSampleIndex++)
            {
                //get the sample on deck
                EventMetricSample curSample = Samples[curSampleIndex];

                //Is this sample in our range?
                if (curSample.Timestamp >= startDateTime)
                {
                    //Go ahead and calculate the value, adding it to our set.  We don't use a baseline because we're looking at each individual value.
                    CalculateSample(newMetricValueCollection, new List <EventMetricSample>(new[] { curSample }), lastValue, curSampleIndex, curSample.Timestamp, (EventMetricValueDefinition)trendValue);
                }
                else if (curSample.Timestamp > endDateTime)
                {
                    //we're done - there are no more samples to consider, break out of the for loop
                    break;
                }
            }

            return(newMetricValueCollection);
        }
        /// <summary>
        /// Calculate one effective value from the provided objects
        /// </summary>
        /// <param name="metricValueCollection">The value set to add the new value to</param>
        /// <param name="baselineSample">The baseline to calculate from.  Only used (and required) for metrics that require multiple samples.</param>
        /// <param name="valueSample">The value sample to perform the calculation with.</param>
        /// <param name="timeStampOverride">An optional override timestamp</param>
        private static double CalculateSample(MetricValueCollection metricValueCollection, SampledMetricSample baselineSample, SampledMetricSample valueSample, DateTimeOffset?timeStampOverride)
        {
            double calculatedValue;

            if (valueSample.RequiresMultipleSamples == false)
            {
                calculatedValue = valueSample.ComputeValue(); //we just need one
            }
            else
            {
                calculatedValue = valueSample.ComputeValue(baselineSample);
            }

            DateTimeOffset timeStamp = ((timeStampOverride == null) ? valueSample.Timestamp : (DateTimeOffset)timeStampOverride);

            //now create & add the value to our values collection.
            new MetricValue(metricValueCollection, timeStamp, calculatedValue);

            return(calculatedValue);
        }
        private MetricValueCollection OnCalculateValuesInterval(MetricSampleInterval interval, int intervals, DateTimeOffset startDateTime, DateTimeOffset endDateTime, IEventMetricValueDefinition trendValue)
        {
            //there are two possibilities for unit caption:  Either use the caption from the trend definition or, if that's null, we'll just do Count.
            string unitCaption = null;

            if (trendValue != null)
            {
                unitCaption = trendValue.UnitCaption;
            }

            if (string.IsNullOrEmpty(unitCaption))
            {
                unitCaption = "Events";
            }

            MetricValueCollection newMetricValueCollection;

            if (trendValue == null)
            {
                //initialize with our metric object for default caption/description behavior
                newMetricValueCollection = new MetricValueCollection(this, interval, intervals, unitCaption);
            }
            else
            {
                //initialize with our trend for trend-specific captions and descriptions
                newMetricValueCollection = new MetricValueCollection((EventMetricValueDefinition)trendValue, interval, intervals, unitCaption);
            }

            //And prep for our process loop
            DateTimeOffset windowStartDateTime = startDateTime;
            DateTimeOffset windowEndDateTime   = CalculateOffset(windowStartDateTime, interval, intervals);
            double         lastValue           = 0.0;

            //now loop through all of the intervals in the range to create the sample ranges.
            int lastUsedSampleIndex = 0;

            while (windowStartDateTime < endDateTime)
            {
                //find all of the samples in the current range...
                List <EventMetricSample> samples = new List <EventMetricSample>();
                for (int curSampleIndex = lastUsedSampleIndex; curSampleIndex < Samples.Count; curSampleIndex++)
                {
                    //get the sample on deck
                    EventMetricSample curSample = Samples[curSampleIndex];

                    //if we're outside of the window (before or after) then break out because we've found this whole bucket.
                    if ((curSample.Timestamp < windowStartDateTime) || (curSample.Timestamp > windowEndDateTime))
                    {
                        //we'll need to look at this sample in the next window to see if it fits.
                        lastUsedSampleIndex = curSampleIndex;
                        break;
                    }

                    //this sample is in the bucket
                    samples.Add(curSample);
                }

                //add a metric value for this bucket now that we know everything in it.
                lastValue = CalculateSample(newMetricValueCollection, samples, lastValue, lastUsedSampleIndex, windowEndDateTime, (EventMetricValueDefinition)trendValue);

                //and now we have recorded a metric value for the requested date & time if possible, move that forward.
                windowStartDateTime = windowEndDateTime; //we're moving on to the next window
                windowEndDateTime   = CalculateOffset(windowEndDateTime, interval, intervals);

                //if the target is beyond our end date & time, set to the end date and time so we calculate our last sample
                if (windowEndDateTime > endDateTime)
                {
                    windowEndDateTime = endDateTime;
                }
            }

            return(newMetricValueCollection);
        }
        /// <summary>
        /// Calculate one effective value from the provided objects
        /// </summary>
        /// <param name="metricValueCollection">Optional.  The value set to add the new value to</param>
        /// <param name="samples">The set of samples to put in this value</param>
        /// <param name="previousValue">The value of the prior sample in time.</param>
        /// <param name="previousCount">The number of event metric samples previously used (for running average)</param>
        /// <param name="timeStamp">The timestamp to use for the sample</param>
        /// <param name="trendValue">The specific event metric value to trend</param>
        internal double CalculateSample(MetricValueCollection metricValueCollection, IList <EventMetricSample> samples, double previousValue, int previousCount, DateTimeOffset timeStamp, EventMetricValueDefinition trendValue)
        {
            double calculatedValue;

            //There are three totally different routes we go:  Either we are calculating an explicit trend value,
            //or we are just going to count the # of events.  This varies depending on whether the caller specified
            //a trend value
            if (trendValue == null)
            {
                //just count them
                calculatedValue = samples.Count;
            }
            //There are two overall ways we trend things:  Either we count the # of non-null values for non-trendables, or
            //we actually make a numeric trend
            else if ((trendValue.IsTrendable == false) || (trendValue.DefaultTrend == EventMetricValueTrend.Count))
            {
                //count the number of things.
                int itemCount = 0;

                if (samples.Count > 0)
                {
                    int valueIndex = Definition.Values.IndexOf(trendValue); //the zero-based index in the value collection of the value we want

                    //Now count all of the samples with a non-null value, including us (inclusive)
                    foreach (EventMetricSample sample in samples)
                    {
                        if (sample.Values[valueIndex] != null)
                        {
                            itemCount++;
                        }
                    }
                }

                calculatedValue = itemCount;
            }
            else
            {
                //ohh kay, now we either sum or average.
                double sumValue   = 0;                                     //sum or average, we need the sum.
                int    items      = samples.Count;                         //for an average we will need the item count.
                int    valueIndex = Definition.Values.IndexOf(trendValue); //the zero-based index in the value collection of the value we want

                //if it's a running value we need to start with the previous sample's state
                if ((trendValue.DefaultTrend == EventMetricValueTrend.RunningAverage) ||
                    (trendValue.DefaultTrend == EventMetricValueTrend.RunningSum))
                {
                    //we need the previous running data.
                    sumValue = previousValue;
                    items   += previousCount;
                }

                //Now add up the effective value of the requested trend line for every sample in the range, inclusive
                foreach (EventMetricSample currentSample in samples)
                {
                    sumValue += currentSample.GetEffectiveValue(valueIndex);
                }

                //figure out the calculated value.  For performance and safety, don't do a divide we don't have to
                if ((items > 1) &&
                    ((trendValue.DefaultTrend == EventMetricValueTrend.Average) ||
                     (trendValue.DefaultTrend == EventMetricValueTrend.RunningAverage)))
                {
                    calculatedValue = sumValue / items;
                }
                else
                {
                    calculatedValue = sumValue;
                }
            }

            //now create & add the value to our values collection.
            if (metricValueCollection != null)
            {
                new MetricValue(metricValueCollection, timeStamp, calculatedValue);
            }

            return(calculatedValue);
        }
        /// <summary>
        /// Calculate the value set for the provided date range inclusive with samples exactly on the provided interval.
        /// </summary>
        /// <remarks></remarks>
        /// <param name="interval">The interval to bias to.</param>
        /// <param name="intervals">The number of intervals to have between each value exactly.</param>
        /// <param name="startDateTime">The exact date and time desired to start the value set.</param>
        /// <param name="endDateTime">The exact end date and time to not exceed.</param>
        /// <returns>A new metric value set with all calculated values.</returns>
        private MetricValueCollection OnCalculateValuesOnInterval(MetricSampleInterval interval, int intervals, DateTimeOffset startDateTime, DateTimeOffset endDateTime)
        {
            MetricValueCollection newMetricValueCollection = new MetricValueCollection(this, interval, intervals, Definition.UnitCaption);

            //based on the requested interval, calculate the delta between each sample and the tolerance (how close we have to be
            //to the requested time to be pulled forward in time as the best sample)
            TimeSpan sampleTolerance = CalculateOffsetTolerance(interval);

            DateTimeOffset targetDateTime          = startDateTime;
            DateTimeOffset targetToleranceDateTime = targetDateTime + sampleTolerance;

            SampledMetricSample baselineSample = null;
            double previousMetricValue         = 0;
            int    firstSampleIndex            = 0;

            //First, we need to find the value for the start date & time and the sample index into the collection.
            for (int curSampleIndex = 0; curSampleIndex < base.Samples.Count; curSampleIndex++)
            {
                SampledMetricSample curSample = (SampledMetricSample)Samples[curSampleIndex];

                //is this the sample that exactly covers our target date & time?  It is if it's equal to our target, or
                //it is a more exact fit than the next sample.
                if ((curSample.Timestamp == targetDateTime) ||
                    ((curSample.Timestamp < targetToleranceDateTime) && (Samples[curSampleIndex + 1].Timestamp > targetToleranceDateTime)))
                {
                    //yes, this sample is the best fit for our start date & time.  The next one is after our "pull forward" tolerance.
                    //but to get a value, we may have to back up one more interval from this sample to establish its value
                    if (curSample.RequiresMultipleSamples)
                    {
                        //back up as much as we can - as close to one full interval as possible so we get the most accurate initial value sample
                        DateTimeOffset baselineTargetDateTime = CalculateOffset(targetDateTime, interval, -intervals);

                        //start with the first sample before us... if there is one.
                        for (int baselineSampleIndex = curSampleIndex - 1; baselineSampleIndex >= 0; baselineSampleIndex--)
                        {
                            //keep walking back until we are before our target (or run out of choices)
                            baselineSample = (SampledMetricSample)Samples[curSampleIndex];
                            if (baselineSample.Timestamp <= baselineTargetDateTime)
                            {
                                //this is our best fit - it's the first that covers our baseline date time.
                                break;
                            }
                        }
                    }

                    //we either got our baseline or we didn't - if we didn't and we need it we can't calculate value.
                    if ((baselineSample != null) || (curSample.RequiresMultipleSamples == false))
                    {
                        //Calculate the initial metric value sample and add it to the collection.
                        previousMetricValue =
                            CalculateSample(newMetricValueCollection, baselineSample, curSample, startDateTime);
                    }

                    //and this sample becomes our baseline into the next routine
                    baselineSample   = curSample;
                    firstSampleIndex = curSampleIndex;
                    break;  //and we're done - we only wanted to get the start value.
                }
            }

            //Now that we've found our first sample, and the offset into the collection for the first sample, we can go on (provide there are more samples)
            firstSampleIndex++;   //because we used the current sample in the for loop above.
            if (firstSampleIndex < (Samples.Count - 1))
            {
                //we now want to look for the first sample after the start date.  If the user was silly and requested an end date that is less
                //than one interval from the start date, we'll never get into our while loop this way.
                targetDateTime          = CalculateOffset(targetDateTime, interval, intervals);
                targetToleranceDateTime = targetDateTime + sampleTolerance;

                int curSampleIndex            = firstSampleIndex; //we start with the first sample after what was used above.
                SampledMetricSample curSample = null;

                //keep looping until we fill up the timespan or run out of samples.
                while ((targetDateTime <= endDateTime) && (curSampleIndex < (Samples.Count - 1)))
                {
                    //if we have no sample on deck, we must have used it in the last pass of the loop (or this is the first pass)
                    //so get it now.
                    if (curSample == null)
                    {
                        curSample = (SampledMetricSample)Samples[curSampleIndex];
                    }

                    //is this the sample that exactly covers our target date & time?  It is if it's equal to our target, or
                    //it is a more exact fit than the next sample.
                    if ((curSample.Timestamp == targetDateTime) ||
                        ((curSample.Timestamp < targetToleranceDateTime) && (Samples[curSampleIndex + 1].Timestamp > targetToleranceDateTime)))
                    {
                        //yes, this sample is the best fit for our start date & time.  The next one is after our "pull forward" tolerance.
                        if ((baselineSample != null) || (curSample.RequiresMultipleSamples == false))
                        {
                            //Calculate the next metric value sample and add it to the collection.
                            previousMetricValue =
                                CalculateSample(newMetricValueCollection, baselineSample, curSample, targetDateTime);
                        }

                        //and this sample becomes our baseline into the next round
                        baselineSample = curSample;

                        //and we need a new current sample.
                        curSample = null;
                        curSampleIndex++;

                        //and now we have recorded a metric value for the requested date & time if possible, move that forward.
                        targetDateTime          = CalculateOffset(targetDateTime, interval, intervals);
                        targetToleranceDateTime = targetDateTime + sampleTolerance;
                    }
                    else if (curSample.Timestamp < targetToleranceDateTime)
                    {
                        //this sample AND the next sample are both before target tolerance - we're going to skip this guy and record nothing
                        //(because we have more samples than we need for the interval we want)
                        curSample = null;
                        curSampleIndex++;
                    }
                    else
                    {
                        //the sample on deck doesn't apply yet - it's in the future.  We need to "invent" a sample for the current date and time.
                        //we'll just re-record the last metric value
                        new MetricValue(newMetricValueCollection, targetDateTime, previousMetricValue);

                        //and now we have definitely recorded a metric value for the requested date & time, move that forward.
                        targetDateTime          = CalculateOffset(targetDateTime, interval, intervals);
                        targetToleranceDateTime = targetDateTime + sampleTolerance;
                    }
                }
            }

            return(newMetricValueCollection);
        }
        /// <summary>
        /// Calculate a value set for the provided date range inclusive using all of the sample data, even if
        /// that produces irregular sample intervals.
        /// </summary>
        /// <param name="startDateTime">The exact date and time desired to start the value set.</param>
        /// <param name="endDateTime">The exact end date and time to not exceed.</param>
        /// <returns>A new metric value set with all calculated values.</returns>
        private MetricValueCollection OnCalculateValuesShortest(DateTimeOffset startDateTime, DateTimeOffset endDateTime)
        {
            MetricValueCollection newMetricValueCollection = new MetricValueCollection(this, MetricSampleInterval.Shortest, 0, Definition.UnitCaption);

            //ohhhhkay, now we need to figure out what samples we want to include and not include.
            //we want to start by getting the sample one interval before the user wants to have values for so we can have a value exactly
            //on that starting point
            SampledMetricSample baselineSample         = null;
            SampledMetricSample previousBaselineSample = null;

            foreach (SampledMetricSample curSample in base.Samples)
            {
                //is this sample in our range?
                if (curSample.Timestamp < startDateTime)
                {
                    //It isn't, but there are still reasons we'd be interested in it.
                    //because the next one could be in the range and we'll want to mess with this one.
                    previousBaselineSample = baselineSample;
                    baselineSample         = curSample;
                }
                else if (curSample.Timestamp > endDateTime)
                {
                    //no, BUT if it's the first sample after our range, and we didn't end EXACTLY
                    //on the range then we want to include it, but we'll short it back to the end of the timeframe.
                    if ((baselineSample != null) && (baselineSample.Timestamp < endDateTime))
                    {
                        CalculateSample(newMetricValueCollection, baselineSample, curSample, endDateTime);
                    }

                    //and no matter what, we're done.  Either we got a special "Bracket" sample, or there isn't one.
                    break;
                }
                else
                {
                    //the sample is in the range - neither after nor before.  We definitely use this sample.

                    //but before we do this, is this the FIRST one in the range?
                    if ((baselineSample != null) && (baselineSample.Timestamp < startDateTime))
                    {
                        //yes it is - the baseline is before our start date, and we're after or equal.
                        //If it's not only the last before we get in but ALSO the first one in isn't EXACTLY on the line, we need to
                        //create a fake interim sample, based on this guy and the one BEFORE him.
                        if (curSample.Timestamp > startDateTime)
                        {
                            //we have to insert a fake sample based on the baseline and the guy that came before him.
                            //but guard against us not having enough previous information to do this (this will happen more often than not)
                            if ((previousBaselineSample != null) || (baselineSample.RequiresMultipleSamples == false))
                            {
                                CalculateSample(newMetricValueCollection, previousBaselineSample, baselineSample, startDateTime);
                            }
                        }
                    }

                    //calculate our sample, however we have to guard against there being no baseline
                    // (because we're the first sample of a metric that requires multiple samples)
                    if ((baselineSample != null) || (curSample.RequiresMultipleSamples == false))
                    {
                        CalculateSample(newMetricValueCollection, baselineSample, curSample, null);
                    }

                    //and this sample becomes the new baseline
                    baselineSample = curSample;
                }
            }

            return(newMetricValueCollection);
        }