namespace System.Logging;
using System.Collections.Generic;
using System.Threading;
/// What a factory is configured through (.NET's `ILoggingBuilder`): its
/// providers and its filter rules.
public readonly interface ILoggingBuilder {
readonly void AddProvider(ILoggerProvider provider);
readonly void ClearProviders();
/// Its filter rules, changed under their lock.
Lock<LoggerFilterOptions> Filters { get; }
}
/// A builder gathering what `LoggerFactory.Create` makes its factory of.
public readonly sealed class LoggingBuilder : ILoggingBuilder {
private readonly Lock<List<ILoggerProvider>> _providers = new Lock<List<ILoggerProvider>>(new List<ILoggerProvider>());
public Lock<LoggerFilterOptions> Filters { get; } = new Lock<LoggerFilterOptions>(new LoggerFilterOptions());
public readonly void AddProvider(ILoggerProvider provider) {
lock (_providers, ref let all) {
all.Add(move provider);
}
}
public readonly void ClearProviders() {
lock (_providers, ref let all) {
all.Clear();
}
}
/// The factory it describes.
public LoggerFactory Build() {
let mut providers = new List<ILoggerProvider>();
lock (_providers, ref let all) {
providers.AddRange(all);
}
let mut options = new LoggerFilterOptions();
lock (Filters, ref let o) {
options.MinLevel = o.MinLevel;
options.CaptureScopes = o.CaptureScopes;
options.Rules.AddRange(o.Rules);
}
return new LoggerFactory(move providers, move options);
}
}
/// What a builder is configured with (.NET's `LoggingBuilderExtensions`,
/// `FilterLoggingBuilderExtensions`): each gives the builder back, so they
/// chain (`b.SetMinimumLevel(…).AddFilter(…)`); it's shared, a copy of one.
public static class LoggingBuilderExtensions {
/// Entries below `level` written by no provider, unless a rule says.
public static ILoggingBuilder SetMinimumLevel(this ILoggingBuilder builder, LogLevel level) {
lock (builder.Filters, ref let o) {
o.MinLevel = level;
}
return builder;
}
/// Entries of categories starting with `category` written from `level` up.
public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, string? category, LogLevel level) {
return builder.Rule(null, category, level, null);
}
/// Entries `filter` lets through, by provider, category and level.
public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func<string?, string?, LogLevel, bool> filter) {
return builder.Rule(null, null, null, filter);
}
/// Entries `filter` lets through, by category and level.
public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func<string?, LogLevel, bool> categoryLevelFilter) {
return builder.Rule(null, null, null, (provider, category, level) => categoryLevelFilter(category, level));
}
/// Entries `filter` lets through, by level.
public static ILoggingBuilder AddFilter(this ILoggingBuilder builder, Func<LogLevel, bool> levelFilter) {
return builder.Rule(null, null, null, (provider, category, level) => levelFilter(level));
}
/// Provider `TProvider`'s entries of categories starting with `category` from `level` up.
public static ILoggingBuilder AddFilter<TProvider>(this ILoggingBuilder builder, string? category, LogLevel level) where TProvider : ILoggerProvider {
return builder.Rule(typeof(TProvider).FullName, category, level, null);
}
/// The rule of these added.
private static ILoggingBuilder Rule(this ILoggingBuilder builder, string? provider, string? category, LogLevel? level, Func<string?, string?, LogLevel, bool>? filter) {
lock (builder.Filters, ref let o) {
o.Rules.Add(new LoggerFilterRule(provider, category, level, filter));
}
return builder;
}
}