返回顶部
首页 > 资讯 > 后端开发 > ASP.NET >.net core日志系统相关总结
  • 884
分享到

.net core日志系统相关总结

2024-04-02 19:04:59 884人浏览 安东尼
摘要

目录前言正文前言 本节开始整理日志相关的东西。先整理一下日志的基本原理。 正文 首先介绍一下包: 1.Microsoft.Extengsion.Logging.Abstratio

前言

本节开始整理日志相关的东西。先整理一下日志的基本原理。

正文

首先介绍一下包:

1.Microsoft.Extengsion.Logging.Abstrations

这个是接口包。

2.Microsoft.Extengsion.Logging

这个是实现包

3.Microsoft.Extengsion.Logging.Console

这个是扩展包

代码如下:


static void Main(string[] args)
{
	IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
	configurationBuilder.AddJSONFile("appsettings.json",optional:false,reloadOnChange:true);
	var config = configurationBuilder.Build();

	IServiceCollection serviceCollection = new ServiceCollection();
	serviceCollection.AddSingleton<IConfiguration>(p=>config);

	serviceCollection.AddLogging(builder =>
	{
		builder.AddConfiguration(config.GetSection("Logging"));
		builder.AddConsole();
	});

	IServiceProvider service = serviceCollection.BuildServiceProvider();

	ILoggerFactory loggerFactory = service.GetService<ILoggerFactory>();

	var loggerObj = loggerFactory.CreateLogger("Default");

	loggerObj.LogInfORMation(2021, "Default,now that is 2021");

	var loggerObj2 = loggerFactory.CreateLogger("loggerObj");

	loggerObj2.LogDebug(2021, "loggerObj,now that is 2021");

	Console.ReadKey();
}

配置文件:


{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    },
    "Console": {
      "LogLevel": {
        "Default": "Information",
        "Program": "Trace",
        "loggerObj": "Debug"
      }
    }
  }
}

结果:

首先是配置级别的问题,查看loglevel 文件:


public enum LogLevel
{
/// <summary>Logs that contain the most detailed messages. These messages may contain sensitive application data.
/// These messages are disabled by default and should never be enabled in a production environment.</summary>
Trace,
/// <summary>Logs that are used for interactive investigation during development.  These logs should primarily contain
/// information useful for debugging and have no long-term value.</summary>
Debug,
/// <summary>Logs that track the general flow of the application. These logs should have long-term value.</summary>
Information,
/// <summary>Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the
/// application execution to stop.</summary>
Warning,
/// <summary>Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a
/// failure in the current activity, not an application-wide failure.</summary>
Error,
/// <summary>Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires
/// immediate attention.</summary>
Critical,
/// <summary>Not used for writing log messages. Specifies that a logging cateGory should not write any messages.</summary>
None,
}

从上之下,依次提高log级别。

比如说设置了log 级别是Error,那么Debug、Information、Warning 都不会被答应出来。

那么就来分析一下代码吧。

AddLogging:


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;
}

这里面给注册了ILoggerFactory和ILogger。然后设置了一个打印log的级别配置,LogLevel.Information,这个就是如果我们没有配置文件默认就是Information这种级别了。

configure(new LoggingBuilder(services)) 给我们的委托提供了一个LoggingBuilder的实例化对象。这个对象就是用来专门做扩展的,是解耦的一种方式。


internal class LoggingBuilder : ILoggingBuilder
{
	public LoggingBuilder(IServiceCollection services)
	{
		Services = services;
	}

	public IServiceCollection Services { get; }
}

这个LoggingBuilder 类基本什么功能都没有,但是因为有了这样一个类,就可以作为扩展的标志了。

比如说上文的:


builder.AddConfiguration(config.GetSection("Logging"));
builder.AddConsole();

看下AddConfiguration:


public static ILoggingBuilder AddConfiguration(this ILoggingBuilder builder, IConfiguration configuration)
{
	builder.AddConfiguration();

	builder.Services.AddSingleton<IConfigureOptions<LoggerFilterOptions>>(new LoggerFilterConfigureOptions(configuration));
	builder.Services.AddSingleton<IOptionsChangeTokenSource<LoggerFilterOptions>>(new ConfigurationChangeTokenSource<LoggerFilterOptions>(configuration));

	builder.Services.AddSingleton(new LogginGConfiguration(configuration));

	return builder;
}

