From 3c17a165f2858b17ad6e8bce296bc52855e5c3af Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 27 Feb 2023 16:17:35 -0800 Subject: [PATCH] [Geneva.Metrics] Add option to disable name validation (#1006) Co-authored-by: Cijo Thomas --- .../CHANGELOG.md | 4 ++ .../Internal/ConnectionStringBuilder.cs | 14 +++++ .../Metrics/GenevaMetricExporter.cs | 23 ++++++++ .../GenevaMetricExporterTests.cs | 56 +++++++++++++++++++ 4 files changed, 97 insertions(+) diff --git a/src/OpenTelemetry.Exporter.Geneva/CHANGELOG.md b/src/OpenTelemetry.Exporter.Geneva/CHANGELOG.md index 770cf13c49..11c85b8402 100644 --- a/src/OpenTelemetry.Exporter.Geneva/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Geneva/CHANGELOG.md @@ -5,6 +5,10 @@ * Update OpenTelemetry to 1.4.0 ([#1038](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1038)) +* Add `DisableMetricNameValidation` connection string flag for controlling + metric name validation performed by the OpenTelemetry SDK. + ([#1006](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1006)) + ## 1.4.0-rc.4 Released 2023-Feb-13 diff --git a/src/OpenTelemetry.Exporter.Geneva/Internal/ConnectionStringBuilder.cs b/src/OpenTelemetry.Exporter.Geneva/Internal/ConnectionStringBuilder.cs index 4ea5dee0b7..d34d91f337 100644 --- a/src/OpenTelemetry.Exporter.Geneva/Internal/ConnectionStringBuilder.cs +++ b/src/OpenTelemetry.Exporter.Geneva/Internal/ConnectionStringBuilder.cs @@ -230,6 +230,20 @@ public string Namespace set => this._parts[nameof(this.Namespace)] = value; } + public bool DisableMetricNameValidation + { + get + { + if (!this._parts.TryGetValue(nameof(this.DisableMetricNameValidation), out var value)) + { + return false; + } + + return string.Equals(bool.TrueString, value, StringComparison.OrdinalIgnoreCase); + } + set => this._parts[nameof(this.DisableMetricNameValidation)] = value ? bool.TrueString : bool.FalseString; + } + private T ThrowIfNotExists(string name) { if (!this._parts.TryGetValue(name, out var value)) diff --git a/src/OpenTelemetry.Exporter.Geneva/Metrics/GenevaMetricExporter.cs b/src/OpenTelemetry.Exporter.Geneva/Metrics/GenevaMetricExporter.cs index 97b6b366b8..485982c91c 100644 --- a/src/OpenTelemetry.Exporter.Geneva/Metrics/GenevaMetricExporter.cs +++ b/src/OpenTelemetry.Exporter.Geneva/Metrics/GenevaMetricExporter.cs @@ -17,9 +17,11 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using System.Text.RegularExpressions; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; @@ -108,6 +110,11 @@ public GenevaMetricExporter(GenevaMetricExporterOptions options) { this.fixedPayloadStartIndex = sizeof(BinaryHeader); } + + if (connectionStringBuilder.DisableMetricNameValidation) + { + DisableOpenTelemetrySdkMetricNameValidation(); + } } public override ExportResult Export(in Batch batch) @@ -258,6 +265,22 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } + internal static PropertyInfo GetOpenTelemetryInstrumentNameRegexProperty() + { + var meterProviderBuilderSdkType = typeof(Sdk).Assembly.GetType("OpenTelemetry.Metrics.MeterProviderBuilderSdk", throwOnError: false) + ?? throw new InvalidOperationException("OpenTelemetry.Metrics.MeterProviderBuilderSdk type could not be found reflectively."); + + var instrumentNameRegexProperty = meterProviderBuilderSdkType.GetProperty("InstrumentNameRegex", BindingFlags.Public | BindingFlags.Static) + ?? throw new InvalidOperationException("OpenTelemetry.Metrics.MeterProviderBuilderSdk.InstrumentNameRegex property could not be found reflectively."); + + return instrumentNameRegexProperty; + } + + internal static void DisableOpenTelemetrySdkMetricNameValidation() + { + GetOpenTelemetryInstrumentNameRegexProperty().SetValue(null, new Regex(".*", RegexOptions.Compiled)); + } + internal unsafe ushort SerializeMetric( MetricEventType eventType, string metricName, diff --git a/test/OpenTelemetry.Exporter.Geneva.Tests/GenevaMetricExporterTests.cs b/test/OpenTelemetry.Exporter.Geneva.Tests/GenevaMetricExporterTests.cs index 6ffbe93a5c..344aae91c6 100644 --- a/test/OpenTelemetry.Exporter.Geneva.Tests/GenevaMetricExporterTests.cs +++ b/test/OpenTelemetry.Exporter.Geneva.Tests/GenevaMetricExporterTests.cs @@ -625,6 +625,62 @@ public void SuccessfulExportOnLinux() } } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void DisableMetricNameValidationTest(bool disableMetricNameValidation) + { + var instrumentNameRegexProperty = GenevaMetricExporter.GetOpenTelemetryInstrumentNameRegexProperty(); + var initialInstrumentNameRegexValue = instrumentNameRegexProperty.GetValue(null); + Socket server = null; + try + { + var exportedMetrics = new List(); + + using var meter = new Meter(Guid.NewGuid().ToString()); + + using (var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddGenevaMetricExporter(options => + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + options.ConnectionString = $"Account=OTelMonitoringAccount;Namespace=OTelMetricNamespace;DisableMetricNameValidation={disableMetricNameValidation}"; + } + else + { + var path = GenerateTempFilePath(); + options.ConnectionString = $"Endpoint=unix:{path};Account=OTelMonitoringAccount;Namespace=OTelMetricNamespace;DisableMetricNameValidation={disableMetricNameValidation}"; + + var endpoint = new UnixDomainSocketEndPoint(path); + server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP); + server.Bind(endpoint); + server.Listen(1); + } + }) + .AddInMemoryExporter(exportedMetrics) + .Build()) + { + var counter = meter.CreateCounter("count/invalid"); + counter.Add(1); + } + + if (disableMetricNameValidation) + { + Assert.Single(exportedMetrics); + } + else + { + Assert.Empty(exportedMetrics); + } + } + finally + { + instrumentNameRegexProperty.SetValue(null, initialInstrumentNameRegexValue); + server?.Dispose(); + } + } + private static string GenerateTempFilePath() { while (true)