Skip to content

Commit

Permalink
Merge branch 'main' into vibankwa/re-introduce-grpc-support-experimental
Browse files Browse the repository at this point in the history
  • Loading branch information
utpilla authored Dec 7, 2023
2 parents 8cff782 + 7419d85 commit 2602283
Show file tree
Hide file tree
Showing 15 changed files with 395 additions and 46 deletions.
2 changes: 2 additions & 0 deletions src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

* Export OpenMetrics format from Prometheus exporters ([#5107](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5107))

## 1.7.0-rc.1

Released 2023-Nov-29
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Prometheus.HttpListener\Internal\PrometheusSerializerExt.cs" Link="Includes/PrometheusSerializerExt.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Prometheus.HttpListener\Internal\PrometheusType.cs" Link="Includes/PrometheusType.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Prometheus.HttpListener\Internal\PrometheusMetric.cs" Link="Includes/PrometheusMetric.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Prometheus.HttpListener\Internal\PrometheusHeadersParser.cs" Link="Includes/PrometheusHeadersParser.cs" />
<Compile Include="$(RepoRoot)\src\Shared\ExceptionExtensions.cs" Link="Includes\ExceptionExtensions.cs" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

using System.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using OpenTelemetry.Exporter.Prometheus;
using OpenTelemetry.Internal;
using OpenTelemetry.Metrics;
Expand Down Expand Up @@ -64,7 +65,9 @@ public async Task InvokeAsync(HttpContext httpContext)

try
{
var collectionResponse = await this.exporter.CollectionManager.EnterCollect().ConfigureAwait(false);
var openMetricsRequested = AcceptsOpenMetrics(httpContext.Request);
var collectionResponse = await this.exporter.CollectionManager.EnterCollect(openMetricsRequested).ConfigureAwait(false);

try
{
if (collectionResponse.View.Count > 0)
Expand All @@ -75,7 +78,9 @@ public async Task InvokeAsync(HttpContext httpContext)
#else
response.Headers.Add("Last-Modified", collectionResponse.GeneratedAtUtc.ToString("R"));
#endif
response.ContentType = "text/plain; charset=utf-8; version=0.0.4";
response.ContentType = openMetricsRequested
? "application/openmetrics-text; version=1.0.0; charset=utf-8"
: "text/plain; charset=utf-8; version=0.0.4";

await response.Body.WriteAsync(collectionResponse.View.Array, 0, collectionResponse.View.Count).ConfigureAwait(false);
}
Expand All @@ -102,4 +107,24 @@ public async Task InvokeAsync(HttpContext httpContext)

this.exporter.OnExport = null;
}

private static bool AcceptsOpenMetrics(HttpRequest request)
{
var acceptHeader = request.Headers.Accept;

if (StringValues.IsNullOrEmpty(acceptHeader))
{
return false;
}

foreach (var header in acceptHeader)
{
if (PrometheusHeadersParser.AcceptsOpenMetrics(header))
{
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

* Export OpenMetrics format from Prometheus exporters ([#5107](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5107))

## 1.7.0-rc.1

Released 2023-Nov-29
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public PrometheusCollectionManager(PrometheusExporter exporter)
}

#if NET6_0_OR_GREATER
public ValueTask<CollectionResponse> EnterCollect()
public ValueTask<CollectionResponse> EnterCollect(bool openMetricsRequested)
#else
public Task<CollectionResponse> EnterCollect()
public Task<CollectionResponse> EnterCollect(bool openMetricsRequested)
#endif
{
this.EnterGlobalLock();
Expand Down Expand Up @@ -93,7 +93,7 @@ public Task<CollectionResponse> EnterCollect()
this.ExitGlobalLock();

CollectionResponse response;
var result = this.ExecuteCollect();
var result = this.ExecuteCollect(openMetricsRequested);
if (result)
{
this.previousDataViewGeneratedAtUtc = DateTime.UtcNow;
Expand Down Expand Up @@ -168,9 +168,10 @@ private void WaitForReadersToComplete()
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool ExecuteCollect()
private bool ExecuteCollect(bool openMetricsRequested)
{
this.exporter.OnExport = this.onCollectRef;
this.exporter.OpenMetricsRequested = openMetricsRequested;
var result = this.exporter.Collect(Timeout.Infinite);
this.exporter.OnExport = null;
return result;
Expand All @@ -193,7 +194,13 @@ private ExportResult OnCollect(Batch<Metric> metrics)
{
try
{
cursor = PrometheusSerializer.WriteMetric(this.buffer, cursor, metric, this.GetPrometheusMetric(metric));
cursor = PrometheusSerializer.WriteMetric(
this.buffer,
cursor,
metric,
this.GetPrometheusMetric(metric),
this.exporter.OpenMetricsRequested);

break;
}
catch (IndexOutOfRangeException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ internal Func<Batch<Metric>, ExportResult> OnExport

internal int ScrapeResponseCacheDurationMilliseconds { get; }

internal bool OpenMetricsRequested { get; set; }

/// <inheritdoc/>
public override ExportResult Export(in Batch<Metric> metrics)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// <copyright file="PrometheusHeadersParser.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>

namespace OpenTelemetry.Exporter.Prometheus;

internal static class PrometheusHeadersParser
{
private const string OpenMetricsMediaType = "application/openmetrics-text";

internal static bool AcceptsOpenMetrics(string contentType)
{
var value = contentType.AsSpan();

while (value.Length > 0)
{
var headerValue = SplitNext(ref value, ',');
var mediaType = SplitNext(ref headerValue, ';');

if (mediaType.Equals(OpenMetricsMediaType.AsSpan(), StringComparison.Ordinal))
{
return true;
}
}

return false;
}

private static ReadOnlySpan<char> SplitNext(ref ReadOnlySpan<char> span, char character)
{
var index = span.IndexOf(character);

if (index == -1)
{
var part = span;
span = span.Slice(span.Length);

return part;
}
else
{
var part = span.Slice(0, index);
span = span.Slice(index + 1);

return part;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,32 @@ public static int WriteUnitMetadata(byte[] buffer, int cursor, PrometheusMetric
return cursor;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteTimestamp(byte[] buffer, int cursor, long value, bool useOpenMetrics)
{
if (useOpenMetrics)
{
cursor = WriteLong(buffer, cursor, value / 1000);
buffer[cursor++] = unchecked((byte)'.');

long millis = value % 1000;

if (millis < 100)
{
buffer[cursor++] = unchecked((byte)'0');
}

if (millis < 10)
{
buffer[cursor++] = unchecked((byte)'0');
}

return WriteLong(buffer, cursor, millis);
}

return WriteLong(buffer, cursor, value);
}

private static string MapPrometheusType(PrometheusType type)
{
return type switch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static bool CanWriteMetric(Metric metric)
return true;
}

public static int WriteMetric(byte[] buffer, int cursor, Metric metric, PrometheusMetric prometheusMetric)
public static int WriteMetric(byte[] buffer, int cursor, Metric metric, PrometheusMetric prometheusMetric, bool openMetricsRequested = false)
{
cursor = WriteTypeMetadata(buffer, cursor, prometheusMetric);
cursor = WriteUnitMetadata(buffer, cursor, prometheusMetric);
Expand Down Expand Up @@ -94,7 +94,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe

buffer[cursor++] = unchecked((byte)' ');

cursor = WriteLong(buffer, cursor, timestamp);
cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested);

buffer[cursor++] = ASCII_LINEFEED;
}
Expand Down Expand Up @@ -136,7 +136,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe
cursor = WriteLong(buffer, cursor, totalCount);
buffer[cursor++] = unchecked((byte)' ');

cursor = WriteLong(buffer, cursor, timestamp);
cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested);

buffer[cursor++] = ASCII_LINEFEED;
}
Expand All @@ -163,7 +163,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe
cursor = WriteDouble(buffer, cursor, metricPoint.GetHistogramSum());
buffer[cursor++] = unchecked((byte)' ');

cursor = WriteLong(buffer, cursor, timestamp);
cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested);

buffer[cursor++] = ASCII_LINEFEED;

Expand All @@ -189,14 +189,12 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe
cursor = WriteLong(buffer, cursor, metricPoint.GetHistogramCount());
buffer[cursor++] = unchecked((byte)' ');

cursor = WriteLong(buffer, cursor, timestamp);
cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested);

buffer[cursor++] = ASCII_LINEFEED;
}
}

buffer[cursor++] = ASCII_LINEFEED;

return cursor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,18 @@ public void Dispose()
}
}

private static bool AcceptsOpenMetrics(HttpListenerRequest request)
{
var acceptHeader = request.Headers["Accept"];

if (string.IsNullOrEmpty(acceptHeader))
{
return false;
}

return PrometheusHeadersParser.AcceptsOpenMetrics(acceptHeader);
}

private void WorkerProc()
{
this.httpListener.Start();
Expand Down Expand Up @@ -148,15 +160,19 @@ private async Task ProcessRequestAsync(HttpListenerContext context)
{
try
{
var collectionResponse = await this.exporter.CollectionManager.EnterCollect().ConfigureAwait(false);
var openMetricsRequested = AcceptsOpenMetrics(context.Request);
var collectionResponse = await this.exporter.CollectionManager.EnterCollect(openMetricsRequested).ConfigureAwait(false);

try
{
context.Response.Headers.Add("Server", string.Empty);
if (collectionResponse.View.Count > 0)
{
context.Response.StatusCode = 200;
context.Response.Headers.Add("Last-Modified", collectionResponse.GeneratedAtUtc.ToString("R"));
context.Response.ContentType = "text/plain; charset=utf-8; version=0.0.4";
context.Response.ContentType = openMetricsRequested
? "application/openmetrics-text; version=1.0.0; charset=utf-8"
: "text/plain; charset=utf-8; version=0.0.4";

await context.Response.OutputStream.WriteAsync(collectionResponse.View.Array, 0, collectionResponse.View.Count).ConfigureAwait(false);
}
Expand Down
Loading

0 comments on commit 2602283

Please sign in to comment.