这里面给我们注入了配置文件的配置:builder.Services.AddSingleton<IConfigureOptions>(new LoggerFilterConfigureOptions(configuration))

同时给我们注册监听令牌:builder.Services.AddSingleton<IOptionsChangeTokenSource>(new ConfigurationChangeTokenSource(configuration));

这里给我们注册配置保存在LoggingConfiguration中:builder.Services.AddSingleton(new LoggingConfiguration(configuration));

因为LoggingConfiguration 保存了,故而我们随时可以获取到LoggingConfiguration 的配置。

看下AddConsole:


/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
public static ILoggingBuilder AddConsole(this ILoggingBuilder builder)
{
	builder.AddConfiguration();

	builder.AddConsoleFormatter<JsonConsoleFormatter, JsonConsoleFormatterOptions>();
	builder.AddConsoleFormatter<SystemdConsoleFormatter, ConsoleFormatterOptions>();
	builder.AddConsoleFormatter<SimpleConsoleFormatter, SimpleConsoleFormatterOptions>();

	builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, ConsoleLoggerProvider>());
	LoggerProviderOptions.ReGISterProviderOptions<ConsoleLoggerOptions, ConsoleLoggerProvider>(builder.Services);

	return builder;
}

builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, ConsoleLoggerProvider>()) 里面给我们ILoggerProvider 增加了一个ConsoleLoggerProvider,故而我们多了一个打印的功能。

LoggerProviderOptions.RegisterProviderOptions<ConsoleLoggerOptions, ConsoleLoggerProvider>(builder.Services) 给我们加上了ConsoleLoggerOptions 绑定为ConsoleLoggerProvider的配置。

RegisterProviderOptions 如下:


public static void RegisterProviderOptions<TOptions, TProvider>(IServiceCollection services) where TOptions : class
{
	services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<TOptions>, LoggerProviderConfigureOptions<TOptions, TProvider>>());
	services.TryAddEnumerable(ServiceDescriptor.Singleton<IOptionsChangeTokenSource<TOptions>, LoggerProviderOptionsChangeTokenSource<TOptions, TProvider>>());
}

接下来就是调用服务:


var loggerObj = loggerFactory.CreateLogger("Default");

loggerObj.LogInformation(2021, "Default,now that is 2021");

看下LoggerFactory的CreateLogger:


public ILogger CreateLogger(string categoryName)
{
	if (CheckDisposed())
	{
		throw new ObjectDisposedException(nameof(LoggerFactory));
	}

	lock (_sync)
	{
		if (!_loggers.TryGetValue(categoryName, out Logger logger))
		{
			logger = new Logger
			{
				Loggers = CreateLoggers(categoryName),
			};

			(logger.MessageLoggers, logger.ScopeLoggers) = ApplyFilters(logger.Loggers);

			_loggers[categoryName] = logger;
		}

		return logger;
	}
}

里面做了缓存,如果categoryName有缓存的话直接使用缓存,如果没有那么调用CreateLoggers创建。

查看CreateLoggers:


private LoggerInformation[] CreateLoggers(string categoryName)
{
	var loggers = new LoggerInformation[_providerRegistrations.Count];
	for (int i = 0; i < _providerRegistrations.Count; i++)
	{
		loggers[i] = new LoggerInformation(_providerRegistrations[i].Provider, categoryName);
	}
	return loggers;
}

这里面就用我们前面注册过的全部logger的provider,封装进LoggerInformation。

查看LoggerInformation:


internal readonly struct LoggerInformation
{
	public LoggerInformation(ILoggerProvider provider, string category) : this()
	{
		ProviderType = provider.GetType();
		Logger = provider.CreateLogger(category);
		Category = category;
		ExternalScope = provider is ISupportExternalScope;
	}

	public ILogger Logger { get; }

	public string Category { get; }

	public Type ProviderType { get; }

	public bool ExternalScope { get; }
}

里面调用了我们,每个provider的CreateLogger。

那么这个时候我们就找一个provider 看下CreateLogger到底做了什么,这里就找一下ConsoleLoggerProvider,因为我们添加了这个。


