Skip to content

Commit c9ac6c5

Browse files
[OpenTelemetry] Use Regex source generator
Use `[GeneratedRegex]`, where supported, for statically-known regular expressions. See open-telemetry#6849. Co-Authored-By: Mihail Golubev <[email protected]>
1 parent 496e4ac commit c9ac6c5

File tree

2 files changed

+60
-41
lines changed

2 files changed

+60
-41
lines changed

src/OpenTelemetry/Internal/SelfDiagnosticsConfigParser.cs

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88

99
namespace OpenTelemetry.Internal;
1010

11-
internal sealed class SelfDiagnosticsConfigParser
11+
/// <summary>
12+
/// Parses the self-diagnostics configuration file.
13+
/// </summary>
14+
internal sealed partial class SelfDiagnosticsConfigParser
1215
{
1316
public const string ConfigFileName = "OTEL_DIAGNOSTICS.json";
1417
private const int FileSizeLowerLimit = 1024; // Lower limit for log file size in KB: 1MB
@@ -19,17 +22,20 @@ internal sealed class SelfDiagnosticsConfigParser
1922
/// </summary>
2023
private const int ConfigBufferSize = 4 * 1024;
2124

22-
private static readonly Regex LogDirectoryRegex = new(
23-
@"""LogDirectory""\s*:\s*""(?<LogDirectory>.*?)""", RegexOptions.IgnoreCase | RegexOptions.Compiled);
25+
private const string LogDirectoryRegexPattern = @"""LogDirectory""\s*:\s*""(?<LogDirectory>.*?)""";
26+
private const string FileSizeRegexPattern = @"""FileSize""\s*:\s*(?<FileSize>\d+)";
27+
private const string LogLevelRegexPattern = @"""LogLevel""\s*:\s*""(?<LogLevel>.*?)""";
28+
private const string FormatMessageRegexPattern = @"""FormatMessage""\s*:\s*(?:""(?<FormatMessage>.*?)""|(?<FormatMessage>true|false))";
2429

25-
private static readonly Regex FileSizeRegex = new(
26-
@"""FileSize""\s*:\s*(?<FileSize>\d+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
30+
#if !NET
31+
private static readonly Regex LogDirectoryRegex = new(LogDirectoryRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
2732

28-
private static readonly Regex LogLevelRegex = new(
29-
@"""LogLevel""\s*:\s*""(?<LogLevel>.*?)""", RegexOptions.IgnoreCase | RegexOptions.Compiled);
33+
private static readonly Regex FileSizeRegex = new(FileSizeRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
3034

31-
private static readonly Regex FormatMessageRegex = new(
32-
@"""FormatMessage""\s*:\s*(?:""(?<FormatMessage>.*?)""|(?<FormatMessage>true|false))", RegexOptions.IgnoreCase | RegexOptions.Compiled);
35+
private static readonly Regex LogLevelRegex = new(LogLevelRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
36+
37+
private static readonly Regex FormatMessageRegex = new(FormatMessageRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
38+
#endif
3339

3440
// This class is called in SelfDiagnosticsConfigRefresher.UpdateMemoryMappedFileFromConfiguration
3541
// in both main thread and the worker thread.
@@ -128,15 +134,15 @@ internal static bool TryParseLogDirectory(
128134
string configJson,
129135
out string logDirectory)
130136
{
131-
var logDirectoryResult = LogDirectoryRegex.Match(configJson);
137+
var logDirectoryResult = GetLogDirectoryRegex().Match(configJson);
132138
logDirectory = logDirectoryResult.Groups["LogDirectory"].Value;
133139
return logDirectoryResult.Success && !string.IsNullOrWhiteSpace(logDirectory);
134140
}
135141

136142
internal static bool TryParseFileSize(string configJson, out int fileSizeInKB)
137143
{
138144
fileSizeInKB = 0;
139-
var fileSizeResult = FileSizeRegex.Match(configJson);
145+
var fileSizeResult = GetFileSizeRegex().Match(configJson);
140146
return fileSizeResult.Success && int.TryParse(fileSizeResult.Groups["FileSize"].Value, out fileSizeInKB);
141147
}
142148

@@ -145,15 +151,15 @@ internal static bool TryParseLogLevel(
145151
[NotNullWhen(true)]
146152
out string? logLevel)
147153
{
148-
var logLevelResult = LogLevelRegex.Match(configJson);
154+
var logLevelResult = GetLogLevelRegex().Match(configJson);
149155
logLevel = logLevelResult.Groups["LogLevel"].Value;
150156
return logLevelResult.Success && !string.IsNullOrWhiteSpace(logLevel);
151157
}
152158

153159
internal static bool TryParseFormatMessage(string configJson, out bool formatMessage)
154160
{
155161
formatMessage = false;
156-
var formatMessageResult = FormatMessageRegex.Match(configJson);
162+
var formatMessageResult = GetFormatMessageRegex().Match(configJson);
157163
if (formatMessageResult.Success)
158164
{
159165
var formatMessageValue = formatMessageResult.Groups["FormatMessage"].Value;
@@ -162,4 +168,26 @@ internal static bool TryParseFormatMessage(string configJson, out bool formatMes
162168

163169
return true;
164170
}
171+
172+
#if NET
173+
[GeneratedRegex(LogDirectoryRegexPattern, RegexOptions.IgnoreCase)]
174+
private static partial Regex GetLogDirectoryRegex();
175+
176+
[GeneratedRegex(FileSizeRegexPattern, RegexOptions.IgnoreCase)]
177+
private static partial Regex GetFileSizeRegex();
178+
179+
[GeneratedRegex(LogLevelRegexPattern, RegexOptions.IgnoreCase)]
180+
private static partial Regex GetLogLevelRegex();
181+
182+
[GeneratedRegex(FormatMessageRegexPattern, RegexOptions.IgnoreCase)]
183+
private static partial Regex GetFormatMessageRegex();
184+
#else
185+
private static Regex GetLogDirectoryRegex() => LogDirectoryRegex;
186+
187+
private static Regex GetFileSizeRegex() => FileSizeRegex;
188+
189+
private static Regex GetLogLevelRegex() => LogLevelRegex;
190+
191+
private static Regex GetFormatMessageRegex() => FormatMessageRegex;
192+
#endif
165193
}

src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderSdk.cs

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ namespace OpenTelemetry.Metrics;
1313
/// <summary>
1414
/// Stores state used to build a <see cref="MeterProvider"/>.
1515
/// </summary>
16-
internal sealed class MeterProviderBuilderSdk : MeterProviderBuilder, IMeterProviderBuilder
16+
internal sealed partial class MeterProviderBuilderSdk : MeterProviderBuilder, IMeterProviderBuilder
1717
{
1818
public const int DefaultMetricLimit = 1000;
1919
public const int DefaultCardinalityLimit = 2000;
2020
private const string DefaultInstrumentationVersion = "1.0.0.0";
21+
private const string InstrumentNameRegexPattern = @"^[a-z][a-z0-9-._/]{0,254}$";
2122

2223
private readonly IServiceProvider serviceProvider;
2324
private MeterProviderSdk? meterProvider;
@@ -32,22 +33,21 @@ public MeterProviderBuilderSdk(IServiceProvider serviceProvider)
3233
// fields. See: https://github.com/dotnet/runtime/issues/11571.
3334
// Customers: This is not guaranteed to work forever. We may change this
3435
// mechanism in the future do this at your own risk.
35-
public static Regex InstrumentNameRegex { get; set; } = new(
36-
@"^[a-z][a-z0-9-._/]{0,254}$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
36+
public static Regex InstrumentNameRegex { get; set; } = GetInstrumentNameRegex();
3737

38-
public List<InstrumentationRegistration> Instrumentation { get; } = new();
38+
public List<InstrumentationRegistration> Instrumentation { get; } = [];
3939

4040
public ResourceBuilder? ResourceBuilder { get; private set; }
4141

4242
public ExemplarFilterType? ExemplarFilter { get; private set; }
4343

4444
public MeterProvider? Provider => this.meterProvider;
4545

46-
public List<MetricReader> Readers { get; } = new();
46+
public List<MetricReader> Readers { get; } = [];
4747

48-
public List<string> MeterSources { get; } = new();
48+
public List<string> MeterSources { get; } = [];
4949

50-
public List<Func<Instrument, MetricStreamConfiguration?>> ViewConfigs { get; } = new();
50+
public List<Func<Instrument, MetricStreamConfiguration?>> ViewConfigs { get; } = [];
5151

5252
public int MetricLimit { get; private set; } = DefaultMetricLimit;
5353

@@ -60,31 +60,17 @@ public MeterProviderBuilderSdk(IServiceProvider serviceProvider)
6060
/// <param name="instrumentName">The instrument name.</param>
6161
/// <returns>Boolean indicating if the instrument is valid.</returns>
6262
public static bool IsValidInstrumentName(string instrumentName)
63-
{
64-
if (string.IsNullOrWhiteSpace(instrumentName))
65-
{
66-
return false;
67-
}
68-
69-
return InstrumentNameRegex.IsMatch(instrumentName);
70-
}
63+
=> !string.IsNullOrWhiteSpace(instrumentName) && InstrumentNameRegex.IsMatch(instrumentName);
7164

7265
/// <summary>
7366
/// Returns whether the given custom view name is valid according to the specification.
7467
/// </summary>
7568
/// <remarks>See specification: <see href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument"/>.</remarks>
7669
/// <param name="customViewName">The view name.</param>
7770
/// <returns>Boolean indicating if the instrument is valid.</returns>
78-
public static bool IsValidViewName(string customViewName)
79-
{
80-
// Only validate the view name in case it's not null. In case it's null, the view name will be the instrument name as per the spec.
81-
if (customViewName == null)
82-
{
83-
return true;
84-
}
85-
86-
return InstrumentNameRegex.IsMatch(customViewName);
87-
}
71+
public static bool IsValidViewName(string customViewName) =>
72+
/* Only validate the view name in case it's not null. In case it's null, the view name will be the instrument name as per the spec. */
73+
customViewName == null || InstrumentNameRegex.IsMatch(customViewName);
8874

8975
public void RegisterProvider(MeterProviderSdk meterProvider)
9076
{
@@ -208,13 +194,18 @@ public MeterProviderBuilder ConfigureBuilder(Action<IServiceProvider, MeterProvi
208194
}
209195

210196
public MeterProviderBuilder ConfigureServices(Action<IServiceCollection> configure)
211-
{
212-
throw new NotSupportedException("Services cannot be configured after ServiceProvider has been created.");
213-
}
197+
=> throw new NotSupportedException("Services cannot be configured after ServiceProvider has been created.");
214198

215199
MeterProviderBuilder IDeferredMeterProviderBuilder.Configure(Action<IServiceProvider, MeterProviderBuilder> configure)
216200
=> this.ConfigureBuilder(configure);
217201

202+
#if NET
203+
[GeneratedRegex(InstrumentNameRegexPattern, RegexOptions.IgnoreCase)]
204+
private static partial Regex GetInstrumentNameRegex();
205+
#else
206+
private static Regex GetInstrumentNameRegex() => new(InstrumentNameRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
207+
#endif
208+
218209
internal readonly struct InstrumentationRegistration
219210
{
220211
public readonly string Name;

0 commit comments

Comments
 (0)