|
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace OperationPerformanceCounter.Diagnostics
{
/// <summary>
/// Watches an IOperationCompletable and updates the
/// configured performance counters as the events are received
/// </summary>
public class OperationPerformanceCounter
{
/// <summary>
/// Use the local machine
/// </summary>
private const string MACHINE_NAME = ".";
/// <summary>
/// Performance Counter category name
/// </summary>
private readonly string _category;
/// <summary>
/// The operations being raised in IOperationCompletable to watch
/// </summary>
private readonly string[] _handledOperations;
/// <summary>
/// Counter group lookup
/// </summary>
private readonly IDictionary<string, OperationalCounterGroup> _counterGroups;
/// <summary>
/// A PerformanceCounter and its CreationData
/// </summary>
private class CounterCreationCounter
{
public CounterCreationData Creation { get; private set; }
public PerformanceCounter Counter { get; private set; }
public CounterCreationCounter(string counterName, string counterHelp, PerformanceCounterType counterType,
string categoryName)
{
Creation = new CounterCreationData
{
CounterName = counterName,
CounterHelp = counterHelp,
CounterType = counterType,
};
Counter = new PerformanceCounter
{
CategoryName = categoryName,
CounterName = counterName,
MachineName = MACHINE_NAME,
ReadOnly = false
};
}
}
/// <summary>
/// A group of PerformanceCounters used for measuring
/// operational timing
/// </summary>
private class OperationalCounterGroup
{
private readonly CounterCreationCounter _countPerSec;
private readonly CounterCreationCounter _totalCount;
private readonly CounterCreationCounter _averageDuration;
private readonly CounterCreationCounter _averageDurationBase;
private readonly CounterCreationCounter _mostRecentDuration;
public OperationalCounterGroup(string operationName, string categoryName)
{
_countPerSec
= new CounterCreationCounter(
string.Format("{0} # / sec", operationName),
string.Format("Number of {0} per second", operationName),
PerformanceCounterType.RateOfCountsPerSecond32,
categoryName);
_totalCount
= new CounterCreationCounter(
string.Format("{0} total #", operationName),
string.Format("Total number of {0}", operationName),
PerformanceCounterType.NumberOfItems32,
categoryName);
_averageDuration
= new CounterCreationCounter(
string.Format("{0} avg duration", operationName),
string.Format("Average duration for {0} in ticks", operationName),
PerformanceCounterType.AverageTimer32,
categoryName);
_averageDurationBase
= new CounterCreationCounter(
string.Format("{0} avg duration base", operationName),
string.Format("Average duration for {0} in ticks base", operationName),
PerformanceCounterType.AverageBase,
categoryName);
_mostRecentDuration
= new CounterCreationCounter(
string.Format("{0} most recent duration", operationName),
string.Format("Most recent duration for {0} in ticks", operationName),
PerformanceCounterType.NumberOfItems32,
categoryName);
}
/// <summary>
/// Increment all counters
/// </summary>
public void OperationCompleted(TimeSpan elapsed)
{
_totalCount.Counter.Increment();
_countPerSec.Counter.Increment();
_averageDuration.Counter.IncrementBy(elapsed.Ticks);
_averageDurationBase.Counter.Increment();
_mostRecentDuration.Counter.RawValue = elapsed.Ticks;
}
public IEnumerable<CounterCreationData> GetCreationData()
{
yield return _countPerSec.Creation;
yield return _totalCount.Creation;
yield return _averageDuration.Creation;
yield return _averageDurationBase.Creation;
yield return _mostRecentDuration.Creation;
}
}
/// <summary>
/// Handler for operation completion
/// </summary>
/// <param name="operationName">Name of the operation.</param>
/// <param name="elapsed">Time elapsed.</param>
private void OperationCompleted(string operationName, TimeSpan elapsed)
{
if (_counterGroups.ContainsKey(operationName))
_counterGroups[operationName].OperationCompleted(elapsed);
}
/// <summary>
/// Initializes a new instance of the <see cref="OperationPerformanceCounter"/> class.
/// </summary>
/// <param name="categoryName">Name of the category.</param>
/// <param name="handledOperations">The handled operations.</param>
/// <param name="operationCompletable">The object to watch for operations completing.</param>
public OperationPerformanceCounter(string categoryName, string[] handledOperations,
IOperationCompletable operationCompletable)
{
_category = categoryName;
_handledOperations = handledOperations;
_counterGroups =
(from c in _handledOperations
select new {Name = c, Group = new OperationalCounterGroup(c, _category)})
.ToDictionary(o => o.Name, o => o.Group);
UpdateOrCreateCategory((from c in _counterGroups
from cd in c.Value.GetCreationData()
select cd).ToArray());
operationCompletable.OperationCompleted += OperationCompleted;
}
/// <summary>
/// Updates existing or Creates category and the counters
/// </summary>
private void UpdateOrCreateCategory(CounterCreationData[] counterCreationData)
{
if (PerformanceCounterCategory.Exists(_category))
{
var installedCounters = new HashSet<string>(
from ec in new PerformanceCounterCategory(_category).GetCounters()
select ec.CounterName);
var knownCounters = new HashSet<string>(
from c in counterCreationData
select c.CounterName);
if (!installedCounters.SetEquals(knownCounters))
{
DeleteCategory();
CreateCategory(counterCreationData);
}
}
else
{
CreateCategory(counterCreationData);
}
}
/// <summary>
/// Creates a category with the given counter creation data
/// </summary>
private void CreateCategory(CounterCreationData[] counterCreationData)
{
PerformanceCounterCategory.Create(
_category, "", PerformanceCounterCategoryType.Unknown,
new CounterCreationDataCollection(counterCreationData));
PerformanceCounter.CloseSharedResources();
}
/// <summary>
/// Deletes the category.
/// </summary>
public void DeleteCategory()
{
if (!PerformanceCounterCategory.Exists(_category)) return;
PerformanceCounterCategory.Delete(_category);
PerformanceCounter.CloseSharedResources();
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.