예제 #1
0
        private IReadOnlyDictionary <string, string> ValidateDefaultTags(IReadOnlyDictionary <string, string> tags)
        {
            var defaultTags       = tags?.ToImmutableDictionary() ?? ImmutableDictionary <string, string> .Empty;
            var defaultTagBuilder = defaultTags.ToBuilder();

            foreach (var key in defaultTags.Keys.ToArray())
            {
                if (!MetricValidation.IsValidTagName(key))
                {
                    throw new Exception($"\"{key}\" is not a valid tag name.");
                }

                if (TagValueConverter != null)
                {
                    defaultTagBuilder[key] = TagValueConverter(key, defaultTags[key]);
                }

                if (!MetricValidation.IsValidTagValue(defaultTags[key]))
                {
                    throw new Exception($"\"{defaultTags[key]}\" is not a valid tag value.");
                }
            }

            return(defaultTagBuilder.ToImmutable());
        }
예제 #2
0
        ReadOnlyDictionary <string, string> ValidateDefaultTags(Dictionary <string, string> tags)
        {
            var defaultTags = tags == null
                ? new Dictionary <string, string>()
                : new Dictionary <string, string>(tags);

            foreach (var key in defaultTags.Keys.ToArray())
            {
                if (!MetricValidation.IsValidTagName(key))
                {
                    throw new Exception($"\"{key}\" is not a valid tag name.");
                }

                if (TagValueConverter != null)
                {
                    defaultTags[key] = TagValueConverter(key, defaultTags[key]);
                }

                if (!MetricValidation.IsValidTagValue(defaultTags[key]))
                {
                    throw new Exception($"\"{defaultTags[key]}\" is not a valid tag value.");
                }
            }

            return(new ReadOnlyDictionary <string, string>(defaultTags));
        }
예제 #3
0
        private T GetMetricInternal <T>(string name, bool addPrefix, string unit, string description, T metric, bool mustBeNew) where T : MetricBase
        {
            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)
            {
                if (_rootNameToInfo.TryGetValue(name, out var 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 (!MetricValidation.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 {string.Join(", ", metric.Tags.Keys)}.");
                    }

                    return((T)_rootNameAndTagsToMetric[key]);
                }

                // metric doesn't exist yet.
                _rootNameAndTagsToMetric[key] = metric;
                metric.IsAttached             = true;
                _hasNewMetadata = true;

                var needsPreSerialize = metric.NeedsPreSerializeCalled();
                if (needsPreSerialize)
                {
                    _metricsNeedingPreSerialize.Add(metric);
                }

                _metrics.Add(metric);

                if (metric.SerializeInitialValue)
                {
                    if (needsPreSerialize)
                    {
                        metric.PreSerializeInternal();
                    }

                    foreach (var endpoint in _endpoints)
                    {
                        using (var batch = endpoint.Handler.BeginBatch())
                        {
                            try
                            {
                                metric.SerializeInternal(batch, DateTime.UtcNow);
                            }
                            catch (Exception ex)
                            {
                                SendExceptionToHandler(ex);
                            }
                        }
                    }
                }

                return(metric);
            }
        }
예제 #4
0
        /// <summary>
        /// Instantiates a new collector. You should typically only instantiate one collector for the lifetime of your
        /// application. It will manage the serialization of metrics and sending data to metric handlers.
        /// </summary>
        /// <param name="options">
        /// <see cref="MetricsCollectorOptions" /> representing the options to use for this collector.
        /// </param>
        public MetricsCollector(MetricsCollectorOptions options)
        {
            ExceptionHandler  = options.ExceptionHandler ?? (_ => { });
            MetricsNamePrefix = options.MetricsNamePrefix ?? "";
            if (MetricsNamePrefix != "" && !MetricValidation.IsValidMetricName(MetricsNamePrefix))
            {
                throw new Exception("\"" + MetricsNamePrefix + "\" is not a valid metric name prefix.");
            }

            _endpoints = options.Endpoints?.ToImmutableArray() ?? ImmutableArray <MetricEndpoint> .Empty;
            _sets      = options.Sets?.ToImmutableArray() ?? ImmutableArray <IMetricSet> .Empty;

            ThrowOnPostFail   = options.ThrowOnPostFail;
            ThrowOnQueueFull  = options.ThrowOnQueueFull;
            ReportingInterval = options.SnapshotInterval;
            FlushInterval     = options.FlushInterval;
            RetryInterval     = options.RetryInterval;
            PropertyToTagName = options.PropertyToTagName;
            TagValueConverter = options.TagValueConverter;
            DefaultTags       = ValidateDefaultTags(options.DefaultTags);

            _maxRetries          = 3;
            _shutdownTokenSource = new CancellationTokenSource();

            // initialize any metric sets
            foreach (var set in _sets)
            {
                set.Initialize(this);
            }

            // start continuous queue-flushing
            _flushTask = Task.Run(
                async() =>
            {
                while (!_shutdownTokenSource.IsCancellationRequested)
                {
                    await Task.Delay(FlushInterval);

                    try
                    {
                        await FlushAsync(true);
                    }
                    catch (Exception ex)
                    {
                        SendExceptionToHandler(ex);
                    }
                }
            });

            // start reporting timer
            _reportingTask = Task.Run(
                async() =>
            {
                while (!_shutdownTokenSource.IsCancellationRequested)
                {
                    await Task.Delay(ReportingInterval);

                    try
                    {
                        await SnapshotAsync(true);
                    }
                    catch (Exception ex)
                    {
                        SendExceptionToHandler(ex);
                    }
                }
            });
        }