/// <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> public static EventMetricDefinition AddOrGet(MetricDefinitionCollection definitions, string metricTypeName, string categoryName, string counterName) { //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 to grab a lock so our try get & the create are done as one lock. 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 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. } } return((EventMetricDefinition)definition); }
/// <summary> /// Create a new sampled metric object from the provided raw data packet. /// </summary> /// <remarks>The metric definition <b>will</b> be automatically added to the provided collection.</remarks> /// <param name="definitions">The definitions dictionary this definition is a part of.</param> /// <param name="packet">The packet to create a definition from.</param> internal SampledMetricDefinition(MetricDefinitionCollection definitions, SampledMetricDefinitionPacket packet) : base(definitions, packet) { // After the base constructor, auto-add ourself to the definition collection SetReadOnly(); // Make sure we're read-only before we're added to the collection. definitions.Add(this); // ToDo: Determine whether to keep or discard this behavior for sampled metrics. }
/// <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> /// Register this instance as a completed definition and return the valid usable definition for this event metric. /// </summary> /// <remarks>This call is necessary to complete a new event metric definition (created by calls to AddValue) before /// it can be used, and it signifies that all desired value columns have been added to the definition. Only the /// first registration of a metric definition with a given Key (metrics system, category name, and counter name) /// will be effective and return the same definition object; subsequent calls (perhaps by another thread) will /// instead return the existing definition already registered. If a definition already registered with that Key /// can not be an event metric (e.g. a sampled metric is defined with that Key) or if this instance defined value /// columns not present as compatible value columns in the existing registered definition with that Key, then an /// ArgumentException will be thrown to signal your programming mistake.</remarks> /// <returns>The actual usable definition with the same metrics system, category name, and counter name as this instance.</returns> public EventMetricDefinition Register() { EventMetricDefinition officialDefinition; MetricDefinitionCollection definitionCollection = (MetricDefinitionCollection)Definitions; // We need to lock the collection while we check for an existing definition and maybe add this one to it. lock (definitionCollection.Lock) { IMetricDefinition rawDefinition; if (definitionCollection.TryGetValue(MetricTypeName, CategoryName, CounterName, out rawDefinition) == false) { // There isn't already one by that Key. Great! Register ourselves. SetReadOnly(); // Mark this definition as completed. officialDefinition = this; definitionCollection.Add(this); } else { // Oooh, we found one already registered. We'll want to do some checking on this, but outside the lock. officialDefinition = rawDefinition as EventMetricDefinition; } } // End of collection lock if (officialDefinition == null) { throw new ArgumentException( string.Format( "There is already a metric definition for the same metrics system ({0}), category name ({1}), and counter name ({2}), but it is not an event metric.", MetricTypeName, CategoryName, CounterName)); } else if (this != officialDefinition) { // There was one other than us, make sure it's compatible with us. IEventMetricValueDefinitionCollection officialValues = officialDefinition.Values; foreach (EventMetricValueDefinition ourValue in Values) { IEventMetricValueDefinition officialValue; if (officialValues.TryGetValue(ourValue.Name, out officialValue) == false) { // It doesn't have one of our value columns! throw new ArgumentException( string.Format( "There is already an event metric definition for the same metrics system ({0}), category name ({1}), and counter name ({2}), but it is not compatible; it does not define value column \"{3}\".", MetricTypeName, CategoryName, CounterName, ourValue.Name)); } else if (ourValue.SerializedType != ((EventMetricValueDefinition)officialValue).SerializedType) { throw new ArgumentException( string.Format( "There is already an event metric definition for the same metrics system ({0}), category name ({1}), and counter name ({2}), but it is not compatible; " + "it defines value column \"{3}\" with type {4} rather than type {5}.", MetricTypeName, CategoryName, CounterName, ourValue.Name, officialValue.Type.Name, ourValue.Type.Name)); } } // We got through all the values defined in this instance? Then we're okay to return the official one. } // Otherwise, it's just us, so we're all good. return(officialDefinition); }