Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Prometheus UNIT metadata #3651

Merged
merged 3 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
* Bug fix for Prometheus Exporter reporting StatusCode 204
instead of 200, when no metrics are collected
([#3643](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3643))

* Added overloads which accept a name to the `MeterProviderBuilder`
`AddPrometheusExporter` extension to allow for more fine-grained options
management
([#3648](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3648))
* Added support for OpenMetrics UNIT metadata
([#3651](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3651))

## 1.4.0-alpha.2

Expand All @@ -21,7 +22,6 @@ Released 2022-Aug-18
([#3430](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3430)
[#3503](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3503)
[#3507](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3507))

* Added `IEndpointRouteBuilder` extension methods to help with Prometheus
middleware configuration on ASP.NET Core
([#3295](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3295))
Expand All @@ -41,10 +41,8 @@ Released 2022-Apr-15
* Added `IApplicationBuilder` extension methods to help with Prometheus
middleware configuration on ASP.NET Core
([#3029](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3029))

* Changed Prometheus exporter to return 204 No Content and log a warning event
if there are no metrics to collect.

* Removes .NET Framework 4.6.1. The minimum .NET Framework
version supported is .NET 4.6.2. ([#3190](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3190))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
* Bug fix for Prometheus Exporter reporting StatusCode 204
instead of 200, when no metrics are collected
([#3643](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3643))

* Added overloads which accept a name to the `MeterProviderBuilder`
`AddPrometheusHttpListener` extension to allow for more fine-grained options
management
([#3648](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3648))
* Added support for OpenMetrics UNIT metadata
([#3651](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3651))

## 1.4.0-alpha.2

Expand Down Expand Up @@ -41,10 +42,8 @@ Released 2022-Apr-15
* Added `IApplicationBuilder` extension methods to help with Prometheus
middleware configuration on ASP.NET Core
([#3029](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3029))

* Changed Prometheus exporter to return 204 No Content and log a warning event
if there are no metrics to collect.

* Removes .NET Framework 4.6.1. The minimum .NET Framework
version supported is .NET 4.6.2. ([#3190](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3190))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,13 @@ public static int WriteMetricName(byte[] buffer, int cursor, string metricName,
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteHelpText(byte[] buffer, int cursor, string metricName, string metricUnit = null, string metricDescription = null)
public static int WriteHelpMetadata(byte[] buffer, int cursor, string metricName, string metricUnit, string metricDescription)
{
if (string.IsNullOrEmpty(metricDescription))
{
return cursor;
}

cursor = WriteAsciiStringNoEscape(buffer, cursor, "# HELP ");
cursor = WriteMetricName(buffer, cursor, metricName, metricUnit);

Expand All @@ -288,7 +293,7 @@ public static int WriteHelpText(byte[] buffer, int cursor, string metricName, st
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteTypeInfo(byte[] buffer, int cursor, string metricName, string metricUnit, string metricType)
public static int WriteTypeMetadata(byte[] buffer, int cursor, string metricName, string metricUnit, string metricType)
{
Debug.Assert(!string.IsNullOrEmpty(metricType), $"{nameof(metricType)} should not be null or empty.");

Expand All @@ -301,5 +306,39 @@ public static int WriteTypeInfo(byte[] buffer, int cursor, string metricName, st

return cursor;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteUnitMetadata(byte[] buffer, int cursor, string metricName, string metricUnit)
{
if (string.IsNullOrEmpty(metricUnit))
{
return cursor;
}

cursor = WriteAsciiStringNoEscape(buffer, cursor, "# UNIT ");
cursor = WriteMetricName(buffer, cursor, metricName, metricUnit);

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

for (int i = 0; i < metricUnit.Length; i++)
{
var ordinal = (ushort)metricUnit[i];

if ((ordinal >= (ushort)'A' && ordinal <= (ushort)'Z') ||
(ordinal >= (ushort)'a' && ordinal <= (ushort)'z') ||
(ordinal >= (ushort)'0' && ordinal <= (ushort)'9'))
{
buffer[cursor++] = unchecked((byte)ordinal);
}
else
{
buffer[cursor++] = unchecked((byte)'_');
}
}

buffer[cursor++] = ASCII_LINEFEED;

return cursor;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,10 @@ UpDownCounter becomes gauge

public static int WriteMetric(byte[] buffer, int cursor, Metric metric)
{
if (!string.IsNullOrWhiteSpace(metric.Description))
{
cursor = WriteHelpText(buffer, cursor, metric.Name, metric.Unit, metric.Description);
}

int metricType = (int)metric.MetricType >> 4;
cursor = WriteTypeInfo(buffer, cursor, metric.Name, metric.Unit, MetricTypes[metricType]);
cursor = WriteTypeMetadata(buffer, cursor, metric.Name, metric.Unit, MetricTypes[metricType]);
cursor = WriteUnitMetadata(buffer, cursor, metric.Name, metric.Unit);
cursor = WriteHelpMetadata(buffer, cursor, metric.Name, metric.Unit, metric.Description);

if (!metric.MetricType.IsHistogram())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public void GaugeZeroDimensionWithDescription()
var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]);
Assert.Matches(
("^"
+ "# HELP test_gauge Hello, world!\n"
+ "# TYPE test_gauge gauge\n"
+ "# HELP test_gauge Hello, world!\n"
+ "test_gauge 123 \\d+\n"
+ "$").Replace('\'', '"'),
Encoding.UTF8.GetString(buffer, 0, cursor));
Expand All @@ -96,6 +96,34 @@ public void GaugeZeroDimensionWithUnit()
Assert.Matches(
("^"
+ "# TYPE test_gauge_seconds gauge\n"
+ "# UNIT test_gauge_seconds seconds\n"
+ "test_gauge_seconds 123 \\d+\n"
+ "$").Replace('\'', '"'),
Encoding.UTF8.GetString(buffer, 0, cursor));
}

[Fact]
public void GaugeZeroDimensionWithDescriptionAndUnit()
{
var buffer = new byte[85000];
var metrics = new List<Metric>();

using var meter = new Meter(Utils.GetCurrentMethodName());
using var provider = Sdk.CreateMeterProviderBuilder()
.AddMeter(meter.Name)
.AddInMemoryExporter(metrics)
.Build();

meter.CreateObservableGauge("test_gauge", () => 123, unit: "seconds", description: "Hello, world!");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice description :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs some clever Greek mythology thing. Defer to @alanwest to come up with it 🤣

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, would "Hello, Prometheus!" not have been clever enough? And also what the heck do I know about Greek mythology?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we consider the Prometheus Exporters to be renamed to OpenMetrics Exporters? 😆


provider.ForceFlush();

var cursor = PrometheusSerializer.WriteMetric(buffer, 0, metrics[0]);
Assert.Matches(
("^"
+ "# TYPE test_gauge_seconds gauge\n"
+ "# UNIT test_gauge_seconds seconds\n"
+ "# HELP test_gauge_seconds Hello, world!\n"
+ "test_gauge_seconds 123 \\d+\n"
+ "$").Replace('\'', '"'),
Encoding.UTF8.GetString(buffer, 0, cursor));
Expand Down