public async Task ReportMetricAsync_GetsValidInputWithTwoMetricValues_SuccessfullyWritesMultipleMetrics() { // Arrange var metricName = _bogus.Name.FirstName(); var metricDescription = _bogus.Lorem.Sentence(); var firstMetricValue = _bogus.Random.Double(); var secondMetricValue = _bogus.Random.Double(); var firstMetric = MeasuredMetric.CreateWithoutDimension(firstMetricValue); var secondMetric = MeasuredMetric.CreateWithoutDimension(secondMetricValue); var scrapeResult = ScrapeResultGenerator.GenerateFromMetric(firstMetric); scrapeResult.MetricValues.Add(secondMetric); var metricsDeclarationProvider = CreateMetricsDeclarationProvider(metricName); var prometheusConfiguration = CreatePrometheusConfiguration(); var mocks = CreatePrometheusMetricFactoryMock(); var metricSink = new PrometheusScrapingEndpointMetricSink(mocks.Factory.Object, metricsDeclarationProvider, prometheusConfiguration, NullLogger <PrometheusScrapingEndpointMetricSink> .Instance); // Act await metricSink.ReportMetricAsync(metricName, metricDescription, scrapeResult); // Assert mocks.Factory.Verify(mock => mock.CreateGauge(metricName, metricDescription, It.IsAny <bool>(), It.Is <string[]>(specified => EnsureAllArrayEntriesAreSpecified(specified, scrapeResult.Labels.Keys.ToArray()))), Times.Exactly(2)); mocks.MetricFamily.Verify(mock => mock.WithLabels(It.Is <string[]>(specified => EnsureAllArrayEntriesAreSpecified(specified, scrapeResult.Labels.Values.ToArray()))), Times.Exactly(2)); mocks.Gauge.Verify(mock => mock.Set(firstMetricValue), Times.Once()); mocks.Gauge.Verify(mock => mock.Set(secondMetricValue), Times.Once()); }
public async Task ReportMetricAsync_GetsValidInputWithOneDimensions_SuccessfullyWritesMetric() { // Arrange var metricName = _bogus.Name.FirstName(); var metricDescription = _bogus.Lorem.Sentence(); var metricValue = _bogus.Random.Double(); var dimensionName = _bogus.Name.FirstName(); var dimensionValue = _bogus.Name.FirstName(); var expectedDimensionName = dimensionName.ToLower(); var timeSeries = new TimeSeriesElement { Metadatavalues = new List <MetadataValue> { new MetadataValue(name: new LocalizableString(dimensionName), value: dimensionValue) } }; var measuredMetric = MeasuredMetric.CreateForDimension(metricValue, dimensionName.ToUpper(), timeSeries); var scrapeResult = ScrapeResultGenerator.GenerateFromMetric(measuredMetric); var metricsDeclarationProvider = CreateMetricsDeclarationProvider(metricName); var prometheusConfiguration = CreatePrometheusConfiguration(); var mocks = CreatePrometheusMetricFactoryMock(); var metricSink = new PrometheusScrapingEndpointMetricSink(mocks.Factory.Object, metricsDeclarationProvider, prometheusConfiguration, NullLogger <PrometheusScrapingEndpointMetricSink> .Instance); // Act await metricSink.ReportMetricAsync(metricName, metricDescription, scrapeResult); // Assert mocks.Factory.Verify(mock => mock.CreateGauge(metricName, metricDescription, It.IsAny <bool>(), It.Is <string[]>(specified => specified.Contains(expectedDimensionName) && EnsureAllArrayEntriesAreSpecified(specified, scrapeResult.Labels.Keys.ToArray()))), Times.Once()); mocks.MetricFamily.Verify(mock => mock.WithLabels(It.Is <string[]>(specified => specified.Contains(dimensionValue) && EnsureAllArrayEntriesAreSpecified(specified, scrapeResult.Labels.Values.ToArray()))), Times.Once()); mocks.Gauge.Verify(mock => mock.Set(metricValue), Times.Once()); }
public static ScrapeResult GenerateFromMetric(MeasuredMetric measuredMetric) { var subscriptionId = bogus.Name.FirstName(); var resourceGroupName = bogus.Name.FirstName(); var instanceName = bogus.Name.FirstName(); var resourceUri = bogus.Internet.Url(); return(new ScrapeResult(subscriptionId, resourceGroupName, instanceName, resourceUri, new List <MeasuredMetric> { measuredMetric })); }
public async Task ReportMetricAsync_InputDoesNotContainMetricDescription_Succeeds(string metricDescription) { // Arrange var metricName = _bogus.Name.FirstName(); var metricValue = _bogus.Random.Double(); var measuredMetric = MeasuredMetric.CreateWithoutDimension(metricValue); var statsDPublisherMock = new Mock <IStatsDPublisher>(); var metricSink = new StatsdMetricSink(statsDPublisherMock.Object, NullLogger <StatsdMetricSink> .Instance); // Act & Assert // ReSharper disable once ExpressionIsAlwaysNull await metricSink.ReportMetricAsync(metricName, metricDescription, measuredMetric); }
public async Task ReportMetricAsync_InputDoesNotContainMeasuredMetric_ThrowsException() { // Arrange var metricName = _bogus.Name.FirstName(); var metricDescription = _bogus.Lorem.Sentence(); MeasuredMetric measuredMetric = null; var statsDPublisherMock = new Mock <IStatsDPublisher>(); var metricSink = new StatsdMetricSink(statsDPublisherMock.Object, NullLogger <StatsdMetricSink> .Instance); // Act & Assert // ReSharper disable once ExpressionIsAlwaysNull await Assert.ThrowsAsync <ArgumentNullException>(() => metricSink.ReportMetricAsync(metricName, metricDescription, measuredMetric)); }
public async Task ReportMetricAsync_InputDoesNotContainMetricName_ThrowsException(string metricName) { // Arrange var metricDescription = _bogus.Lorem.Sentence(); var metricValue = _bogus.Random.Double(); var measuredMetric = MeasuredMetric.CreateWithoutDimension(metricValue); var statsDPublisherMock = new Mock <IStatsDPublisher>(); var metricSink = new StatsdMetricSink(statsDPublisherMock.Object, NullLogger <StatsdMetricSink> .Instance); // Act & Assert // ReSharper disable once ExpressionIsAlwaysNull await Assert.ThrowsAsync <ArgumentException>(() => metricSink.ReportMetricAsync(metricName, metricDescription, measuredMetric)); }
public Task ReportMetricAsync(string metricName, string metricDescription, MeasuredMetric measuredMetric) { Guard.NotNullOrEmpty(metricName, nameof(metricName)); Guard.NotNull(measuredMetric, nameof(measuredMetric)); var metricValue = measuredMetric.Value ?? 0; _statsDPublisher.Gauge(metricValue, metricName); _logger.LogTrace("Metric {MetricName} with value {MetricValue} was written to StatsD server", metricName, metricValue); return(Task.CompletedTask); }
public async Task ReportMetricAsync_InputDoesNotContainMetricDescription_Succeeds(string metricDescription) { // Arrange var metricName = BogusGenerator.Name.FirstName(); var metricValue = BogusGenerator.Random.Double(); var measuredMetric = MeasuredMetric.CreateWithoutDimension(metricValue); var scrapeResult = ScrapeResultGenerator.GenerateFromMetric(measuredMetric); var systemMetricConfigOptions = BogusAtlassianStatuspageMetricSinkConfigurationGenerator.GetSinkConfiguration(); var atlassianStatuspageClientMock = new Mock <IAtlassianStatuspageClient>(); var metricSink = new AtlassianStatuspageMetricSink(atlassianStatuspageClientMock.Object, systemMetricConfigOptions, NullLogger <AtlassianStatuspageMetricSink> .Instance); // Act & Assert // ReSharper disable once ExpressionIsAlwaysNull await metricSink.ReportMetricAsync(metricName, metricDescription, scrapeResult); }
public async Task ReportMetricAsync_GetsValidInputWithMetricValue_SuccessfullyWritesMetric() { // Arrange var metricName = _bogus.Name.FirstName(); var metricDescription = _bogus.Lorem.Sentence(); var metricValue = _bogus.Random.Double(); var measuredMetric = MeasuredMetric.CreateWithoutDimension(metricValue); var statsDPublisherMock = new Mock <IStatsDPublisher>(); var metricSink = new StatsdMetricSink(statsDPublisherMock.Object, NullLogger <StatsdMetricSink> .Instance); // Act await metricSink.ReportMetricAsync(metricName, metricDescription, measuredMetric); // Assert statsDPublisherMock.Verify(mock => mock.Gauge(metricValue, metricName), Times.Once()); }
public async Task ReportMetricAsync_GetsValidInputWithoutMetricValue_SuccessfullyWritesMetricWithDefault() { // Arrange const double expectedDefaultValue = 0; var metricName = _bogus.Name.FirstName(); var metricDescription = _bogus.Lorem.Sentence(); double? metricValue = null; // ReSharper disable once ExpressionIsAlwaysNull var measuredMetric = MeasuredMetric.CreateWithoutDimension(metricValue); var statsDPublisherMock = new Mock <IStatsDPublisher>(); var metricSink = new StatsdMetricSink(statsDPublisherMock.Object, NullLogger <StatsdMetricSink> .Instance); // Act await metricSink.ReportMetricAsync(metricName, metricDescription, measuredMetric); // Assert statsDPublisherMock.Verify(mock => mock.Gauge(expectedDefaultValue, metricName), Times.Once()); }
/// <inheritdoc /> protected override async Task <ScrapeResult> ScrapeResourceAsync(string subscriptionId, ScrapeDefinition <IAzureResourceDefinition> scrapeDefinition, TResourceDefinition resourceDefinition, AggregationType aggregationType, TimeSpan aggregationInterval) { Guard.NotNull(scrapeDefinition, nameof(scrapeDefinition)); Guard.NotNull(scrapeDefinition.AzureMetricConfiguration, nameof(scrapeDefinition.AzureMetricConfiguration)); var metricName = scrapeDefinition.AzureMetricConfiguration.MetricName; // Compose URI of the resource to measure var resourceUri = BuildResourceUri(subscriptionId, scrapeDefinition, resourceDefinition); // Determine the metric filter to use, if any var metricFilter = DetermineMetricFilter(metricName, resourceDefinition); // Determine the metric limit to use, if any var metricLimit = DetermineMetricLimit(scrapeDefinition); // Determine the metric dimension to use, if any var dimensionName = DetermineMetricDimension(metricName, resourceDefinition, scrapeDefinition.AzureMetricConfiguration?.Dimension); List <MeasuredMetric> measuredMetrics = new List <MeasuredMetric>(); try { // Query Azure Monitor for metrics measuredMetrics = await AzureMonitorClient.QueryMetricAsync(metricName, dimensionName, aggregationType, aggregationInterval, resourceUri, metricFilter, metricLimit); } catch (MetricInformationNotFoundException metricsNotFoundException) { Logger.LogWarning("No metric information found for metric {MetricName} with dimension {MetricDimension}. Details: {Details}", metricsNotFoundException.Name, metricsNotFoundException.Dimension, metricsNotFoundException.Details); var measuredMetric = string.IsNullOrWhiteSpace(dimensionName) ? MeasuredMetric.CreateWithoutDimension(null) : MeasuredMetric.CreateForDimension(null, dimensionName, "unknown"); measuredMetrics.Add(measuredMetric); } // Provide more metric labels, if we need to var metricLabels = DetermineMetricLabels(resourceDefinition); // Enrich measured metrics, in case we need to var finalMetricValues = EnrichMeasuredMetrics(resourceDefinition, dimensionName, measuredMetrics); // We're done! return(new ScrapeResult(subscriptionId, scrapeDefinition.ResourceGroupName, resourceDefinition.ResourceName, resourceUri, finalMetricValues, metricLabels)); }
public async Task ReportMetricAsync_GetsValidInputWithMetricValueAndPromitorToSystemMetricMapping_SuccessfullyWritesMetric() { // Arrange var promitorMetricName = BogusGenerator.Name.FirstName(); var systemMetricId = BogusGenerator.Name.FirstName(); var metricDescription = BogusGenerator.Lorem.Sentence(); var metricValue = BogusGenerator.Random.Double(); var measuredMetric = MeasuredMetric.CreateWithoutDimension(metricValue); var scrapeResult = ScrapeResultGenerator.GenerateFromMetric(measuredMetric); var systemMetricConfigOptions = BogusAtlassianStatuspageMetricSinkConfigurationGenerator.GetSinkConfiguration(systemMetricId: systemMetricId, promitorMetricName: promitorMetricName); var atlassianStatuspageClientMock = new Mock <IAtlassianStatuspageClient>(); var metricSink = new AtlassianStatuspageMetricSink(atlassianStatuspageClientMock.Object, systemMetricConfigOptions, NullLogger <AtlassianStatuspageMetricSink> .Instance); // Act await metricSink.ReportMetricAsync(promitorMetricName, metricDescription, scrapeResult); // Assert atlassianStatuspageClientMock.Verify(mock => mock.ReportMetricAsync(systemMetricId, metricValue), Times.Once()); }
public async Task ReportMetricAsync_WriteToStatsDSink_Succeeds() { // Arrange var metricName = _bogus.Name.FirstName(); var metricDescription = _bogus.Lorem.Sentence(); var metricValue = _bogus.Random.Double(); var measuredMetric = MeasuredMetric.CreateWithoutDimension(metricValue); var scrapeResult = GenerateScrapeResult(measuredMetric); var statsDPublisherMock = new Mock <IStatsDPublisher>(); var statsdMetricSink = new StatsdMetricSink(statsDPublisherMock.Object, NullLogger <StatsdMetricSink> .Instance); var metricSinkWriter = new MetricSinkWriter(new List <IMetricSink> { statsdMetricSink }, NullLogger <MetricSinkWriter> .Instance); // Act await metricSinkWriter.ReportMetricAsync(metricName, metricDescription, scrapeResult); // Assert statsDPublisherMock.Verify(mock => mock.Gauge(metricValue, metricName), Times.Once()); }
public async Task ReportMetricAsync_GetsValidInputWithPromitorMetricThatIsNotMappedToSystemMetricId_DoesNotWriteMetric() { // Arrange const double expectedDefaultValue = 0; var promitorMetricName = BogusGenerator.Name.FirstName(); var systemMetricId = BogusGenerator.Name.FirstName(); var metricDescription = BogusGenerator.Lorem.Sentence(); double? metricValue = null; // ReSharper disable once ExpressionIsAlwaysNull var measuredMetric = MeasuredMetric.CreateWithoutDimension(metricValue); var scrapeResult = ScrapeResultGenerator.GenerateFromMetric(measuredMetric); var systemMetricConfigOptions = BogusAtlassianStatuspageMetricSinkConfigurationGenerator.GetSinkConfiguration(promitorMetricName: promitorMetricName); var atlassianStatuspageClientMock = new Mock <IAtlassianStatuspageClient>(); var metricSink = new AtlassianStatuspageMetricSink(atlassianStatuspageClientMock.Object, systemMetricConfigOptions, NullLogger <AtlassianStatuspageMetricSink> .Instance); // Act await metricSink.ReportMetricAsync(promitorMetricName, metricDescription, scrapeResult); // Assert atlassianStatuspageClientMock.Verify(mock => mock.ReportMetricAsync(systemMetricId, expectedDefaultValue), Times.Never); }
protected override async Task <ScrapeResult> ScrapeResourceAsync(string subscriptionId, ScrapeDefinition <IAzureResourceDefinition> scrapeDefinition, StorageQueueResourceDefinition resource, AggregationType aggregationType, TimeSpan aggregationInterval) { Guard.NotNull(scrapeDefinition, nameof(scrapeDefinition)); Guard.NotNull(scrapeDefinition.AzureMetricConfiguration, nameof(scrapeDefinition.AzureMetricConfiguration)); Guard.NotNull(resource.SasToken, nameof(resource.SasToken)); Guard.NotNullOrEmpty(scrapeDefinition.AzureMetricConfiguration.MetricName, nameof(scrapeDefinition.AzureMetricConfiguration.MetricName)); var resourceUri = string.Format(ResourceUriTemplate, subscriptionId, scrapeDefinition.ResourceGroupName, resource.AccountName); var sasToken = resource.SasToken.GetSecretValue(); double foundMetricValue; switch (scrapeDefinition.AzureMetricConfiguration.MetricName.ToLowerInvariant()) { case AzureStorageConstants.Queues.Metrics.TimeSpentInQueue: foundMetricValue = await _azureStorageQueueClient.GetQueueMessageTimeSpentInQueueAsync(resource.AccountName, resource.QueueName, sasToken); break; case AzureStorageConstants.Queues.Metrics.MessageCount: foundMetricValue = await _azureStorageQueueClient.GetQueueMessageCountAsync(resource.AccountName, resource.QueueName, sasToken); break; default: throw new InvalidMetricNameException(scrapeDefinition.AzureMetricConfiguration.MetricName, resource.ResourceType.ToString()); } var labels = new Dictionary <string, string> { { "queue_name", resource.QueueName } }; var measuredMetrics = new List <MeasuredMetric> { MeasuredMetric.CreateWithoutDimension(foundMetricValue) }; return(new ScrapeResult(subscriptionId, scrapeDefinition.ResourceGroupName, resource.AccountName, resourceUri, measuredMetrics, labels)); }
public async Task ReportMetricAsync_GetsValidInputWithoutMetricValue_SuccessfullyWritesMetricWithDefault(double?expectedDefaultValue) { // Arrange double expectedMeasuredMetric = expectedDefaultValue ?? double.NaN; // If nothing is configured, NaN is used. var metricName = _bogus.Name.FirstName(); var metricDescription = _bogus.Lorem.Sentence(); double?metricValue = null; // ReSharper disable once ExpressionIsAlwaysNull var measuredMetric = MeasuredMetric.CreateWithoutDimension(metricValue); var scrapeResult = ScrapeResultGenerator.GenerateFromMetric(measuredMetric); var metricsDeclarationProvider = CreateMetricsDeclarationProvider(metricName); var prometheusConfiguration = CreatePrometheusConfiguration(metricUnavailableValue: expectedDefaultValue); var mocks = CreatePrometheusMetricFactoryMock(); var metricSink = new PrometheusScrapingEndpointMetricSink(mocks.Factory.Object, metricsDeclarationProvider, prometheusConfiguration, NullLogger <PrometheusScrapingEndpointMetricSink> .Instance); // Act await metricSink.ReportMetricAsync(metricName, metricDescription, scrapeResult); // Assert mocks.Factory.Verify(mock => mock.CreateGauge(metricName, metricDescription, It.IsAny <bool>(), It.Is <string[]>(specified => EnsureAllArrayEntriesAreSpecified(specified, scrapeResult.Labels.Keys.ToArray()))), Times.Once()); mocks.MetricFamily.Verify(mock => mock.WithLabels(It.Is <string[]>(specified => EnsureAllArrayEntriesAreSpecified(specified, scrapeResult.Labels.Values.ToArray()))), Times.Once()); mocks.Gauge.Verify(mock => mock.Set(expectedMeasuredMetric), Times.Once()); }
private (string[] Names, string[] Values) DetermineLabels(PrometheusMetricDefinition metricDefinition, ScrapeResult scrapeResult, MeasuredMetric measuredMetric) { var labels = new Dictionary <string, string>(scrapeResult.Labels); if (measuredMetric.IsDimensional) { labels.Add(measuredMetric.DimensionName.ToLower(), measuredMetric.DimensionValue); } if (metricDefinition?.Labels?.Any() == true) { foreach (var customLabel in metricDefinition.Labels) { if (labels.ContainsKey(customLabel.Key)) { _logger.LogWarning("Custom label {CustomLabelName} was already specified with value 'LabelValue' instead of 'CustomLabelValue'. Ignoring...", customLabel.Key, labels[customLabel.Key], customLabel.Value); continue; } labels.Add(customLabel.Key, customLabel.Value); } } return(labels.Keys.ToArray(), labels.Values.ToArray()); }
private double DetermineMetricMeasurement(MeasuredMetric scrapedMetricResult) { var metricUnavailableValue = _prometheusConfiguration.CurrentValue?.MetricUnavailableValue ?? Defaults.Prometheus.MetricUnavailableValue; return(scrapedMetricResult.Value ?? metricUnavailableValue); }
private ScrapeResult GenerateScrapeResult(double metricValue) { return(GenerateScrapeResult(MeasuredMetric.CreateWithoutDimension(metricValue))); }
private Dictionary <string, string> DetermineLabels(PrometheusMetricDefinition metricDefinition, ScrapeResult scrapeResult, MeasuredMetric measuredMetric) { var labels = new Dictionary <string, string>(scrapeResult.Labels.Select(label => new KeyValuePair <string, string>(label.Key.SanitizeForPrometheusLabelKey(), label.Value))); if (measuredMetric.IsDimensional) { labels.Add(measuredMetric.DimensionName.SanitizeForPrometheusLabelKey(), measuredMetric.DimensionValue); } if (metricDefinition?.Labels?.Any() == true) { foreach (var customLabel in metricDefinition.Labels) { var customLabelKey = customLabel.Key.SanitizeForPrometheusLabelKey(); if (labels.ContainsKey(customLabelKey)) { _logger.LogWarning("Custom label {CustomLabelName} was already specified with value 'LabelValue' instead of 'CustomLabelValue'. Ignoring...", customLabel.Key, labels[customLabelKey], customLabel.Value); continue; } labels.Add(customLabelKey, customLabel.Value); } } return(labels); }
private Dictionary <string, string> DetermineLabels(PrometheusMetricDefinition metricDefinition, ScrapeResult scrapeResult, MeasuredMetric measuredMetric, Dictionary <string, string> defaultLabels) { var labels = new Dictionary <string, string>(scrapeResult.Labels.Select(label => new KeyValuePair <string, string>(label.Key.SanitizeForPrometheusLabelKey(), label.Value))); if (measuredMetric.IsDimensional) { labels.Add(measuredMetric.DimensionName.SanitizeForPrometheusLabelKey(), measuredMetric.DimensionValue); } if (metricDefinition?.Labels?.Any() == true) { foreach (var customLabel in metricDefinition.Labels) { var customLabelKey = customLabel.Key.SanitizeForPrometheusLabelKey(); if (labels.ContainsKey(customLabelKey)) { _logger.LogWarning("Custom label {CustomLabelName} was already specified with value '{LabelValue}' instead of '{CustomLabelValue}'. Ignoring...", customLabel.Key, labels[customLabelKey], customLabel.Value); continue; } labels.Add(customLabelKey, customLabel.Value); } } foreach (var defaultLabel in defaultLabels) { var defaultLabelKey = defaultLabel.Key.SanitizeForPrometheusLabelKey(); if (labels.ContainsKey(defaultLabelKey) == false) { labels.Add(defaultLabelKey, defaultLabel.Value); } } // Add the tenant id var metricsDeclaration = _metricsDeclarationProvider.Get(applyDefaults: true); if (labels.ContainsKey("tenant_id") == false) { labels.Add("tenant_id", metricsDeclaration.AzureMetadata.TenantId); } // Transform labels, if need be if (_prometheusConfiguration.CurrentValue.Labels != null) { labels = LabelTransformer.TransformLabels(_prometheusConfiguration.CurrentValue.Labels.Transformation, labels); } var orderedLabels = labels.OrderBy(kvp => kvp.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); return(orderedLabels); }
public static ScrapeResult Generate(double metricValue) { return(GenerateFromMetric(MeasuredMetric.CreateWithoutDimension(metricValue))); }