private long m_SampleSequence = 0; //used when adding sampled metrics to ensure order. /// <summary> /// Create a new metric with the provided metric definition and metric packet. /// </summary> /// <remarks>Most derived classes will provide a more convenient implementation that will automatically /// create the correct metric packet instead of the caller having to first create it. /// The new metric will automatically be added to the metric definition's metrics collection.</remarks> /// <param name="definition">The definition for this metric</param> /// <param name="packet">The metric packet to use for this metric</param> internal Metric(MetricDefinition definition, MetricPacket packet) { //verify and store off our input if (definition == null) { throw new ArgumentNullException(nameof(definition)); } if (packet == null) { throw new ArgumentNullException(nameof(packet)); } //one last safety check: The definition and the packet better agree. if (definition.Id != packet.DefinitionId) { throw new ArgumentOutOfRangeException(nameof(packet), "The provided metric packet has a different definition Id than the provide metric definition."); } //and now that we know everything isn't null, go ahead and store things off m_MetricDefinition = definition; m_Packet = packet; //and force our inheritor to create the samples collection so we get the right derived class. // ReSharper disable DoNotCallOverridableMethodsInConstructor m_Samples = OnSampleCollectionCreate(); // ReSharper restore DoNotCallOverridableMethodsInConstructor //finally, add ourself to the metric definition's metrics collection m_MetricDefinition.Metrics.Add(this); }
/// <summary> /// Create a new metric dictionary for the provided definition. /// </summary> /// <remarks>This dictionary is created automatically by the Metric Definition during its initialization.</remarks> /// <param name="metricDefinition"></param> internal MetricCollection(MetricDefinition metricDefinition) { if (metricDefinition == null) { throw new ArgumentNullException(nameof(metricDefinition)); } m_MetricDefinition = metricDefinition; }
/// <summary> /// Calculate the string key for a metric. /// </summary> /// <param name="metricDefinition">The existing metric definition object to generate a string key for</param> /// <param name="instanceName">The name of the performance counter category instance, or an empty string (""), if the category contains a single instance.</param> /// <returns>The unique string key for this item</returns> public static string GetKey(MetricDefinition metricDefinition, string instanceName) { //make sure the metric definition object isn't null if (metricDefinition == null) { throw new ArgumentNullException(nameof(metricDefinition)); } return(GetKey(metricDefinition.MetricTypeName, metricDefinition.CategoryName, metricDefinition.CounterName, instanceName)); }
/// <summary> /// Takes an instance name or complete metric name and normalizes it to a metric name so it can be used to look up a metric /// </summary> /// <param name="metricDefinition">The metric definition to look for metrics within</param> /// <param name="metricKey">The instance name or complete metric name</param> /// <returns></returns> internal static string NormalizeKey(MetricDefinition metricDefinition, string metricKey) { string returnVal; string trueMetricKey; bool prependDefinitionName = false; //Did we get a null? If we got a null, we know we need to pre-pend the definition (and it isn't safe to do any more testing) if (metricKey == null) { prependDefinitionName = true; trueMetricKey = null; } else { //trim the input for subsequent testing to see what we get trueMetricKey = metricKey.Trim(); if (string.IsNullOrEmpty(trueMetricKey)) { //we know we need to pre-pend the definition name prependDefinitionName = true; } else { //OK, a true key is a full name, so see if the key we got STARTS with our definition name if (trueMetricKey.Length < metricDefinition.Name.Length) { //the key we got is shorter than the length of the metric definition name, so it can't include the metric definition name. prependDefinitionName = true; } else { //now check the start of the string to see what we get if (trueMetricKey.StartsWith(metricDefinition.Name, StringComparison.Ordinal) == false) { //they aren't the same at least as long as the metric definition name is, so we assume we need to pre-pend. prependDefinitionName = true; } } } } //If the value we got was just the instance name, we need to put the metric definition's key in front of it. if (prependDefinitionName) { returnVal = GetKey(metricDefinition, trueMetricKey); } else { returnVal = trueMetricKey; } return(returnVal); }
/// <summary> /// Determines whether the collection contains an element with the specified key. /// </summary> /// <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> /// <returns>true if the collection contains an element with the key; otherwise, false.</returns> public bool ContainsKey(string metricTypeName, string categoryName, string counterName) { //get the key for the provided values string key = MetricDefinition.GetKey(metricTypeName, categoryName, counterName); lock (m_Lock) { //gateway to our alternate inner dictionary return(m_DictionaryByName.ContainsKey(key)); } }
/// <summary> /// Determines whether the collection contains an element with the specified key. /// </summary> /// <param name="key">The metric name to locate in the collection</param> /// <returns>true if the collection contains an element with the key; otherwise, false.</returns> public bool ContainsKey(string key) { //we do a few cute tricks to normalize the key before checking it to be tolerant of what users do string trueKey = MetricDefinition.NormalizeKey(m_MetricDefinition, key); lock (m_Lock) { //gateway to our alternate inner dictionary return(m_DictionaryByName.ContainsKey(trueKey)); } }
/// <summary> /// Retrieve an item from the collection by its key if present. If not present, the default value of the object is returned. /// </summary> /// <param name="key">The metric name to locate in the collection</param> /// <param name="value">When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized.</param> /// <returns>true if the collection contains an element with the specified key; otherwise false.</returns> public bool TryGetValue(string key, out IMetric value) { //we do a few cute tricks to normalize the key before checking it to be tolerant of what users do string trueKey = MetricDefinition.NormalizeKey(m_MetricDefinition, key); lock (m_Lock) { //gateway to our inner dictionary try get value return(m_DictionaryByName.TryGetValue(trueKey, out value)); } }
/// <summary> /// Retrieve an item from the collection by its key if present. If not present, the default value of the object is returned. /// </summary> /// <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="value">When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized.</param> /// <returns>true if the collection contains an element with the specified key; otherwise false.</returns> /// <exception cref="ArgumentNullException">The provided metricsSystem, categoryName, or counterName was null.</exception> public bool TryGetValue(string metricTypeName, string categoryName, string counterName, out IMetricDefinition value) { //get the key for the provided values string key = MetricDefinition.GetKey(metricTypeName, categoryName, counterName); lock (m_Lock) { //gateway to our inner dictionary try get value return(m_DictionaryByName.TryGetValue(key, out value)); } }
/// <summary> /// Retrieve metric object by its type, category, and counter names. /// </summary> /// <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> /// <returns></returns> public IMetricDefinition this[string metricTypeName, string categoryName, string counterName] { get { //create the key from the parts we got string key = MetricDefinition.GetKey(metricTypeName, categoryName, counterName); lock (m_Lock) { return(m_DictionaryByName[key]); } } }
/// <summary> /// Retrieve metric object by its name /// </summary> /// <param name="key"></param> /// <returns></returns> public IMetric this[string key] { get { //we do a few cute tricks to normalize the key before checking it to be tolerant of what users do string trueKey = MetricDefinition.NormalizeKey(m_MetricDefinition, key); lock (m_Lock) { return(m_DictionaryByName[trueKey]); } } }
/// <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> /// Add an existing metric packet object to our collection for the supplied packet. /// </summary> /// <param name="newDefinitionPacket">The new metric packet object to add.</param> internal MetricDefinition Add(MetricDefinitionPacket newDefinitionPacket) { //just do the null check - the rest of the checks (like dupe checks) are done in the normal add routine if (newDefinitionPacket == null) { throw new ArgumentNullException(nameof(newDefinitionPacket), "A metric packet object must be provided to add it to the collection."); } //create a new metric object to wrap the supplied metric packet MetricDefinition newDefinition = new MetricDefinition(this, newDefinitionPacket); //and call our mainstream add method which does the rest of our checks. Add(newDefinition); //return the newly added object so our caller doesn't have to go digging for it return(newDefinition); }
/// <summary>Creates a new metric instance or returns an existing one 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 an Event 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="instanceName">The unique name of this instance within the metric's collection.</param> /// <returns>The event metric object for the specified event metric instance.</returns> public static EventMetric AddOrGet(MetricDefinitionCollection definitions, string metricTypeName, string categoryName, string counterName, 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; if (definitions.TryGetValue(definitionKey, out definition)) { //if the metric definition exists, but is of the wrong type we have a problem. if ((definition is EventMetricDefinition) == false) { throw new ArgumentException("A metric already exists with the provided type, category, and counter name but it is not compatible with being an event metric. Please use a different counter name.", nameof(counterName)); } } else { //we didn't find one, make a new one definition = new EventMetricDefinition(definitions, metricTypeName, categoryName, counterName); definitions.Add(definition); // Add it to the collection, no longer done in the constructor. // ToDo: Reconsider this implementation; putting incomplete event metric definitions in the collection is not ideal, // and creating a metric from an empty event metric definition is fairly pointless. } //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) //make sure the get & add are atomic { if (definition.Metrics.TryGetValue(metricKey, out metric) == false) { metric = new EventMetric((EventMetricDefinition)definition, instanceName); } } return((EventMetric)metric); }
/// <summary> /// Retrieves the specified metric instance, or creates it if it doesn't exist /// </summary> /// <param name="instanceName"></param> /// <returns>The custom sampled metric object.</returns> private CustomSampledMetric EnsureMetricExists(string instanceName) { //Find the right metric sample instance, creating it if we have to. string metricKey = MetricDefinition.GetKey(this, instanceName); IMetric ourMetric; //This must be protected in a multi-threaded environment lock (Metrics.Lock) { if (Metrics.TryGetValue(metricKey, out ourMetric) == false) { //it doesn't exist - go ahead and add it ourMetric = new CustomSampledMetric(this, instanceName); } } //and return the metric return((CustomSampledMetric)ourMetric); }
/// <summary> /// Add the supplied Metric item to this collection. /// </summary> /// <remarks>Metrics automatically add themselves when they are created, so it isn't necessary (and will produce errors) to manually add them.</remarks> /// <param name="item">The new Metric item to add to this collection</param> public void Add(IMetric item) { //we really don't want to support this method, but we have to for ICollection<T> compatibility. So we're going to ruthlessly //verify that the metric object was created correctly. if (item == null) { throw new ArgumentNullException(nameof(item), "A metric item must be provided to add it to the collection."); } //make sure the metric is for the right definition, namely our definition. if (MetricDefinition.GetKey(item) != m_MetricDefinition.Name) { throw new ArgumentOutOfRangeException(nameof(item), "The provided metric item is not related to the metric definition that owns this metrics collection."); } //we're about to modify the collection, get a lock. We don't want the lock to cover the changed event since //we really don't know how long that will take, and it could be deadlock prone. lock (m_Lock) { //make sure we don't already have it if (m_Dictionary.ContainsKey(item.Id)) { throw new ArgumentException("The specified metric item is already in the collection.", nameof(item)); } if (m_DictionaryByName.ContainsKey(item.Name)) { throw new ArgumentException("A metric item for the same metric is already in the collection.", nameof(item)); } //add it to all of our collections, and to the definition metric cache. m_Dictionary.Add(item.Id, item); m_DictionaryByName.Add(item.Name, item); m_List.Add(item); ((MetricDefinitionCollection)m_MetricDefinition.Definitions).AddMetric(item); } //and fire our event OnCollectionChanged(new CollectionChangedEventArgs <IMetricCollection, IMetric>(this, item, CollectionAction.Added)); }
/// <summary>Creates a new event metric object from the metric definition looked up with the provided key information.</summary> /// <remarks>The metric definition must already exist or an exception will be raised.</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="instanceName">The unique name of this instance within the metric's collection.</param> public EventMetric(MetricDefinitionCollection definitions, string metricTypeName, string categoryName, string counterName, string instanceName) : this((EventMetricDefinition)definitions[MetricDefinition.GetKey(metricTypeName, categoryName, counterName)], instanceName) { }
/// <summary> /// Determines if the provided object is identical to this object. /// </summary> /// <param name="obj">The object to compare this object to</param> /// <returns>True if the other object is also a MetricDefinition and represents the same data.</returns> public override bool Equals(object obj) { MetricDefinition otherMetricDefinition = obj as MetricDefinition; return(Equals(otherMetricDefinition)); // Just have type-specific Equals do the check (it even handles null) }