Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
54 changes: 41 additions & 13 deletions src/OpenTelemetry/Internal/SelfDiagnosticsConfigParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

namespace OpenTelemetry.Internal;

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

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

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

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

private static readonly Regex FormatMessageRegex = new(
@"""FormatMessage""\s*:\s*(?:""(?<FormatMessage>.*?)""|(?<FormatMessage>true|false))", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex LogLevelRegex = new(LogLevelRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);

private static readonly Regex FormatMessageRegex = new(FormatMessageRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
#endif

// This class is called in SelfDiagnosticsConfigRefresher.UpdateMemoryMappedFileFromConfiguration
// in both main thread and the worker thread.
Expand Down Expand Up @@ -128,15 +134,15 @@ internal static bool TryParseLogDirectory(
string configJson,
out string logDirectory)
{
var logDirectoryResult = LogDirectoryRegex.Match(configJson);
var logDirectoryResult = GetLogDirectoryRegex().Match(configJson);
logDirectory = logDirectoryResult.Groups["LogDirectory"].Value;
return logDirectoryResult.Success && !string.IsNullOrWhiteSpace(logDirectory);
}

internal static bool TryParseFileSize(string configJson, out int fileSizeInKB)
{
fileSizeInKB = 0;
var fileSizeResult = FileSizeRegex.Match(configJson);
var fileSizeResult = GetFileSizeRegex().Match(configJson);
return fileSizeResult.Success && int.TryParse(fileSizeResult.Groups["FileSize"].Value, out fileSizeInKB);
}

Expand All @@ -145,15 +151,15 @@ internal static bool TryParseLogLevel(
[NotNullWhen(true)]
out string? logLevel)
{
var logLevelResult = LogLevelRegex.Match(configJson);
var logLevelResult = GetLogLevelRegex().Match(configJson);
logLevel = logLevelResult.Groups["LogLevel"].Value;
return logLevelResult.Success && !string.IsNullOrWhiteSpace(logLevel);
}

internal static bool TryParseFormatMessage(string configJson, out bool formatMessage)
{
formatMessage = false;
var formatMessageResult = FormatMessageRegex.Match(configJson);
var formatMessageResult = GetFormatMessageRegex().Match(configJson);
if (formatMessageResult.Success)
{
var formatMessageValue = formatMessageResult.Groups["FormatMessage"].Value;
Expand All @@ -162,4 +168,26 @@ internal static bool TryParseFormatMessage(string configJson, out bool formatMes

return true;
}

#if NET
[GeneratedRegex(LogDirectoryRegexPattern, RegexOptions.IgnoreCase)]
private static partial Regex GetLogDirectoryRegex();

[GeneratedRegex(FileSizeRegexPattern, RegexOptions.IgnoreCase)]
private static partial Regex GetFileSizeRegex();

[GeneratedRegex(LogLevelRegexPattern, RegexOptions.IgnoreCase)]
private static partial Regex GetLogLevelRegex();

[GeneratedRegex(FormatMessageRegexPattern, RegexOptions.IgnoreCase)]
private static partial Regex GetFormatMessageRegex();
#else
private static Regex GetLogDirectoryRegex() => LogDirectoryRegex;

private static Regex GetFileSizeRegex() => FileSizeRegex;

private static Regex GetLogLevelRegex() => LogLevelRegex;

private static Regex GetFormatMessageRegex() => FormatMessageRegex;
#endif
}
47 changes: 19 additions & 28 deletions src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderSdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ namespace OpenTelemetry.Metrics;
/// <summary>
/// Stores state used to build a <see cref="MeterProvider"/>.
/// </summary>
internal sealed class MeterProviderBuilderSdk : MeterProviderBuilder, IMeterProviderBuilder
internal sealed partial class MeterProviderBuilderSdk : MeterProviderBuilder, IMeterProviderBuilder
{
public const int DefaultMetricLimit = 1000;
public const int DefaultCardinalityLimit = 2000;
private const string DefaultInstrumentationVersion = "1.0.0.0";
private const string InstrumentNameRegexPattern = @"^[a-z][a-z0-9-._/]{0,254}$";

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

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

public ResourceBuilder? ResourceBuilder { get; private set; }

public ExemplarFilterType? ExemplarFilter { get; private set; }

public MeterProvider? Provider => this.meterProvider;

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

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

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

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

Expand All @@ -60,31 +60,17 @@ public MeterProviderBuilderSdk(IServiceProvider serviceProvider)
/// <param name="instrumentName">The instrument name.</param>
/// <returns>Boolean indicating if the instrument is valid.</returns>
public static bool IsValidInstrumentName(string instrumentName)
{
if (string.IsNullOrWhiteSpace(instrumentName))
{
return false;
}

return InstrumentNameRegex.IsMatch(instrumentName);
}
=> !string.IsNullOrWhiteSpace(instrumentName) && InstrumentNameRegex.IsMatch(instrumentName);

/// <summary>
/// Returns whether the given custom view name is valid according to the specification.
/// </summary>
/// <remarks>See specification: <see href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument"/>.</remarks>
/// <param name="customViewName">The view name.</param>
/// <returns>Boolean indicating if the instrument is valid.</returns>
public static bool IsValidViewName(string customViewName)
{
// 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.
if (customViewName == null)
{
return true;
}

return InstrumentNameRegex.IsMatch(customViewName);
}
public static bool IsValidViewName(string customViewName) =>
/* 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. */
customViewName == null || InstrumentNameRegex.IsMatch(customViewName);

public void RegisterProvider(MeterProviderSdk meterProvider)
{
Expand Down Expand Up @@ -208,13 +194,18 @@ public MeterProviderBuilder ConfigureBuilder(Action<IServiceProvider, MeterProvi
}

public MeterProviderBuilder ConfigureServices(Action<IServiceCollection> configure)
{
throw new NotSupportedException("Services cannot be configured after ServiceProvider has been created.");
}
=> throw new NotSupportedException("Services cannot be configured after ServiceProvider has been created.");

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

#if NET
[GeneratedRegex(InstrumentNameRegexPattern, RegexOptions.IgnoreCase)]
private static partial Regex GetInstrumentNameRegex();
#else
private static Regex GetInstrumentNameRegex() => new(InstrumentNameRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
#endif

internal readonly struct InstrumentationRegistration
{
public readonly string Name;
Expand Down
Loading