public void BindMetricWithoutPrefix(string name, string unit, Type type) { lock (_metricsLock) { RootMetricInfo rmi; if (_rootNameToInfo.TryGetValue(name, out rmi)) { if (rmi.Type != type) { throw new Exception($"Cannot bind metric name \"{name}\" to Type {type.FullName}. It has already been bound to {rmi.Type.FullName}"); } if (rmi.Unit != unit) { throw new Exception($"Cannot bind metric name \"{name}\" to unit \"{unit}\". It has already been bound to \"{rmi.Unit}\""); } return; } if (!type.IsSubclassOf(typeof(BosunMetric))) { throw new Exception($"Cannot bind metric \"{name}\" to Type {type.FullName}. It does not inherit from BosunMetric."); } _rootNameToInfo[name] = new RootMetricInfo { Type = type, Unit = unit }; } }
/// <summary> /// Binds a given metric name to a specific data model. Metrics with this name will only be allowed to use the type <paramref name="type"/>. Calling /// this method is usually not necessary. A metric will be bound to the type that it is first instantiated with. /// </summary> public void BindMetric(string name, string unit, Type type, bool includePrefix = true) { if (includePrefix) { name = MetricsNamePrefix + name; } lock (_metricsLock) { if (_rootNameToInfo.TryGetValue(name, out var rmi)) { if (rmi.Type != type) { throw new Exception($"Cannot bind metric name \"{name}\" to Type {type.FullName}. It has already been bound to {rmi.Type.FullName}"); } if (rmi.Unit != unit) { throw new Exception($"Cannot bind metric name \"{name}\" to unit \"{unit}\". It has already been bound to \"{rmi.Unit}\""); } return; } if (!type.IsSubclassOf(typeof(MetricBase))) { throw new Exception($"Cannot bind metric \"{name}\" to Type {type.FullName}. It does not inherit from MetricBase."); } _rootNameToInfo[name] = new RootMetricInfo { Type = type, Unit = unit }; } }
private T GetMetricInternal <T>(string name, bool addPrefix, string unit, string description, T metric, bool mustBeNew) where T : BosunMetric { if (addPrefix) { name = MetricsNamePrefix + name; } var metricType = typeof(T); if (metric == null) { // if the type has a constructor without params, then create an instance var constructor = metricType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null); if (constructor == null) { throw new ArgumentNullException(nameof(metric), metricType.FullName + " has no public default constructor. Therefore the metric parameter cannot be null."); } metric = (T)constructor.Invoke(new object[0]); } metric.Collector = this; metric.Name = name; metric.Description = description; metric.Unit = unit; metric.LoadSuffixes(); lock (_metricsLock) { RootMetricInfo rmi; if (_rootNameToInfo.TryGetValue(name, out rmi)) { if (rmi.Type != metricType) { throw new Exception( $"Attempted to create metric name \"{name}\" with Type {metricType.FullName}. This metric name has already been bound to Type {rmi.Type.FullName}."); } if (rmi.Unit != unit) { throw new Exception( $"Cannot bind metric name \"{name}\" to unit \"{unit}\". It has already been bound to \"{rmi.Unit}\""); } } else if (_nameAndSuffixToRootName.ContainsKey(name)) { throw new Exception( $"Attempted to create metric name \"{name}\" with Type {metricType.FullName}. " + $"This metric name is already in use as a suffix of Type {_rootNameToInfo[_nameAndSuffixToRootName[name]].Type.FullName}."); } // claim all suffixes. Do this in two passes (check then add) so we don't end up in an inconsistent state. foreach (var s in metric.SuffixesArray) { var ns = name + s; // verify this is a valid metric name at all (it should be, since both parts are pre-validated, but just in case). if (!BosunValidation.IsValidMetricName(ns)) { throw new Exception($"\"{ns}\" is not a valid metric name"); } if (_nameAndSuffixToRootName.ContainsKey(ns) && _nameAndSuffixToRootName[ns] != name) { throw new Exception( $"Attempted to create metric name \"{ns}\" with Type {metricType.FullName}. " + $"This metric name is already in use as a suffix of Type {_rootNameToInfo[_nameAndSuffixToRootName[ns]].Type.FullName}."); } } foreach (var s in metric.SuffixesArray) { _nameAndSuffixToRootName[name + s] = name; } // claim the root type _rootNameToInfo[name] = new RootMetricInfo { Type = metricType, Unit = unit }; // see if this metric name and tag combination already exists var key = metric.GetMetricKey(); if (_rootNameAndTagsToMetric.ContainsKey(key)) { if (mustBeNew) { throw new Exception($"Attempted to create duplicate metric with name \"{name}\" and tags {metric.TagsJson}."); } return((T)_rootNameAndTagsToMetric[key]); } // metric doesn't exist yet. _rootNameAndTagsToMetric[key] = metric; metric.IsAttached = true; var needsPreSerialize = metric.NeedsPreSerializeCalled(); if (needsPreSerialize) { _metricsNeedingPreSerialize.Add(metric); } var isExternal = metric.IsExternalCounter(); if (isExternal) { _externalCounterMetrics.Add(metric); } else { _localMetrics.Add(metric); } if (metric.SerializeInitialValue) { MetricWriter writer = null; try { var queue = isExternal ? _externalCounterQueue : _localMetricsQueue; writer = queue.GetWriter(); if (needsPreSerialize) { metric.PreSerializeInternal(); } metric.SerializeInternal(writer, DateTime.UtcNow); } finally { writer?.EndBatch(); } } return(metric); } }