[ProviderAlias("Console")]
 public class ConsoleLoggerProvider : ILoggerProvider, ISupportExternalScope
{
        private readonly IOptionsMonitor<ConsoleLoggerOptions> _options;
        public ILogger CreateLogger(string name)
        {
            if (_options.CurrentValue.FormatterName == null || !_formatters.TryGetValue(_options.CurrentValue.FormatterName, out ConsoleFormatter logFormatter))
            {
#pragma warning disable CS0618
                logFormatter = _options.CurrentValue.Format switch
                {
                    ConsoleLoggerFormat.Systemd => _formatters[ConsoleFormatterNames.Systemd],
                    _ => _formatters[ConsoleFormatterNames.Simple],
                };
                if (_options.CurrentValue.FormatterName == null)
                {
                    UpdateFormatterOptions(logFormatter, _options.CurrentValue);
                }
#pragma warning disable CS0618
            }

            return _loggers.GetOrAdd(name, loggerName => new ConsoleLogger(name, _messageQueue)
            {
                Options = _options.CurrentValue,
                ScopeProvider = _scopeProvider,
                Formatter = logFormatter,
            });
        }
}

看到这个IOptionsMonitor,就知道console 配置是支持热更新的,里面创建了ConsoleLogger,这个ConsoleLogger就是用来打log正在的调用类。

值得注意的是_messageQueue这个,看了打印log还是有一个队列的,按照先进先出原则。

那么最后来看一下loggerObj.LogInformation(2021, "Default,now that is 2021");:


第一层
public static void LogInformation(this ILogger logger, EventId eventId, string message, params object[] args)
{
	logger.Log(LogLevel.Information, eventId, message, args);
}
第二层
public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, string message, params object[] args)
{
         logger.Log(logLevel, eventId, null, message, args);
}
第三层
public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, Exception exception, string message, params object[] args)
{
	if (logger == null)
	{
		throw new ArgumentNullException(nameof(logger));
	}

	logger.Log(logLevel, eventId, new FormattedLogValues(message, args), exception, _messageFormatter);
}

那么这个logger.Log 是调用具体某个logger,像consoleLogger 吗? 不是,我们看LoggerFactory的CreateLogger时候封装了:


logger = new Logger
{
       Loggers = CreateLoggers(categoryName),
};

那么看下Logger的Log到底干了什么。


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)
	{
		MessageLogger[] loggers = MessageLoggers;
		if (loggers == null)
		{
			return;
		}

		List<Exception> exceptions = null;
		for (int i = 0; i < loggers.Length; i++)
		{
			ref readonly MessageLogger 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);
			}
		}
	}
}

里面循环判断是否当前级别能够输出:!loggerInfo.IsEnabled(logLevel)

然后调用对应的具体ILog实现的Log,这里贴一下ConsoleLogger 的实现:


[ThreadStatic]
private static StringWriter t_stringWriter;

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
	if (!IsEnabled(logLevel))
	{
		return;
	}
	if (formatter == null)
	{
		throw new ArgumentNullException(nameof(formatter));
	}
	t_stringWriter ??= new StringWriter();
	LogEntry<TState> logEntry = new LogEntry<TState>(logLevel, _name, eventId, state, exception, formatter);
	Formatter.Write(in logEntry, ScopeProvider, t_stringWriter);

	var sb = t_stringWriter.GetStringBuilder();
	if (sb.Length == 0)
	{
		return;
	}
	string computedAnsiString = sb.ToString();
	sb.Clear();
	if (sb.Capacity > 1024)
	{
		sb.Capacity = 1024;
	}
	_queueProcessor.EnqueueMessage(new LogMessageEntry(computedAnsiString, logAsError: logLevel >= Options.LogToStandardErrorThreshold));
}

把这个队列的也贴一下,比较经典吧。


internal class ConsoleLoggerProcessor : IDisposable
{
	private const int _maxQueuedMessages = 1024;

	private readonly BlockingCollection<LogMessageEntry> _messageQueue = new BlockingCollection<LogMessageEntry>(_maxQueuedMessages);
	private readonly Thread _outputThread;

	public IConsole Console;
	public IConsole ErrorConsole;

