• netcore3.0 Logging 日志系统(一)


    nuget包:以Microsoft.Extensins.Logging开头的nuget包

    Github地址:https://github.com/dotnet/extensions/tree/master/src/Logging

    Logging的依赖注入:

    public static class LoggingServiceCollectionExtensions
        {
            /// <summary>
            /// Adds logging services to the specified <see cref="IServiceCollection" />.
            /// </summary>
            /// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            public static IServiceCollection AddLogging(this IServiceCollection services)
            {
                return AddLogging(services, builder => { });
            }
    
            /// <summary>
            /// Adds logging services to the specified <see cref="IServiceCollection" />.
            /// </summary>
            /// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
            /// <param name="configure">The <see cref="ILoggingBuilder"/> configuration delegate.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure)
            {
                if (services == null)
                {
                    throw new ArgumentNullException(nameof(services));
                }
    
                services.AddOptions();
    
                services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
                services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
    
                services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<LoggerFilterOptions>>(
                    new DefaultLoggerLevelConfigureOptions(LogLevel.Information)));
    
                configure(new LoggingBuilder(services));
                return services;
            }
        }

    看下Logger<>的实现类Logger<>

    /// <summary>
        /// Delegates to a new <see cref="ILogger"/> instance using the full name of the given type, created by the
        /// provided <see cref="ILoggerFactory"/>.
        /// </summary>
        /// <typeparam name="T">The type.</typeparam>
        public class Logger<T> : ILogger<T>
        {
            private readonly ILogger _logger;
    
            /// <summary>
            /// Creates a new <see cref="Logger{T}"/>.
            /// </summary>
            /// <param name="factory">The factory.</param>
            public Logger(ILoggerFactory factory)
            {
                if (factory == null)
                {
                    throw new ArgumentNullException(nameof(factory));
                }
    
                _logger = factory.CreateLogger(TypeNameHelper.GetTypeDisplayName(typeof(T), includeGenericParameters: false, nestedTypeDelimiter: '.'));
            }
    
            /// <inheritdoc />
            IDisposable ILogger.BeginScope<TState>(TState state)
            {
                return _logger.BeginScope(state);
            }
    
            /// <inheritdoc />
            bool ILogger.IsEnabled(LogLevel logLevel)
            {
                return _logger.IsEnabled(logLevel);
            }
    
            /// <inheritdoc />
            void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                _logger.Log(logLevel, eventId, state, exception, formatter);
            }
        }

    其内部调用ILoggerFactory 来创建对应的ILogger对象

    /// <summary>
        /// Produces instances of <see cref="ILogger"/> classes based on the given providers.
        /// </summary>
        public class LoggerFactory : ILoggerFactory
        {
            private static readonly LoggerRuleSelector RuleSelector = new LoggerRuleSelector();
    
            private readonly Dictionary<string, Logger> _loggers = new Dictionary<string, Logger>(StringComparer.Ordinal);
            private readonly List<ProviderRegistration> _providerRegistrations = new List<ProviderRegistration>();
            private readonly object _sync = new object();
            private volatile bool _disposed;
            private IDisposable _changeTokenRegistration;
            private LoggerFilterOptions _filterOptions;
            private LoggerExternalScopeProvider _scopeProvider;
    
            /// <summary>
            /// Creates a new <see cref="LoggerFactory"/> instance.
            /// </summary>
            public LoggerFactory() : this(Enumerable.Empty<ILoggerProvider>())
            {
            }
    
            /// <summary>
            /// Creates a new <see cref="LoggerFactory"/> instance.
            /// </summary>
            /// <param name="providers">The providers to use in producing <see cref="ILogger"/> instances.</param>
            public LoggerFactory(IEnumerable<ILoggerProvider> providers)
                : this(providers, new StaticFilterOptionsMonitor(new LoggerFilterOptions()))
            {
            }
    
            /// <summary>
            /// Creates a new <see cref="LoggerFactory"/> instance.
            /// </summary>
            /// <param name="providers">The providers to use in producing <see cref="ILogger"/> instances.</param>
            /// <param name="filterOptions">The filter options to use.</param>
            public LoggerFactory(IEnumerable<ILoggerProvider> providers, LoggerFilterOptions filterOptions)
                : this(providers, new StaticFilterOptionsMonitor(filterOptions))
            {
            }
    
            /// <summary>
            /// Creates a new <see cref="LoggerFactory"/> instance.
            /// </summary>
            /// <param name="providers">The providers to use in producing <see cref="ILogger"/> instances.</param>
            /// <param name="filterOption">The filter option to use.</param>
            public LoggerFactory(IEnumerable<ILoggerProvider> providers, IOptionsMonitor<LoggerFilterOptions> filterOption)
            {
                foreach (var provider in providers)
                {
                    AddProviderRegistration(provider, dispose: false);
                }
    
                _changeTokenRegistration = filterOption.OnChange(RefreshFilters);
                RefreshFilters(filterOption.CurrentValue);
            }
    
            /// <summary>
            /// Creates new instance of <see cref="ILoggerFactory"/> configured using provided <paramref name="configure"/> delegate.
            /// </summary>
            /// <param name="configure">A delegate to configure the <see cref="ILoggingBuilder"/>.</param>
            /// <returns>The <see cref="ILoggerFactory"/> that was created.</returns>
            public static ILoggerFactory Create(Action<ILoggingBuilder> configure)
            {
                var serviceCollection = new ServiceCollection();
                serviceCollection.AddLogging(configure);
                var serviceProvider = serviceCollection.BuildServiceProvider();
                var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
                return new DisposingLoggerFactory(loggerFactory, serviceProvider);
            }
    
            private void RefreshFilters(LoggerFilterOptions filterOptions)
            {
                lock (_sync)
                {
                    _filterOptions = filterOptions;
                    foreach (var registeredLogger in _loggers)
                    {
                        var logger = registeredLogger.Value;
                        (logger.MessageLoggers, logger.ScopeLoggers) = ApplyFilters(logger.Loggers);
                    }
                }
            }
    
            /// <summary>
            /// Creates an <see cref="ILogger"/> with the given <paramref name="categoryName"/>.
            /// </summary>
            /// <param name="categoryName">The category name for messages produced by the logger.</param>
            /// <returns>The <see cref="ILogger"/> that was created.</returns>
            public ILogger CreateLogger(string categoryName)
            {
                if (CheckDisposed())
                {
                    throw new ObjectDisposedException(nameof(LoggerFactory));
                }
    
                lock (_sync)
                {
                    if (!_loggers.TryGetValue(categoryName, out var logger))
                    {
                        logger = new Logger
                        {
                            Loggers = CreateLoggers(categoryName),
                        };
    
                        (logger.MessageLoggers, logger.ScopeLoggers) = ApplyFilters(logger.Loggers);
    
                        _loggers[categoryName] = logger;
                    }
    
                    return logger;
                }
            }
    
            /// <summary>
            /// Adds the given provider to those used in creating <see cref="ILogger"/> instances.
            /// </summary>
            /// <param name="provider">The <see cref="ILoggerProvider"/> to add.</param>
            public void AddProvider(ILoggerProvider provider)
            {
                if (CheckDisposed())
                {
                    throw new ObjectDisposedException(nameof(LoggerFactory));
                }
    
                lock (_sync)
                {
                    AddProviderRegistration(provider, dispose: true);
    
                    foreach (var existingLogger in _loggers)
                    {
                        var logger = existingLogger.Value;
                        var loggerInformation = logger.Loggers;
    
                        var newLoggerIndex = loggerInformation.Length;
                        Array.Resize(ref loggerInformation, loggerInformation.Length + 1);
                        loggerInformation[newLoggerIndex] = new LoggerInformation(provider, existingLogger.Key);
    
                        logger.Loggers = loggerInformation;
                        (logger.MessageLoggers, logger.ScopeLoggers) = ApplyFilters(logger.Loggers);
                    }
                }
            }
    
            private void AddProviderRegistration(ILoggerProvider provider, bool dispose)
            {
                _providerRegistrations.Add(new ProviderRegistration
                {
                    Provider = provider,
                    ShouldDispose = dispose
                });
    
                if (provider is ISupportExternalScope supportsExternalScope)
                {
                    if (_scopeProvider == null)
                    {
                        _scopeProvider = new LoggerExternalScopeProvider();
                    }
    
                    supportsExternalScope.SetScopeProvider(_scopeProvider);
                }
            }
    
            private LoggerInformation[] CreateLoggers(string categoryName)
            {
                var loggers = new LoggerInformation[_providerRegistrations.Count];
                for (var i = 0; i < _providerRegistrations.Count; i++)
                {
                    loggers[i] = new LoggerInformation(_providerRegistrations[i].Provider, categoryName);
                }
                return loggers;
            }
    
            private (MessageLogger[] MessageLoggers, ScopeLogger[] ScopeLoggers) ApplyFilters(LoggerInformation[] loggers)
            {
                var messageLoggers = new List<MessageLogger>();
                var scopeLoggers = _filterOptions.CaptureScopes ? new List<ScopeLogger>() : null;
    
                foreach (var loggerInformation in loggers)
                {
                    RuleSelector.Select(_filterOptions,
                        loggerInformation.ProviderType,
                        loggerInformation.Category,
                        out var minLevel,
                        out var filter);
    
                    if (minLevel != null && minLevel > LogLevel.Critical)
                    {
                        continue;
                    }
    
                    messageLoggers.Add(new MessageLogger(loggerInformation.Logger, loggerInformation.Category, loggerInformation.ProviderType.FullName, minLevel, filter));
    
                    if (!loggerInformation.ExternalScope)
                    {
                        scopeLoggers?.Add(new ScopeLogger(logger: loggerInformation.Logger, externalScopeProvider: null));
                    }
                }
    
                if (_scopeProvider != null)
                {
                    scopeLoggers?.Add(new ScopeLogger(logger: null, externalScopeProvider: _scopeProvider));
                }
    
                return (messageLoggers.ToArray(), scopeLoggers?.ToArray());
            }
    
            /// <summary>
            /// Check if the factory has been disposed.
            /// </summary>
            /// <returns>True when <see cref="Dispose()"/> as been called</returns>
            protected virtual bool CheckDisposed() => _disposed;
    
            /// <inheritdoc/>
            public void Dispose()
            {
                if (!_disposed)
                {
                    _disposed = true;
    
                    _changeTokenRegistration?.Dispose();
    
                    foreach (var registration in _providerRegistrations)
                    {
                        try
                        {
                            if (registration.ShouldDispose)
                            {
                                registration.Provider.Dispose();
                            }
                        }
                        catch
                        {
                            // Swallow exceptions on dispose
                        }
                    }
                }
            }
    
            private struct ProviderRegistration
            {
                public ILoggerProvider Provider;
                public bool ShouldDispose;
            }
    
            private class DisposingLoggerFactory : ILoggerFactory
            {
                private readonly ILoggerFactory _loggerFactory;
    
                private readonly ServiceProvider _serviceProvider;
    
                public DisposingLoggerFactory(ILoggerFactory loggerFactory, ServiceProvider serviceProvider)
                {
                    _loggerFactory = loggerFactory;
                    _serviceProvider = serviceProvider;
                }
    
                public void Dispose()
                {
                    _serviceProvider.Dispose();
                }
    
                public ILogger CreateLogger(string categoryName)
                {
                    return _loggerFactory.CreateLogger(categoryName);
                }
    
                public void AddProvider(ILoggerProvider provider)
                {
                    _loggerFactory.AddProvider(provider);
                }
            }
        }

    LoggerFactory构造函数接收两个参数IEnumerable<ILoggerProvider> providers, IOptionsMonitor<LoggerFilterOptions> filterOption

    当依赖注入系统解析LoggerFactory时,会从容器中获取实现了ILoggerProvider的所有实例

    public ILogger CreateLogger(string categoryName)
            {
                if (CheckDisposed())
                {
                    throw new ObjectDisposedException(nameof(LoggerFactory));
                }
    
                lock (_sync)
                {
                    if (!_loggers.TryGetValue(categoryName, out var logger))
                    {
                        logger = new Logger
                        {
                            Loggers = CreateLoggers(categoryName),
                        };
    
                        (logger.MessageLoggers, logger.ScopeLoggers) = ApplyFilters(logger.Loggers);
    
                        _loggers[categoryName] = logger;
                    }
    
                    return logger;
                }
            }

    该方法根据categoryName参数获取对应的Logger

    private LoggerInformation[] CreateLoggers(string categoryName)
            {
                var loggers = new LoggerInformation[_providerRegistrations.Count];
                for (var i = 0; i < _providerRegistrations.Count; i++)
                {
                    loggers[i] = new LoggerInformation(_providerRegistrations[i].Provider, categoryName);
                }
                return loggers;
            }
    private (MessageLogger[] MessageLoggers, ScopeLogger[] ScopeLoggers) ApplyFilters(LoggerInformation[] loggers)
            {
                var messageLoggers = new List<MessageLogger>();
                var scopeLoggers = _filterOptions.CaptureScopes ? new List<ScopeLogger>() : null;
    
                foreach (var loggerInformation in loggers)
                {
                    RuleSelector.Select(_filterOptions,
                        loggerInformation.ProviderType,
                        loggerInformation.Category,
                        out var minLevel,
                        out var filter);
    
                    if (minLevel != null && minLevel > LogLevel.Critical)
                    {
                        continue;
                    }
    
                    messageLoggers.Add(new MessageLogger(loggerInformation.Logger, loggerInformation.Category, loggerInformation.ProviderType.FullName, minLevel, filter));
    
                    if (!loggerInformation.ExternalScope)
                    {
                        scopeLoggers?.Add(new ScopeLogger(logger: loggerInformation.Logger, externalScopeProvider: null));
                    }
                }
    
                if (_scopeProvider != null)
                {
                    scopeLoggers?.Add(new ScopeLogger(logger: null, externalScopeProvider: _scopeProvider));
                }
    
                return (messageLoggers.ToArray(), scopeLoggers?.ToArray());
            }

    根据一些规则过滤

    再来看下Logger类:

    internal class Logger : ILogger
        {
            public LoggerInformation[] Loggers { get; set; }
            public MessageLogger[] MessageLoggers { get; set; }
            public ScopeLogger[] ScopeLoggers { get; set; }
    
            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                var loggers = MessageLoggers;
                if (loggers == null)
                {
                    return;
                }
    
                List<Exception> exceptions = null;
                for (var i = 0; i < loggers.Length; i++)
                {
                    ref readonly var loggerInfo = ref loggers[i];
                    if (!loggerInfo.IsEnabled(logLevel))
                    {
                        continue;
                    }
    
                    LoggerLog(logLevel, eventId, loggerInfo.Logger, exception, formatter, ref exceptions, state);
                }
    
                if (exceptions != null && exceptions.Count > 0)
                {
                    ThrowLoggingError(exceptions);
                }
    
                static void LoggerLog(LogLevel logLevel, EventId eventId, ILogger logger, Exception exception, Func<TState, Exception, string> formatter, ref List<Exception> exceptions, in TState state)
                {
                    try
                    {
                        logger.Log(logLevel, eventId, state, exception, formatter);
                    }
                    catch (Exception ex)
                    {
                        if (exceptions == null)
                        {
                            exceptions = new List<Exception>();
                        }
    
                        exceptions.Add(ex);
                    }
                }
            }
    
            public bool IsEnabled(LogLevel logLevel)
            {
                var loggers = MessageLoggers;
                if (loggers == null)
                {
                    return false;
                }
    
                List<Exception> exceptions = null;
                var i = 0;
                for (; i < loggers.Length; i++)
                {
                    ref readonly var loggerInfo = ref loggers[i];
                    if (!loggerInfo.IsEnabled(logLevel))
                    {
                        continue;
                    }
    
                    if (LoggerIsEnabled(logLevel, loggerInfo.Logger, ref exceptions))
                    {
                        break;
                    }
                }
    
                if (exceptions != null && exceptions.Count > 0)
                {
                    ThrowLoggingError(exceptions);
                }
    
                return i < loggers.Length ? true : false;
    
                static bool LoggerIsEnabled(LogLevel logLevel, ILogger logger, ref List<Exception> exceptions)
                {
                    try
                    {
                        if (logger.IsEnabled(logLevel))
                        {
                            return true;
                        }
                    }
                    catch (Exception ex)
                    {
                        if (exceptions == null)
                        {
                            exceptions = new List<Exception>();
                        }
    
                        exceptions.Add(ex);
                    }
    
                    return false;
                }
            }
    
            public IDisposable BeginScope<TState>(TState state)
            {
                var loggers = ScopeLoggers;
    
                if (loggers == null)
                {
                    return NullScope.Instance;
                }
    
                if (loggers.Length == 1)
                {
                    return loggers[0].CreateScope(state);
                }
    
                var scope = new Scope(loggers.Length);
                List<Exception> exceptions = null;
                for (var i = 0; i < loggers.Length; i++)
                {
                    ref readonly var scopeLogger = ref loggers[i];
    
                    try
                    {
                        scope.SetDisposable(i, scopeLogger.CreateScope(state));
                    }
                    catch (Exception ex)
                    {
                        if (exceptions == null)
                        {
                            exceptions = new List<Exception>();
                        }
    
                        exceptions.Add(ex);
                    }
                }
    
                if (exceptions != null && exceptions.Count > 0)
                {
                    ThrowLoggingError(exceptions);
                }
    
                return scope;
            }
    
            private static void ThrowLoggingError(List<Exception> exceptions)
            {
                throw new AggregateException(
                    message: "An error occurred while writing to logger(s).", innerExceptions: exceptions);
            }
    
            private class Scope : IDisposable
            {
                private bool _isDisposed;
    
                private IDisposable _disposable0;
                private IDisposable _disposable1;
                private readonly IDisposable[] _disposable;
    
                public Scope(int count)
                {
                    if (count > 2)
                    {
                        _disposable = new IDisposable[count - 2];
                    }
                }
    
                public void SetDisposable(int index, IDisposable disposable)
                {
                    switch (index)
                    {
                        case 0:
                            _disposable0 = disposable;
                            break;
                        case 1:
                            _disposable1 = disposable;
                            break;
                        default:
                            _disposable[index - 2] = disposable;
                            break;
                    }
                }
    
                public void Dispose()
                {
                    if (!_isDisposed)
                    {
                        _disposable0?.Dispose();
                        _disposable1?.Dispose();
    
                        if (_disposable != null)
                        {
                            var count = _disposable.Length;
                            for (var index = 0; index != count; ++index)
                            {
                                if (_disposable[index] != null)
                                {
                                    _disposable[index].Dispose();
                                }
                            }
                        }
    
                        _isDisposed = true;
                    }
                }
            }
        }

    上面介绍了ILogger的原理,

    当注入ILogger<>时,依赖注入会解析到Logger<>类,再调用ILoggerFactory接口对应的LoggerFactory来创建对应的日志提供器

  • 相关阅读:
    iOS应用开发最佳实践
    Pywinauto 基于Win32 程序的自动化功能测试工具
    通信系统概论---电路交换与分组交换
    手动设置3G的wifi迷你无线路由
    作为一个软件测试工作者的思考
    中国人咋对“拼爹”现象如此诟病?
    HLS协议实现
    div:给div加滚动栏 div的滚动栏设置
    关于PCA算法的一点学习总结
    搜索引擎技术之概要预览
  • 原文地址:https://www.cnblogs.com/lanpingwang/p/12541110.html
Copyright © 2020-2023  润新知