Skip to content

Commit

Permalink
Added integration test for PrometheusExporterMiddleware. (#2456)
Browse files Browse the repository at this point in the history
Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
  • Loading branch information
CodeBlanch and cijothomas authored Oct 6, 2021
1 parent 8159df1 commit 3d16cd8
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,18 @@ internal static class PrometheusExporterExtensions
private const string PrometheusHistogramBucketLabelPositiveInfinity = "+Inf";
private const string PrometheusHistogramBucketLabelLessThan = "le";

private static readonly Func<DateTimeOffset> DefaultGetUtcNowDateTimeOffset = () => DateTimeOffset.UtcNow;

/// <summary>
/// Serialize metrics to prometheus format.
/// </summary>
/// <param name="exporter"><see cref="PrometheusExporter"/>.</param>
/// <param name="writer">StreamWriter to write to.</param>
/// <param name="getUtcNowDateTimeOffset">Optional function to resolve the current date &amp; time.</param>
/// <returns><see cref="Task"/> to await the operation.</returns>
public static async Task WriteMetricsCollection(this PrometheusExporter exporter, StreamWriter writer, Func<DateTimeOffset> getUtcNowDateTimeOffset = null)
public static async Task WriteMetricsCollection(this PrometheusExporter exporter, StreamWriter writer, Func<DateTimeOffset> getUtcNowDateTimeOffset)
{
foreach (var metric in exporter.Metrics)
{
var builder = new PrometheusMetricBuilder(getUtcNowDateTimeOffset ?? DefaultGetUtcNowDateTimeOffset)
var builder = new PrometheusMetricBuilder(getUtcNowDateTimeOffset)
.WithName(metric.Name)
.WithDescription(metric.Description);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ private async Task ProcessExportRequest(HttpListenerContext context)
{
this.exporter.Collect(Timeout.Infinite);

await this.exporter.WriteMetricsCollection(writer).ConfigureAwait(false);
await this.exporter.WriteMetricsCollection(writer, this.exporter.Options.GetUtcNowDateTimeOffset).ConfigureAwait(false);
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -97,10 +98,10 @@ internal static async Task WriteMetricsToResponse(PrometheusExporter exporter, H
response.StatusCode = 200;
response.ContentType = PrometheusMetricBuilder.ContentType;

using var writer = new StreamWriter(response.Body);
using var writer = new StreamWriter(response.Body, encoding: Encoding.UTF8, leaveOpen: true);
try
{
await exporter.WriteMetricsCollection(writer).ConfigureAwait(false);
await exporter.WriteMetricsCollection(writer, exporter.Options.GetUtcNowDateTimeOffset).ConfigureAwait(false);
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public async Task Write(StreamWriter writer)

await writer.WriteAsync("# HELP ").ConfigureAwait(false);
await writer.WriteAsync(this.name).ConfigureAwait(false);
await writer.WriteAsync(" ").ConfigureAwait(false);
await writer.WriteAsync(GetSafeMetricDescription(this.description)).ConfigureAwait(false);
await writer.WriteAsync("\n").ConfigureAwait(false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// limitations under the License.
// </copyright>

using System;
using System.Collections.Generic;

namespace OpenTelemetry.Exporter
Expand All @@ -24,6 +25,7 @@ namespace OpenTelemetry.Exporter
public class PrometheusExporterOptions
{
internal const string DefaultScrapeEndpointPath = "/metrics";
internal Func<DateTimeOffset> GetUtcNowDateTimeOffset = () => DateTimeOffset.UtcNow;

#if NETCOREAPP3_1_OR_GREATER
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@

<ItemGroup>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Prometheus\OpenTelemetry.Exporter.Prometheus.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Extensions.Hosting\OpenTelemetry.Extensions.Hosting.csproj" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="5.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ namespace OpenTelemetry.Exporter.Prometheus.Tests
{
public sealed class PrometheusExporterExtensionsTests
{
private const string MeterName = "PrometheusExporterExtensionsTests.WriteMetricsCollectionTest.Meter";
private const string MeterName = "PrometheusExporterExtensionsTests.Meter";

[Theory]
[InlineData(nameof(WriteLongSum))]
[InlineData(nameof(WriteDoubleSum))]
[InlineData(nameof(WriteLongGauge))]
[InlineData(nameof(WriteDoubleGauge))]
[InlineData(nameof(WriteHistogram))]
[InlineData(nameof(WriteMultiple))]
public void WriteMetricsCollectionTest(string writeActionMethodName)
{
using var meter = new Meter(MeterName, "0.0.1");
Expand Down Expand Up @@ -92,7 +93,7 @@ private static string WriteLongSum(Meter meter, KeyValuePair<string, object>[] t
var counter = meter.CreateCounter<int>("counter_int", description: "Prometheus help text goes here \n escaping.");
counter.Add(100, tags);

return $"# HELP counter_intPrometheus help text goes here \\n escaping.\n# TYPE counter_int counter\ncounter_int{tagsExpected} 100 1633041000000\n";
return $"# HELP counter_int Prometheus help text goes here \\n escaping.\n# TYPE counter_int counter\ncounter_int{tagsExpected} 100 1633041000000\n";
}

private static string WriteDoubleSum(Meter meter, KeyValuePair<string, object>[] tags, string tagsExpected)
Expand Down Expand Up @@ -144,5 +145,12 @@ private static string WriteHistogram(Meter meter, KeyValuePair<string, object>[]
+ "histogram_name_bucket{key1=\"value1\",key2=\"value2\",le=\"1000\"} 2 1633041000000\n"
+ "histogram_name_bucket{key1=\"value1\",key2=\"value2\",le=\"+Inf\"} 2 1633041000000\n";
}

private static string WriteMultiple(Meter meter, KeyValuePair<string, object>[] tags, string tagsExpected)
{
string expected = WriteLongSum(meter, tags, tagsExpected);
expected += WriteDoubleSum(meter, tags, tagsExpected);
return expected;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// <copyright file="PrometheusExporterMiddlewareTests.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

#if !NET461
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Metrics;
using Xunit;

namespace OpenTelemetry.Exporter.Prometheus.Tests
{
public sealed class PrometheusExporterMiddlewareTests
{
private const string MeterName = "PrometheusExporterMiddlewareTests.Meter";

[Fact]
public async Task PrometheusExporterMiddlewareIntegration()
{
var host = await new HostBuilder()
.ConfigureWebHost(webBuilder => webBuilder
.UseTestServer()
.UseStartup<Startup>())
.StartAsync();

var tags = new KeyValuePair<string, object>[]
{
new KeyValuePair<string, object>("key1", "value1"),
new KeyValuePair<string, object>("key2", "value2"),
};

using var meter = new Meter(MeterName, "0.0.1");

var counter = meter.CreateCounter<double>("counter_double");
counter.Add(100.18D, tags);
counter.Add(0.99D, tags);

using var response = await host.GetTestClient().GetAsync("/metrics");

Assert.Equal(HttpStatusCode.OK, response.StatusCode);

string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

Assert.Equal(
$"# TYPE counter_double counter\ncounter_double{{key1=\"value1\",key2=\"value2\"}} 101.17 1633041000000\n",
content);

await host.StopAsync().ConfigureAwait(false);
}

public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddOpenTelemetryMetrics(builder => builder
.AddMeter(MeterName)
.AddPrometheusExporter(o =>
{
o.GetUtcNowDateTimeOffset = () => new DateTimeOffset(2021, 9, 30, 22, 30, 0, TimeSpan.Zero);
if (o.StartHttpListener)
{
throw new InvalidOperationException("StartHttpListener should be false on .NET Core 3.1+.");
}
}));
}

public void Configure(IApplicationBuilder app)
{
app.UseOpenTelemetryPrometheusScrapingEndpoint();
}
}
}
}
#endif

0 comments on commit 3d16cd8

Please sign in to comment.