	public ConsoleLoggerProcessor()
	{
		// Start Console message queue processor
		_outputThread = new Thread(ProcessLogQueue)
		{
			IsBackground = true,
			Name = "Console logger queue processing thread"
		};
		_outputThread.Start();
	}

	public virtual void EnqueueMessage(LogMessageEntry message)
	{
		if (!_messageQueue.IsAddingCompleted)
		{
			try
			{
				_messageQueue.Add(message);
				return;
			}
			catch (InvalidOperationException) { }
		}

		// Adding is completed so just log the message
		try
		{
			WriteMessage(message);
		}
		catch (Exception) { }
	}

	// for testing
	internal virtual void WriteMessage(LogMessageEntry entry)
	{
		IConsole console = entry.LogAsError ? ErrorConsole : Console;
		console.Write(entry.Message);
	}

	private void ProcessLogQueue()
	{
		try
		{
			foreach (LogMessageEntry message in _messageQueue.GetConsumingEnumerable())
			{
				WriteMessage(message);
			}
		}
		catch
		{
			try
			{
				_messageQueue.CompleteAdding();
			}
			catch { }
		}
	}

	public void Dispose()
	{
		_messageQueue.CompleteAdding();

		try
		{
			_outputThread.Join(1500); // with timeout in-case Console is locked by user input
		}
		catch (ThreadStateException) { }
	}
}

以上就是.net core日志系统相关总结的详细内容,更多关于.Net Core日志的资料请关注编程网其它相关文章!

--结束END--

本文标题: .net core日志系统相关总结

