您现在的位置是:网站首页> 编程资料编程资料
ASP.Net Core中的日志与分布式链路追踪_实用技巧_
                     2023-05-24
                414人已围观
                
                2023-05-24
                414人已围观
            
简介 ASP.Net Core中的日志与分布式链路追踪_实用技巧_
程序记录的日志一般有两种作用,故障排查、显式程序运行状态,当程序发生故障时,我们可以通过日志定位问题,日志可以给我们留下排查故障的依据。很多时候,往往会认为日志记录非常简单,例如很多程序只是 try-catch{},直接输出到 .txt,但是这些日志往往无法起到帮助定位问题的作用,甚至日志充斥了大量垃圾内容;日志内容全靠人眼一行行扫描,或者 Ctrl+F 搜索,无法高效率审查日志;日志单纯输出到文本文件中,没有很好地管理日志。
接下来,我们将一步步学习日志的编写技巧,以及 OpenTracing API 、Jaeger 分布式链路跟踪的相关知识。
.NET Core 中的日志
控制台输出
最简单的日志,就是控制台输出,利用 Console.WriteLine() 函数直接输出信息。
下面时一个简单的信息输出,当程序调用 SayHello 函数时,SayHello 会打印信息。
 public class Hello { public void SayHello(string content) { var str = $"Hello,{content}"; Console.WriteLine(str); } } class Program { static void Main(string[] args) { Hello hello = new Hello(); hello.SayHello("any one"); Console.Read(); } }非侵入式日志
通过控制台,我们可以看到,为了记录日志,我们必须在函数内编写输入日志的代码,优缺点这些就不多说了,我们可以通过 AOP 框架,实现切面编程,同一记录日志。
这里可以使用笔者开源的 CZGL.AOP 框架,Nuget 中可以搜索到。

编写统一的切入代码,这些代码将在函数被调用时执行。
Before 会在被代理的方法执行前或被代理的属性调用时生效,你可以通过 AspectContext 上下文,获取、修改传递的参数。
After 在方法执行后或属性调用时生效,你可以通过上下文获取、修改返回值。
 public class LogAttribute : ActionAttribute { public override void Before(AspectContext context) { Console.WriteLine($"{context.MethodInfo.Name} 函数被执行前"); } public override object After(AspectContext context) { Console.WriteLine($"{context.MethodInfo.Name} 函数被执行后"); return null; } }改造 Hello 类,代码如下:
 [Interceptor] public class Hello { [Log] public virtual void SayHello(string content) { var str = $"Hello,{content}"; Console.WriteLine(str); } }然后创建代理类型:
 static void Main(string[] args) { Hello hello = AopInterceptor.CreateProxyOfClass(); hello.SayHello("any one"); Console.Read(); } 启动程序,会输出:
SayHello 函数被执行前 Hello,any one SayHello 函数被执行后
你完全不需要担心 AOP 框架会给你的程序带来性能问题,因为 CZGL.AOP 框架采用 EMIT 编写,并且自带缓存,当一个类型被代理过,之后无需重复生成。
CZGL.AOP 可以通过 .NET Core 自带的依赖注入框架和 Autofac 结合使用,自动代理 CI 容器中的服务。这样不需要 AopInterceptor.CreateProxyOfClass 手动调用代理接口。
CZGL.AOP 代码是开源的,可以参考笔者另一篇博文:
https://www.jb51.net/article/238462.htm
Microsoft.Extensions.Logging
有些公司无技术管理规范,不同的开发人员使用不同的日志框架,一个产品中可能有 .txt、NLog、Serilog等,并且没有同一的封装。
.NET Core 中的日志组件有很多,但是流行的日志框架基本都会实现 Microsoft.Extensions.Logging.Abstractions,因此我们可以学习Microsoft.Extensions.Logging 。Microsoft.Extensions.Logging.Abstractions 是官方对日志组件的抽象,如果一个日志组件并不支持 Microsoft.Extensions.Logging.Abstractions 那么这个组件很容易跟项目糅合的,后续难以模块化以及降低耦合程度。
Microsoft.Extensions.Logging 软件包中包含 Logging API ,这些 Logging API 不能独立运行。它与一个或多个日志记录提供程序一起使用,这些日志记录提供程序将日志存储或显示到特定输出,例如 Console, Debug, TraceListeners。
下图是 .NET Core 中 Loggin API 的层次结构:

说实话,Microsoft.Extensions.Logging 刚开始是学着很懵,配置感觉很复杂。因此,有一张清晰的结构图很重要,可以帮助大家理解里面的 Logging API。

ILoggerFactory
.NET Core 中很多标准接口都实践了工厂模式的思想,ILoggerFactory 正是工厂模式的接口,而 LoggerFactory 是工厂模式的实现。
其定义如下:
public interface ILoggerFactory : IDisposable { ILogger CreateLogger(string categoryName); void AddProvider(ILoggerProvider provider); }ILoggerFactory 工厂接口的作用是创建一个 ILogger 类型的实例,即 CreateLogger 接口。
ILoggerProvider
通过实现ILoggerProvider接口可以创建自己的日志记录提供程序,表示可以创建 ILogger 实例的类型。
其定义如下:
public interface ILoggerProvider : IDisposable { ILogger CreateLogger(string categoryName); }ILogger
ILogger 接口提供了将日志记录到基础存储的方法,其定义如下:
public interface ILogger { void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter); bool IsEnabled(LogLevel logLevel); IDisposable BeginScope(TState state); }   Logging Providers
logging providers 称为日志记录程序。
Logging Providers 将日志显示或存储到特定介质,例如 console, debugging event, event log, trace listener 等。
Microsoft.Extensions.Logging 提供了以下类型的 logging providers,我们可以通过 Nuget 获取。
- Microsoft.Extensions.Logging.Console
- Microsoft.Extensions.Logging.AzureAppServices
- Microsoft.Extensions.Logging.Debug
- Microsoft.Extensions.Logging.EventLog
- Microsoft.Extensions.Logging.EventSource
- Microsoft.Extensions.Logging.TraceSource
而 Serilog 则有 File、Console、Elasticsearch、Debug、MSSqlServer、Email等。
这些日志提供程序有很多,我们不必细究;如果一个日志组件,不提供兼容 Microsoft.Extensions.Logging 的实现,那么根本不应该引入他。
实际上,很多程序是直接 File.Write("Log.txt") ,这种产品质量能好到哪里去呢?
怎么使用
前面,介绍了 Microsoft.Extensions.Logging 的组成,这里将学习如何使用 Logging Provider 输入日志。
起码提到,它只是提供了一个 Logging API,因此为了输出日志,我们必须选择合适的 Logging Provider 程序,这里我们选择
Microsoft.Extensions.Logging.Console,请在 Nuget 中引用这个包。
下图是 Logging Provider 和 ConsoleLogger 结合使用的结构图:

从常规方法来弄,笔者发现,没法配置呀。。。
ConsoleLoggerProvider consoleLoggerProvider = new ConsoleLoggerProvider( new OptionsMonitor( new OptionsFactory ( new IEnumerable (... ... ...)))); 
所以只能使用以下代码快速创建工厂:
 using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddSimpleConsole(options => { options.IncludeScopes = true; options.SingleLine = true; options.TimestampFormat = "hh:mm:ss "; }));或者:
ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
当然工厂中可以添加其它日志提供程序,示例:
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddSimpleConsole(...) .AddFile(...) .Add()... );
然后获取 ILogger 实例:
ILogger logger = loggerFactory.CreateLogger(); 
记录日志:
 logger.LogInformation("记录信息");日志等级
Logging API 中,规定了 7 种日志等级,其定义如下:
public enum LogLevel { Debug = 1, Verbose = 2, Information = 3, Warning = 4, Error = 5, Critical = 6, None = int.MaxValue }我们可以通过 ILogger 中的函数,输出以下几种等级的日志:
 logger.LogInformation("Logging information."); logger.LogCritical("Logging critical information."); logger.LogDebug("Logging debug information."); logger.LogError("Logging error information."); logger.LogTrace("Logging trace"); logger.LogWarning("Logging warning.");关于 Microsoft.Extensions.Logging 这里就不再赘述。
Trace、Debug
Debug 、Trace 这两个类的命名空间为 System.Diagnostics,Debug 、Trace 提供一组有助于调试代码的方法和属性。
读者可以参考笔者的另一篇文章:
//www.jb51.net/article/242562.htm
输出到控制台:
Trace.Listeners.Add(new TextWriterTraceListe
                
                
                
            点击排行
本栏推荐
 
 
                                
                                                         
                                
                                                         
                                
                                                         
    