Beispiel #1
0
        private 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 (!BosunValidation.IsValidTagName(key))
                {
                    throw new Exception($"\"{key}\" is not a valid Bosun tag name.");
                }

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

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

            return(new ReadOnlyDictionary <string, string>(defaultTags));
        }
Beispiel #2
0
        public MetricsCollector(BosunOptions options)
        {
            MetricsNamePrefix = options.MetricsNamePrefix ?? "";
            if (MetricsNamePrefix != "" && !BosunValidation.IsValidMetricName(MetricsNamePrefix))
            {
                throw new Exception("\"" + MetricsNamePrefix + "\" is not a valid metric name prefix.");
            }

            GetBosunUrl       = options.GetBosunUrl;
            BosunUrl          = GetBosunUrl == null ? options.BosunUrl : GetBosunUrl();
            MaxQueueLength    = options.MaxQueueLength;
            BatchSize         = options.BatchSize;
            ThrowOnPostFail   = options.ThrowOnPostFail;
            ThrowOnQueueFull  = options.ThrowOnQueueFull;
            ReportingInterval = options.ReportingInterval;
            PropertyToTagName = options.PropertyToTagName;
            TagValueConverter = options.TagValueConverter;
            DefaultTags       = ValidateDefaultTags(options.DefaultTags);

            // start continuous queue-flushing
            _flushTimer = new Timer(Flush, true, 1000, 1000);

            // start reporting timer
            var interval = TimeSpan.FromSeconds(ReportingInterval);

            _reportingTimer = new Timer(Snapshot, true, interval, interval);

            // metadata timer - wait 30 seconds to start (so there is some time for metrics to be delcared)
            if (options.MetaDataReportingInterval > 0)
            {
                _metaDataTimer = new Timer(PostMetaData, true, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(options.MetaDataReportingInterval));
            }
        }
        /// <summary>
        /// Instantiates a new collector (the primary class of BosunReporter). You should typically only instantiate one collector for the lifetime of your
        /// application. It will manage the serialization of metrics and sending data to Bosun.
        /// </summary>
        /// <param name="options"></param>
        /// <param name="exceptionHandler"></param>
        public MetricsCollector(BosunOptions options, Action <Exception> exceptionHandler)
        {
            if (exceptionHandler == null)
            {
                throw new ArgumentNullException(nameof(exceptionHandler));
            }

            ExceptionHandler = exceptionHandler;

            _localMetricsQueue    = new PayloadQueue(QueueType.Local);
            _externalCounterQueue = new PayloadQueue(QueueType.ExternalCounters);

            _localMetricsQueue.PayloadDropped    += OnPayloadDropped;
            _externalCounterQueue.PayloadDropped += OnPayloadDropped;

            // these two setters actually update the queues themselves
            MaxPayloadSize     = options.MaxPayloadSize;
            MaxPendingPayloads = options.MaxPendingPayloads;

            MetricsNamePrefix = options.MetricsNamePrefix ?? "";
            if (MetricsNamePrefix != "" && !BosunValidation.IsValidMetricName(MetricsNamePrefix))
            {
                throw new Exception("\"" + MetricsNamePrefix + "\" is not a valid metric name prefix.");
            }

            GetBosunUrl = options.GetBosunUrl;
            BosunUrl    = GetBosunUrl == null ? options.BosunUrl : GetBosunUrl();

            _accessToken    = options.AccessToken;
            _getAccessToken = options.GetAccessToken;

            ThrowOnPostFail        = options.ThrowOnPostFail;
            ThrowOnQueueFull       = options.ThrowOnQueueFull;
            ReportingInterval      = options.ReportingInterval;
            EnableExternalCounters = options.EnableExternalCounters;
            PropertyToTagName      = options.PropertyToTagName;
            TagValueConverter      = options.TagValueConverter;
            DefaultTags            = ValidateDefaultTags(options.DefaultTags);

            // start continuous queue-flushing
            _flushTimer = new Timer(Flush, true, 1000, 1000);

            // start reporting timer
            var interval = TimeSpan.FromSeconds(ReportingInterval);

            _reportingTimer = new Timer(Snapshot, true, interval, interval);

            // metadata timer - wait 30 seconds to start (so there is some time for metrics to be delcared)
            if (options.MetaDataReportingInterval > 0)
            {
                _metaDataTimer = new Timer(PostMetaData, true, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(options.MetaDataReportingInterval));
            }
        }
Beispiel #4
0
        /// <summary>
        /// Instantiates a new collector (the primary class of BosunReporter). You should typically only instantiate one collector for the lifetime of your
        /// application. It will manage the serialization of metrics and sending data to Bosun.
        /// </summary>
        /// <param name="options"></param>
        public MetricsCollector(BosunOptions options)
        {
            ExceptionHandler = options.ExceptionHandler;

            if (options.SnapshotInterval < TimeSpan.FromSeconds(1))
            {
                throw new InvalidOperationException("options.SnapshotInterval cannot be less than one second");
            }

            _localMetricsQueue    = new PayloadQueue(QueueType.Local);
            _externalCounterQueue = new PayloadQueue(QueueType.ExternalCounters);

            _localMetricsQueue.PayloadDropped    += OnPayloadDropped;
            _externalCounterQueue.PayloadDropped += OnPayloadDropped;

            // these two setters actually update the queues themselves
            MaxPayloadSize     = options.MaxPayloadSize;
            MaxPendingPayloads = options.MaxPendingPayloads;

            MetricsNamePrefix = options.MetricsNamePrefix ?? "";
            if (MetricsNamePrefix != "" && !BosunValidation.IsValidMetricName(MetricsNamePrefix))
            {
                throw new Exception("\"" + MetricsNamePrefix + "\" is not a valid metric name prefix.");
            }

            _getUrlDynamic = options.GetBosunUrl;
            _fixedUrl      = options.BosunUrl;

            _accessToken    = options.AccessToken;
            _getAccessToken = options.GetAccessToken;

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

            // start continuous queue-flushing
            _flushTimer = new Timer(Flush, true, 1000, 1000);

            // start reporting timer
            _reportingTimer = new Timer(Snapshot, true, ReportingInterval, ReportingInterval);
        }
Beispiel #5
0
        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);
            }
        }