本文链接: https://lsjlt.com/news/128397.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • .net core日志系统相关总结
    目录前言正文前言 本节开始整理日志相关的东西。先整理一下日志的基本原理。 正文 首先介绍一下包: 1.Microsoft.Extengsion.Logging.Abstratio...
    99+
    2024-04-02
  • .net core日志结构化
    目录前言正文前言 什么是结构化呢? 结构化,就是将原本没有规律的东西进行有规律话。 就比如我们学习数据结构,需要学习排序然后又要学习查询,说白了这就是一套,没有排序,谈如何查询是没...
    99+
    2024-04-02
  • MySQL 日志相关知识总结
    数据库中用于存储数据的文件称为data file,日志文件称为log file。此外,如果每次读写都是直接访问磁盘,性能很差,所以数据库是有缓存的,数据缓存是data buffer,日志缓存log buffer。 s...
    99+
    2022-05-21
    MySQL 日志
  • MySQL 慢日志相关知识总结
    目录  1.慢日志简介  2.慢日志实战  1.慢日志简介 慢日志全称为慢查询日志(Slow Query Log),主要用来记录在 MySQL 中执行时间超过指定时间的 SQL 语句。通...
    99+
    2022-05-22
    MySQL 慢日志
  • MySQL 撤销日志与重做日志(Undo Log与Redo Log)相关总结
    Undo Log 数据库事务开始之前,会将要修改的记录存放到 Undo 日志里,当事务回滚时或者数据库崩溃时,可以利用 Undo 日志,撤销未提交事务对数据库产生的影响。 Undo Log产生和销毁 Undo L...
    99+
    2022-05-20
    MySQL 撤销日志Undo Log MySQL 重做日志Redo Log
  • ASP.NET Core的日志系统介绍
    .net core是内置了日志系统的,本文这里简单的介绍一下它的基本用法。如下是一个简单的示例:  var service = new ServiceCollection(...
    99+
    2024-04-02
  • Linux查看系统日志的常用命令总结
    本篇内容介绍了“Linux查看系统日志的常用命令总结”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!last-a 把从何处登入系统的主机名称或...
    99+
    2023-06-13
  • ASP.NET Core的日志系统实例分析
    本文小编为大家详细介绍“ASP.NET Core的日志系统实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“ASP.NET Core的日志系统实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习...
    99+
    2023-07-02
  • MySQL 外键约束和表关系相关总结
    目录外键(Foreign Key)如何确定表关系如何建立表关系一对多关系 - 员工表和部门表多对多一对一表关系总结外键(Foreign Key) 按照上述所说,一张表存储员工信息会极大的浪费资源,重复数据太多,这个...
    99+
    2022-05-18
    MySQL 外键约束 MySQL 表关系
  • Java日期相关API的基本操作总结
    目录前言JDK8之前Date对象的使用格式化日期SimpleDateFormat类的使用JDK8之后LocalDate 、LocalTime 、LocalDateTime的使用格式化...
    99+
    2022-11-21
    Java日期API操作 Java日期API
  • Linux系统查看日志的命令汇总
    本篇内容介绍了“Linux系统查看日志的命令汇总”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!日志文件说明/var/log/message ...
    99+
    2023-06-05
  • 基于MongoDB的日志分析与监控系统搭建经验总结
    一、需求分析与系统设计随着互联网和移动设备的普及,各类网络应用和系统的日志数量急剧增长。对这些海量的日志进行分析和监控可以帮助企业实时了解系统运行情况,发现潜在的问题并进行及时修复,提高系统的稳定性和可靠性。为了满足这一需求,我们团队基于M...
    99+
    2023-11-04
    MongoDB 日志分析 监控系统
  • 美国VPS主机Linux系统用户日志相关命令有哪些
    美国VPS主机Linux系统用户日志相关命令有:1、users命令,能够用于显示当前登录用户名称;2、who命令,主要用于输出当前系统所有用户信息;3、w命令,主要用于属于所有用户以及其运行的进程信息;4、last命令,主要用于查询美国VP...
    99+
    2024-04-02
  • 操作系统日志的奥秘:揭开系统运行的真相
    日志类型 操作系统日志有多种类型,每种类型记录不同类型的事件: 系统日志:记录操作系统本身的事件,例如启动、关闭和更新。 安全性日志:记录登录、注销、访问控制和安全事件。 应用程序日志:由应用程序生成的日志,记录应用程序的活动和错误。 ...
    99+
    2024-04-02
  • 并发和日志在Java中有什么相互关系?
    在Java编程中,我们经常需要处理并发和日志。这两个概念在Java中有着非常密切的关系。在本文中,我们将探讨并发和日志在Java中的相互关系,并且演示一些相关的代码。 并发和日志是两个非常重要的概念。并发指的是多个线程同时执行同一个任务,而...
    99+
    2023-09-13
    数据类型 并发 日志
  • LInux 从系统日志中找出系统关闭的原因
    有时候服务器运行的好好的,突然就无法连接无法执行任何操作了,强制重启后需要通过系统日志排查系统故障的原因 只有root特权程序才能正常关闭系统。因此,当系统以正常方式关闭时,它要么是具有root特权的用户,要么是acpi脚本。在这两种情况下...
    99+
    2023-08-19
    linux 服务器 运维
  • Windows系统离线安装.NET Framework 3.5(自己做个总结)
    服务器不允许联网的情况下,安装SQLServer2012会出现需要Framework3.5的环境,但是某些系统上没有安装,又不能联网,需要我们自己去进行安装。自己做个总结 从网上找到sxs目录,是Framework的目录,也可以从对应版本的...
    99+
    2023-09-08
    数据库 java 服务器
  • 操作系统日志宝典:深入探究系统事件的真相
    日志记录机制 操作系统日志事件是通过称为系统日志守护进程的组件捕获的,该组件负责收集和存储来自各种来源的消息。这些来源包括应用程序、内核和系统组件。日志记录级别(例如信息、警告、错误)决定了消息的严重性。 日志文件类型 常见的操作系统日志...
    99+
    2024-04-02
  • 基于MongoDB的实时日志分析与报警系统搭建经验总结
    在当今信息时代,日志分析与报警系统对于企业的数据管理和安全至关重要。随着云计算和大数据的兴起,传统的关系型数据库已经无法满足日益增长的数据量和实时性需求。在这样的背景下,NoSQL数据库成为了一种备受关注的选择。本文将分享基于MongoDB...
    99+
    2023-11-02
    MongoDB 实时日志分析 报警系统搭建
  • PHP日期函数实现时间相关功能的方法总结
    PHP日期函数是PHP语言中的一个重要特性,可以实现各种时间相关的功能。在本篇文章中,将总结一些常用的PHP日期函数的用法,帮助读者更好地理解和使用它们。一、获取当前时间PHP日期函数中最常用的一个函数就是date()函数,可以获取当前的日...
    99+
    2023-11-20
    格式化日期 比如获取当前日期 计算日期之间的差距等。 获取指定日期的时间戳
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作